pt-online-schema-changeのバグ


mysql5.6とmysql 5.7 online DDLに対して大幅な機能強化を行ったが、依然としてメインライブラリがDDLを実行し、在庫から大幅に遅延している場合があるため、現在の生産環境はpt-online-schema-changeツールを通じてonline DDLを実現している.しかしpt-online-schema-changeの使用には制限はありませんか?
まず、pt-online-schema-changeの動作原理に関する公式ドキュメントの説明を見てみましょう.
    pt-online-schema-change works by creating an empty copy of the table to alter, modifying it as desired, and then
copying rows from the original table into the new table. When the copy is complete, it moves away the original table
and replaces it with the new one. By default, it also drops the original table.
    The data copy process is performed in small chunks of data, which are varied to attempt to make them execute in
a specific amount of time (see --chunk-time). This process is very similar to how other tools, such as pt-tablechecksum,
work. Any modifications to data in the original tables during the copy will be reflected in the new table,
because the tool creates triggers on the original table to update the corresponding rows in the new table. The use of
triggers means that the tool will not work if any triggers are already defined on the table.
    When the tool finishes copying data into the new table, it uses an atomic RENAME TABLE operation

   
次にpt-online-schema-changeがどのように動作しているかを実験的に見て、mysqlのgeneral logを開くのを覚えています.generalログを表示してpt-online-schema-changeの動作メカニズムを検証します.
    shell>pt-online-schema-change -u linzj -h 192.168.110.131 -p linzj --alter='add column vid3 int' --execute D=sbtest,t=sbtest
1 alter操作を実行するテーブルと同じ空のテーブル構造を作成します.
                       11 Query     CREATE TABLE `sbtest`.`_sbtest_new` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `k` int(10) unsigned NOT NULL DEFAULT '0',
  `c` char(120) NOT NULL DEFAULT '',
  `pad` char(60) NOT NULL DEFAULT '',
  `vid` int(11) DEFAULT NULL,
  `vid2` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `k` (`k`)
) ENGINE=InnoDB AUTO_INCREMENT=4294967295 DEFAULT CHARSET=utf8

2、表構造の修正を実行する
170407 15:45:46    11 Query     ALTER TABLE `sbtest`.`_sbtest_new` add column vid3 int

3、元のテーブルにトリガを作成し、テーブルにトリガというツールが定義されている場合は動作しません.
                   11 Query     CREATE TRIGGER `pt_osc_sbtest_sbtest_del` AFTER DELETE ON `sbtest`.`sbtest` FOR EACH ROW DELETE IGNORE FROM `sbtest
`.`_sbtest_new` WHERE `sbtest`.`_sbtest_new`.`id` <=> OLD.`id`
                   11 Query     CREATE TRIGGER `pt_osc_sbtest_sbtest_upd` AFTER UPDATE ON `sbtest`.`sbtest` FOR EACH ROW REPLACE INTO `sbtest`.`_sb
test_new` (`id`, `k`, `c`, `pad`, `vid`, `vid2`) VALUES (NEW.`id`, NEW.`k`, NEW.`c`, NEW.`pad`, NEW.`vid`, NEW.`vid2`)
                   11 Query     CREATE TRIGGER `pt_osc_sbtest_sbtest_ins` AFTER INSERT ON `sbtest`.`sbtest` FOR EACH ROW REPLACE INTO `sbtest`.`_sb
test_new` (`id`, `k`, `c`, `pad`, `vid`, `vid2`) VALUES (NEW.`id`, NEW.`k`, NEW.`c`, NEW.`pad`, NEW.`vid`, NEW.`vid2`)

4、主キーor一意インデックスで並べ替え、いくつかのchunkに分けてデータcopyを行う
                   11 Query     EXPLAIN SELECT * FROM `sbtest`.`sbtest` WHERE 1=1
                   11 Query     SELECT /*!40001 SQL_NO_CACHE */ `id` FROM `sbtest`.`sbtest` FORCE INDEX(`PRIMARY`) ORDER BY `id` LIMIT 1 /*first lo
wer boundary*/
                   11 Query     SELECT /*!40001 SQL_NO_CACHE */ `id` FROM `sbtest`.`sbtest` FORCE INDEX (`PRIMARY`) WHERE `id` IS NOT NULL ORDER BY
 `id` LIMIT 1 /*key_len*/
                   11 Query     EXPLAIN SELECT /*!40001 SQL_NO_CACHE */ * FROM `sbtest`.`sbtest` FORCE INDEX (`PRIMARY`) WHERE `id` >= '1' /*key_le
n*/
                   11 Query     EXPLAIN SELECT /*!40001 SQL_NO_CACHE */ `id` FROM `sbtest`.`sbtest` FORCE INDEX(`PRIMARY`) WHERE ((`id` >= '1')) OR
