MySQL 5.1.44リリース
出てます。今回は機能追加が1件、バグ修正が18件あります。バグ修正のうちパーティショニングに関するものが1件、レプリケーションに関するものが5件となっています。InnoDB PluginはMySQL 5.1.42、5.1.43から引き続きRC版の1.0.6となっています。
MySQL 5.1.44では設定パラメータが一つ追加されました。
- binlog-direct-non-transactional-updates
これはMyISAMなどトランザクションに対応していないテーブルに対する更新を、即座にバイナリログに書き出すパラメータです。これでいったい何が変わるのか、以下の例で確認してみましょう。
mysql> select * from test_i; +----+------+ | id | data | +----+------+ | 1 | 20 | | 2 | 40 | +----+------+ 2 rows in set (0.00 sec) mysql> select * from test_m; +-----+------+ | id | data | +-----+------+ | 101 | 100 | +-----+------+ 1 row in set (0.00 sec)
test_iはトランザクションをサポートしたInnoDBのテーブル、test_mはトランザクションに対応していないMyISAMのテーブルです。これらのテーブルに対し、MySQL 5.1.43までのバージョンで以下の処理を実行すると…
client1> begin; client1> update test_i set data = data + 1 where id = 1; client1> update test_m set data = 110 where id = 101; client2> begin; client2> update test_i set data = data + 1 where id = 2; client2> update test_m set data = 120 where id = 101; client2> commit; client1> commit;
test_mテーブルの内容は以下のようになります。
mysql> select * from test_m; +-----+------+ | id | data | +-----+------+ | 101 | 120 | +-----+------+ 1 row in set (0.00 sec)
ところが、バイナリログの中身を見てみると…
# at 1130 #100228 20:19:19 server id 1 end_log_pos 1199 Query thread_id=2 exec_time=0 error_code=0 SET TIMESTAMP=1267355959/*!*/; BEGIN /*!*/; # at 1199 #100228 20:18:04 server id 1 end_log_pos 1309 Query thread_id=2 exec_time=0 error_code=0 SET TIMESTAMP=1267355884/*!*/; update test_i set data = data + 1 where id = 2 /*!*/; # at 1309 #100228 20:18:54 server id 1 end_log_pos 1416 Query thread_id=2 exec_time=0 error_code=0 SET TIMESTAMP=1267355934/*!*/; update test_m set data = 120 where id = 101 /*!*/; # at 1416 #100228 20:19:19 server id 1 end_log_pos 1443 Xid = 19 COMMIT/*!*/; # at 1443 #100228 20:19:31 server id 1 end_log_pos 1512 Query thread_id=1 exec_time=0 error_code=0 SET TIMESTAMP=1267355971/*!*/; BEGIN /*!*/; # at 1512 #100228 20:17:47 server id 1 end_log_pos 1622 Query thread_id=1 exec_time=0 error_code=0 SET TIMESTAMP=1267355867/*!*/; update test_i set data = data + 1 where id = 1 /*!*/; # at 1622 #100228 20:18:44 server id 1 end_log_pos 1729 Query thread_id=1 exec_time=0 error_code=0 SET TIMESTAMP=1267355924/*!*/; update test_m set data = 110 where id = 101 /*!*/; # at 1729 #100228 20:19:31 server id 1 end_log_pos 1756 Xid = 18 COMMIT/*!*/;
このように、最終的にtest_mテーブルのdata列の値が110になってしまっています。これはつまりレプリケーションでデータ不整合が起きるとか、バックアップデータをリストア・リカバリしたら元のデータと違っているとかいう話ですから、非常にまずい状況です。どうしてこのようなことが起こるかというと、MySQLの作りとして、トランザクションの中で行った更新はCOMMITして初めてバイナリログに書き出されるようになっているためです。
MySQL 5.1.44でbinlog-direct-non-transactional-updatesを有効にすると、バイナリログが以下のように変わります。
# at 298 #100308 2:01:25 server id 1 end_log_pos 405 Query thread_id=1 exec_time=0 error_code=0 SET TIMESTAMP=1267981285/*!*/; update test_m set data = 110 where id = 101 /*!*/; # at 405 #100308 2:01:38 server id 1 end_log_pos 512 Query thread_id=2 exec_time=0 error_code=0 SET TIMESTAMP=1267981298/*!*/; update test_m set data = 120 where id = 101 /*!*/; # at 512 #100308 2:01:41 server id 1 end_log_pos 581 Query thread_id=2 exec_time=0 error_code=0 SET TIMESTAMP=1267981301/*!*/; BEGIN /*!*/; # at 581 #100308 2:01:34 server id 1 end_log_pos 691 Query thread_id=2 exec_time=0 error_code=0 SET TIMESTAMP=1267981294/*!*/; update test_i set data = data + 1 where id = 2 /*!*/; # at 691 #100308 2:01:41 server id 1 end_log_pos 718 Xid = 6 COMMIT/*!*/; # at 718 #100308 2:01:43 server id 1 end_log_pos 787 Query thread_id=1 exec_time=0 error_code=0 SET TIMESTAMP=1267981303/*!*/; BEGIN /*!*/; # at 787 #100308 2:01:17 server id 1 end_log_pos 897 Query thread_id=1 exec_time=0 error_code=0 SET TIMESTAMP=1267981277/*!*/; update test_i set data = data + 1 where id = 1 /*!*/; # at 897 #100308 2:01:43 server id 1 end_log_pos 924 Xid = 3 COMMIT/*!*/;
無事、data列の値が120になりました。
ではbinlog-direct-non-transactional-updatesは常に有効にすべきパラメータかというと、そうではありません。このパラメータには副作用があって、例えば以下のようなMyISAMとInnoDBを混ぜて利用するSQLでは逆に不整合を起こしてしまいます。
mysql> begin; mysql> update test_i set data = 25 where id = 1; mysql> update test_m set data = (select data from test_i where id = 1); mysql> commit; mysql> select * from test_m; +-----+------+ | id | data | +-----+------+ | 101 | 25 | +-----+------+ 1 row in set (0.00 sec)
# at 924 #100308 2:10:43 server id 1 end_log_pos 1051 Query thread_id=1 exec_time=0 error_code=0 SET TIMESTAMP=1267981843/*!*/; update test_m set data = (select data from test_i where id = 1); /*!*/; # at 1051 #100308 2:10:48 server id 1 end_log_pos 1120 Query thread_id=1 exec_time=0 error_code=0 SET TIMESTAMP=1267981848/*!*/; BEGIN /*!*/; # at 1120 #100308 2:10:21 server id 1 end_log_pos 1224 Query thread_id=1 exec_time=0 error_code=0 SET TIMESTAMP=1267981821/*!*/; update test_i set data = 25 where id = 1 /*!*/; # at 1224 #100308 2:10:48 server id 1 end_log_pos 1251 Xid = 11 COMMIT/*!*/;
さらに、このパラメータはMySQL 5.1で行ベースのバイナリログを使用している場合は効果がありません。このことから今回の機能追加はあくまで暫定対処だということが分かると思います。アプリケーション開発者の方は以下の流れに沿ってこのパラメータの使用有無を検討してください。
- 基本的に1つのトランザクションの中でMyISAMとInnoDBの両方を更新しないこと
- どうしても更新したい場合は、先にMyISAMを更新すること (MySQL 5.1.39リリース - SH2の日記を参照)
- それでもどうしてもMyISAMの更新が後になる場合は、MyISAMとInnoDBを混ぜて利用するSQLが無いことを確認した上でbinlog-direct-non-transactional-updatesを有効にすること
MySQLの次のメジャーバージョンでは、行ベースのバイナリログを使用した場合に限りこの問題が解決される見込みです。詳しくはWL#2687: Write non-transactional binlog events directly to binary logをご参照ください。