はじめに
Percona XtraDB Cluster(PXC) 8.0.25からOnline Scheme Upgradesの新方式としてNon-Blocking Operations(NBO)方式が追加されました。
本記事ではこのNBO方式について紹介したいと思います。
なお、NBO方式はTech Previewです。変更される可能性があり、テスト目的での使用が推奨されています。
現行のOnline Scheme Upgradesの方式
現在PXCでは、次の2つの方法のOnline Scheme Upgrades(OSU)をサポートしています。
- Total Order Isolation(TOI)
- Rolling Schema Upgrade(RSU)
それぞれの詳しい特徴は以前にも本ブログで取り上げているため、こちらの記事をご参照ください。
簡単にまとめると次のような特徴を持ちます。
- TOI
- DDLを実行した時に全ノードで整合性が保たれる
- DDLが完了するまでの間は全てのトランザクションが待機、またはロールバックされる
- DDLは全ノードに適用される
- RSU
- DDLを実行するノードはクラスターとの同期を解除する
- 全ノードに手動でDDLを実行する必要がある
- ノード間で不整合が発生する可能性がある
どちらの方式にもメリット・デメリットがありますが、特に時間がかかるDDLをTOIで実行する場合は、その間クラスタが更新を受け付けなくなってしまいPXCにおける懸念事項の1つでした。
Non-Blocking Operations
NBO方式は、TOI方式、RSU方式に続く、第3のOSUの方式です。
DDL操作の最終段階でテーブルまたはスキーマのメタデータロックを取得します。 より効率的なロック戦略を提供するため、TOIで長時間更新がブロックされる問題を回避することが可能です。
これまでは一貫性を重視する場合はTOI方式で、全ノードで全ての更新をブロックする必要がありましたが、NBOでは一貫性を保証しつつ、DDL操作を行っていないテーブルにはDDL中も更新ができるようになりました。
- NBO
- 操作中のテーブル以外への変更はブロックされない
- DDLは全ノードに適用される
- 一部のDDLのみサポート
サポートされているDDLは以下の3つです。
- ALTER TABLE
- CREATE INDEX
- DROP INDEX
デモ
NBO方式でDDLを実行してみます。デモの内容は過去の記事で行ったTOIのデモと同じことを行います。
また、今回は最新リリースのPXC8.0.26を使用します。
1. 大きなテストテーブルを作成する
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
node1> CREATE TABLE test.large_tbl (id int primary key auto_increment, col1 int, col2 varchar(30), col3 text); node1> INSERT INTO test.large_tbl (col1, col2, col3) SELECT CEIL(RAND() * 10000), SUBSTRING(MD5(RAND()), 1, 30), SUBSTRING(MD5(RAND()), 1, 50); node1> INSERT INTO test.large_tbl (col1, col2, col3) SELECT CEIL(RAND() * 10000), SUBSTRING(MD5(RAND()), 1, 30), SUBSTRING(MD5(RAND()), 1, 50) FROM test.large_tbl; Query OK, 1 row affected (0.01 sec) Records: 1 Duplicates: 0 Warnings: 0 ... 繰り返し node1> INSERT INTO test.large_tbl (col1, col2, col3) SELECT CEIL(RAND() * 10000), SUBSTRING(MD5(RAND()), 1, 30), SUBSTRING(MD5(RAND()), 1, 50) FROM test.large_tbl; Query OK, 524288 rows affected (26.92 sec) Records: 524288 Duplicates: 0 Warnings: 0 node1> select count(*) from test.large_tbl; +----------+ | count(*) | +----------+ | 1048576 | +----------+ 1 row in set (2.39 sec) node1> select * from test.large_tbl limit 3; +----+------+--------------------------------+----------------------------------+ | id | col1 | col2 | col3 | +----+------+--------------------------------+----------------------------------+ | 2 | 7703 | f73e58078ff944bc70327b557ede2b | 678cc2dc6be1f247e199e08674ff7267 | | 5 | 6419 | ce3b2bd653f0ed15a49db7f6bdc8bf | 102c712185b7a09b5802713a8f6d0912 | | 8 | 5536 | 7b12516c4223f25a6030215232f604 | c4484f9e411dcacf73a76d8982b5e643 | +----+------+--------------------------------+----------------------------------+ 3 rows in set (0.00 sec) |
2. もう1つ同様のテストテーブルを作成する
1 2 3 4 |
node1> CREATE TABLE test.large_tbl_2 LIKE test.large_tbl; node1> INSERT INTO test.large_tbl_2 SELECT * FROM test.large_tbl LIMIT 1000; Query OK, 1000 rows affected (0.08 sec) Records: 1000 Duplicates: 0 Warnings: 0 |
3. wsrep_osu_method=NBOの状態で、インデックスを追加するDDLを実行する
1 2 3 4 5 6 7 8 9 10 11 12 13 |
node1> SET SESSION wsrep_OSU_method='NBO'; Query OK, 0 rows affected (0.00 sec) node1> SHOW SESSION VARIABLES LIKE "wsrep_osu_method"; +------------------+-------+ | Variable_name | Value | +------------------+-------+ | wsrep_OSU_method | NBO | +------------------+-------+ 1 row in set (0.00 sec) node1> ALTER TABLE test.large_tbl ADD INDEX new_idx (col2); ... |
4. ALTER実行中に新しいクライアントを開いて、test.large_tblに対して SELECT / UPDATE を実行する
TOIと同様SELECTはDDL中にも実行でき、UPDATEはDDLが完了した後に完了します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
node1> SELECT * FROM test.large_tbl LIMIT 5; +----+------+--------------------------------+----------------------------------+ | id | col1 | col2 | col3 | +----+------+--------------------------------+----------------------------------+ | 2 | 7703 | f73e58078ff944bc70327b557ede2b | 678cc2dc6be1f247e199e08674ff7267 | | 5 | 6419 | ce3b2bd653f0ed15a49db7f6bdc8bf | 102c712185b7a09b5802713a8f6d0912 | | 8 | 5536 | 7b12516c4223f25a6030215232f604 | c4484f9e411dcacf73a76d8982b5e643 | | 11 | 9566 | 2f05093e7e7e0706880f8dc2141030 | 5ccde6cd7f77b3a7c63e305f2431a0a9 | | 17 | 5759 | c61d68b8af45a01d84bb8822aac8df | 9f96d420068c433ee5dacb3ca341d093 | +----+------+--------------------------------+----------------------------------+ 5 rows in set (0.02 sec) node1> UPDATE test.large_tbl SET col3 = "updated" LIMIT 1; → DDLと同時に処理は行われない。 |
5. ALTER実行中に新しいクライアントを開いて、large_tbl_2に対して SELECT / UPDATE を実行
TOIとは異なり、別のテーブルへのUPDATEがDDL中でも完了します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
node1> ALTER TABLE test.large_tbl ADD INDEX new_idx2 (col2); node1> SELECT * FROM test.large_tbl_2 LIMIT 5; +----+------+--------------------------------+----------------------------------+ | id | col1 | col2 | col3 | +----+------+--------------------------------+----------------------------------+ | 2 | 1294 | 84fb7bdf93c13a4d2d5715b183b125 | 04a51feb180bbb9c84e53e50dd86f5db | | 5 | 1879 | cf7df40cf630b98b4520010e3d8825 | da9c6fcc5d3651a4fdfb4141203480fb | | 8 | 5400 | 5efc0f2786a17dce67aeb2ae9fcbaa | 36b40aeb446c9da1618aaa88f4f1d2c4 | | 11 | 1257 | 41f5ee376ee2b7578590cd2b99442c | 69b37639b74db55459c4e8bb2d765084 | | 17 | 4685 | 8dfb6268dd1fa18251063c9e6c5dd1 | 661d788e53321da6c32ea33ef48250e6 | +----+------+--------------------------------+----------------------------------+ 5 rows in set (0.02 sec) node1> UPDATE test.large_tbl_2 SET col3 = "updated"; Query OK, 1000 rows affected (2.38 sec) Rows matched: 1000 Changed: 1000 Warnings: 0 → DDL実行中のテーブル以外へのトランザクションはDDL実行中も処理される |
NBOの制限
NBO方式にもいくつかの制限があります。
サポートされていないDDL
サポートされていないDDL(ALTER TABLE
,CREATE INDEX
,DROP INDEX
以外のDDL)を実行した場合は、DDLの実行時にエラーとなります。
1 2 |
mysql> CREATE TABLE test.t1 (id int Primary key); ERROR 1235 (42000): This version of MySQL doesn't yet support 'this query in wsrep_OSU_method NBO' |
同じテーブルへの同時DDL
NBO方式は、同じテーブルでロックが競合する2つのDDLステートメントの実行をサポートしていません。
The NBO method does not support running two DDL statements with conflicting locks on the same table. For example, you cannot run two ALTER TABLE statements for an employees table.
Non-Blocking Operations (NBO) method for Online Scheme Upgrades (OSU) | Limitations
実際に試してみるとエラーなりました。
test.large_tblにDDLを実行します。
1 2 3 4 5 6 |
node1> SET SESSION wsrep_OSU_method='NBO'; Query OK, 0 rows affected (0.00 sec) node1> ALTER TABLE test.large_tbl ADD INDEX new_idx1 (col2); Query OK, 0 rows affected (8.64 sec) Records: 0 Duplicates: 0 Warnings: 0 |
別のクライアントでも同じタイミングでtest.large_tblにDDLを実行します。
1 2 3 4 5 |
node1> SET SESSION wsrep_OSU_method='NBO'; Query OK, 0 rows affected (0.00 sec) node1> ALTER TABLE test.large_tbl ADD INDEX new_idx2 (col2); ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction |
片方のクライアントはDeadlockエラーとなりました。
NBO中のSST
NBOの実行中のState Snapshot Transfer(SST)は、失敗します。
Attempting a State Snapshot Transfer (SST) fails during the NBO operation.
Non-Blocking Operations (NBO) method for Online Scheme Upgrades (OSU)
node2が起動するタイミングでnode1でDDLを実行してみます。
1 2 3 |
node1> ALTER TABLE test.large_tbl ADD INDEX new_idx (col2); Query OK, 0 rows affected (2 min 0.00 sec) Records: 0 Duplicates: 0 Warnings: 0 |
node2はしばらくすると以下のような起動に失敗したことを示すメッセージを返します。
1 2 |
# systemctl start mysql Job for mysql.service failed because a fatal signal was delivered to the control process. See "systemctl status mysql.service" and "journalctl -xe" for details. |
node2のエラーログでは以下のようなメッセージが確認できました。
1 2 3 |
2022-02-09T05:34:19.027450Z 0 [ERROR] [MY-000000] [WSREP] Failed to read uuid:seqno from joiner script. 2022-02-09T05:34:19.027497Z 0 [ERROR] [MY-000000] [WSREP] SST script aborted with error 32 (Broken pipe) 2022-02-09T05:34:19.027675Z 3 [ERROR] [MY-000000] [Galera] State transfer request failed unrecoverably: 32 (Broken pipe). Most likely it is due to inability to communicate with the cluster primary component. Restart required. |
まとめ
TOIと異なり、DDL実行中ではないテーブルは更新し続けられるというのは、非常に便利です。OSU実行時の有力な方式となりそうです。
また、「NBOの制限」で確認した通り、サポートされていないDDLは実行できないため、GLOBALではTOIまたはRSUに設定し、NBO方式でDDLを実行したいときだけSET SESSION wsrep_OSU_method='NBO';
で設定するという運用方法がいいのではないかと思います。
サポートされているDDLが増えるのかなど、今後の機能改善には注目していきたいです。