DER BY `id` LIMIT 999, 2 /*next chunk boundary*/
                   11 Query     SELECT /*!40001 SQL_NO_CACHE */ `id` FROM `sbtest`.`sbtest` FORCE INDEX(`PRIMARY`) WHERE ((`id` >= '1')) ORDER BY `
id` LIMIT 999, 2 /*next chunk boundary*/
                   11 Query     SHOW WARNINGS
                   11 Query     SHOW GLOBAL STATUS LIKE 'Threads_running'
                   11 Query     EXPLAIN SELECT /*!40001 SQL_NO_CACHE */ `id` FROM `sbtest`.`sbtest` FORCE INDEX(`PRIMARY`) WHERE ((`id` >= '1001'))
 ORDER BY `id` LIMIT 19329, 2 /*next chunk boundary*/
                   11 Query     SELECT /*!40001 SQL_NO_CACHE */ `id` FROM `sbtest`.`sbtest` FORCE INDEX(`PRIMARY`) WHERE ((`id` >= '1001')) ORDER B
Y `id` LIMIT 19329, 2 /*next chunk boundary*/
                   11 Query     EXPLAIN SELECT `id`, `k`, `c`, `pad`, `vid`, `vid2` FROM `sbtest`.`sbtest` FORCE INDEX(`PRIMARY`) WHERE ((`id` >= '
1001')) AND ((`id` <= '20330')) LOCK IN SHARE MODE /*explain pt-online-schema-change 17219 copy nibble*/
                   11 Query     INSERT LOW_PRIORITY IGNORE INTO `sbtest`.`_sbtest_new` (`id`, `k`, `c`, `pad`, `vid`, `vid2`) SELECT `id`, `k`, `c`
, `pad`, `vid`, `vid2` FROM `sbtest`.`sbtest` FORCE INDEX(`PRIMARY`) WHERE ((`id` >= '1001')) AND ((`id` <= '20330')) LOCK IN SHARE MODE /*pt-onlin
e-schema-change 17219 copy nibble*/

5、renameテーブル、デフォルトで古いテーブルを削除する
                   11 Query     RENAME TABLE `sbtest`.`sbtest` TO `sbtest`.`_sbtest_old`, `sbtest`.`_sbtest_new` TO `sbtest`.`sbtest`
                   11 Query     DROP TABLE IF EXISTS `sbtest`.`_sbtest_old`

では、pt-online-schema-changeツールオンラインonline DDLのテーブルを使用している間に、テーブルのプライマリ・キーor一意インデックス・フィールドをDMLすると、異常はありませんか?
実験シーンは次のとおりです.
最初のウィンドウ:
shell>pt-online-schema-change -u linzj -h 192.168.110.131 -p linzj --alter='add column vid3 int' --execute D=sbtest,t=sbtest
Found 2 slaves:
  mysql2
  ansible
Will check slave lag on:
  mysql2
  ansible
Operation, tries, wait:
  copy_rows, 10, 0.25
  create_triggers, 10, 1
  drop_triggers, 10, 1
  swap_tables, 10, 1
  update_foreign_keys, 10, 1
Altering `sbtest`.`sbtest`...
Creating new table...
Created new table sbtest._sbtest_new OK.
Waiting forever for new table `sbtest`.`_sbtest_new` to replicate to mysql2...
Altering new table...
Altered `sbtest`.`_sbtest_new` OK.
2017-04-07T14:52:50 Creating triggers...
2017-04-07T14:52:50 Created triggers OK.
2017-04-07T14:52:50 Copying approximately 986400 rows...
Copying `sbtest`.`sbtest`:  86% 00:04 remain
2017-04-07T14:53:27 Copied rows OK.
2017-04-07T14:53:27 Swapping tables...
2017-04-07T14:53:27 Swapped original and new tables OK.
2017-04-07T14:53:27 Dropping old table...
2017-04-07T14:53:27 Dropped old table `sbtest`.`_sbtest_old` OK.
2017-04-07T14:53:27 Dropping triggers...
2017-04-07T14:53:27 Dropped triggers OK.
Successfully altered `sbtest`.`sbtest`.

2番目のウィンドウ:
root@localhost:mysql3306.sock  15:44:  [sbtest]>select count(*) from sbtest;
+----------+
| count(*) |
+----------+
|  1000000 |
+----------+
1 row in set (0.17 sec)
root@localhost:mysql3306.sock  15:44:  [sbtest]>update sbtest set id=9999999 where id =110;            
Query OK, 1 row affected (1.33 sec)
Rows matched: 1  Changed: 1  Warnings: 0
root@localhost:mysql3306.sock  15:45:  [sbtest]>update sbtest set id=9999998 where id =111;
Query OK, 1 row affected (0.84 sec)
Rows matched: 1  Changed: 1  Warnings: 0
root@localhost:mysql3306.sock  15:46:  [sbtest]>update sbtest set id=9999997 where id =112;
Query OK, 1 row affected (0.75 sec)
Rows matched: 1  Changed: 1  Warnings: 0
root@localhost:mysql3306.sock  15:46:  [sbtest]>select count(*) from sbtest;
+----------+
| count(*) |
+----------+
|  1000003 |
+----------+
1 row in set (0.70 sec)
root@localhost:mysql3306.sock  15:46:  [sbtest]>select * from sbtest order by id desc limit 5;
+---------+---+---+----------------------------------------------------+------+------+------+
| id      | k | c | pad                                                | vid  | vid2 | vid3 |
+---------+---+---+----------------------------------------------------+------+------+------+
| 9999999 | 0 |   | qqqqqqqqqqwwwwwwwwwweeeeeeeeeerrrrrrrrrrtttttttttt | NULL | NULL | NULL |
| 9999998 | 0 |   | qqqqqqqqqqwwwwwwwwwweeeeeeeeeerrrrrrrrrrtttttttttt | NULL | NULL | NULL |
| 9999997 | 0 |   | qqqqqqqqqqwwwwwwwwwweeeeeeeeeerrrrrrrrrrtttttttttt | NULL | NULL | NULL |
| 1000000 | 0 |   | qqqqqqqqqqwwwwwwwwwweeeeeeeeeerrrrrrrrrrtttttttttt | NULL | NULL | NULL |
|  999999 | 0 |   | qqqqqqqqqqwwwwwwwwwweeeeeeeeeerrrrrrrrrrtttttttttt | NULL | NULL | NULL |
+---------+---+---+----------------------------------------------------+------+------+------+
5 rows in set (0.00 sec)
root@localhost:mysql3306.sock  15:46:  [sbtest]>select * from sbtest where id in (110,111,112);
+-----+---+---+----------------------------------------------------+------+------+------+
| id  | k | c | pad                                                | vid  | vid2 | vid3 |
+-----+---+---+----------------------------------------------------+------+------+------+
| 110 | 0 |   | qqqqqqqqqqwwwwwwwwwweeeeeeeeeerrrrrrrrrrtttttttttt | NULL | NULL | NULL |
| 111 | 0 |   | qqqqqqqqqqwwwwwwwwwweeeeeeeeeerrrrrrrrrrtttttttttt | NULL | NULL | NULL |
| 112 | 0 |   | qqqqqqqqqqwwwwwwwwwweeeeeeeeeerrrrrrrrrrtttttttttt | NULL | NULL | NULL |
+-----+---+---+----------------------------------------------------+------+------+------+
3 rows in set (0.02 sec)

同時にテーブルのプライマリ・キーorユニーク・インデックスを変更すると、新しいテーブルのデータが古いテーブルのデータよりも多くなる場合があります.これはpt-online-schema-changeツールのバグと言えるはずですが、なぜこのような状況になったのか、pt-online-schema-changeツールが元のテーブルで作成した3つのトリガの定義をよく観察するとわかりやすいです.
pt-online-schema-changeを使用する場合は、テーブルのプライマリ・キーor一意のインデックス・カラムのデータ更新を一時停止することをお勧めします.
pt_online_schema_changeの典型的な使い方:
1)pt-online-schema-change–alter「add column c 1 int」D=mydb,t=mytable-dry-runを実際に実行しない列を追加
2)ストレージエンジンをInnoDBに更新し、元のテーブルpt-online-schema-change–alter「ENGINE=InnoDB」–no-drop-old-table–print–statistics–execute D=mydb、t=mytable–executeを削除しない
3)レプリケーション環境で、ログフィルタリングとSlaveレプリケーションの遅延を無視して、テーブルフィールドpt-online-schema-change-no-check-replication-filters-recursion-method=none-alter"drop company_type,drop channel_code"h=192.168.10.14,P=3370,u=user 1,p=pass 1,D=db 1,t=table 1-print-statistics-execute
4)掛け布団表で参照された親表pt-online-schema-change–alter"add newcol int"h=192.168.10.14,P=3370,u=user 1,p=pass 1,D=db 1,t=table 1–alter-foreign-keys-method auto–print–statistics–execute
5)デュアルプライマリレプリケーション環境ではmysqlライブラリのレプリケーションを無視するように設定されており、レプリケーションの遅延をあまり気にせず、外部キーの影響がある場合があります.できるだけ元のテーブルデータを保持し、必要に応じて自分で削除したいと考えています.pt-online-schema-change –no-check-replication-filters –recursion-method=none –alter “drop newcol” h=192.168.10.14,P=3370,u=user1,p=pass1,D=db1,t=table1 –alter-foreign-keys-method auto –no-drop-old-table –print –statistics –execute