MySQL5.0の新しい機能のひとつであるトリガー。便利な機能であることは疑う余地はないのですが、その他の新機能と同様、どうもイマヒトツ感を感じます。
今回見つけたのは、mysqldumpでダンプしたトリガ生成文を実行するとレプリケーションが停止してしまう、というもの。レプリケーション環境で運用している場合はかなり致命的です。
原因は、bin-logに書かれる内容にあります。
以下詳細ご覧ください。
(1)テーブルとトリガの作成
以下のようにテーブル test_table1 と トリガ tri_test を作成します。
CREATE DATABASE test2; use test2; CREATE TABLE test_table1 (no int, str varchar(16)); DELIMITER // CREATE TRIGGER tri_test BEFORE INSERT ON test_table1 FOR EACH ROW begin declare v_no integer; select no into v_no from test_table1 for update; update test_table2 set cnt = cnt + 1; set new.no = v_no; end// DELIMITER ;
(2)このテーブル定義とトリガをダンプします
コマンド:
mysqldump -uroot test2
ダンプ結果:
(前略) -- -- Table structure for table `test_table1` -- DROP TABLE IF EXISTS `test_table1`; CREATE TABLE `test_table1` ( `no` int(11) default NULL, `str` varchar(16) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1; -- -- Dumping data for table `test_table1` -- /*!40000 ALTER TABLE `test_table1` DISABLE KEYS */; LOCK TABLES `test_table1` WRITE; UNLOCK TABLES; /*!40000 ALTER TABLE `test_table1` ENABLE KEYS */; /*!50003 SET @OLD_SQL_MODE=@@SQL_MODE*/; DELIMITER ;; /*!50003 SET SESSION SQL_MODE="" */;; /*!50003 CREATE */ /*!50017 DEFINER=`root`@`localhost` */ /*!50003 TRIGGER `tri_test` BEFORE INSERT ON `test_table1` FOR EACH ROW begin declare v_no integer; select no into v_no from test_table1 for update; update test_table2 set cnt = cnt + 1; set new.no = v_no; end */;; DELIMITER ; (後略)
(3)テーブルを削除後、ダンプ結果を流し込み
・まず DROP TABLE test_table1; でテーブルを削除。
・その後 先ほどのダンプ結果を流し込み。
その結果 bin-log には・・・・
# at 982 #060422 0:02:53 server id 1 end_log_pos 1096 Query thread_id=4 exec_tim e=0 error_code=0 SET TIMESTAMP=1145631773; /*!40000 ALTER TABLE `test_table1` DISABLE KEYS */; # at 1096 #060422 0:02:53 server id 1 end_log_pos 1209 Query thread_id=4 exec_tim e=0 error_code=0 SET TIMESTAMP=1145631773; /*!40000 ALTER TABLE `test_table1` ENABLE KEYS */; # at 1209 #060422 0:02:54 server id 1 end_log_pos 1524 Query thread_id=4 exec_tim e=0 error_code=0 SET TIMESTAMP=1145631774; SET @@session.sql_mode=0; CREATE DEFINER=`root`@`localhost` TRIGGER `tri_test` BEFORE INSERT ON `test_table1` FOR EACH ROW begin declare v_no integer; select no into v_no from test_table1 for update; update test_table2 set cnt = cnt + 1; set new.no = v_no; end */; # End of log file
なんと CREATE TRIGGER 文の end の後に */ がついています! 開始の /*! がないのに!!
これによりレプリケーションのスレーブでの実行がエラーとなり、めでたくレプリケーション停止の結果を得られることになりましたとさ。
log-bin に記録する際に、先頭に /*!50002 とかがつくのが正しいのか、 それとも */ は除去するほうが良いのかはわかりませんが、
いずれにせよコメント(バージョン指定)処理が、bin-log出力処理の中で半端な状態であることは確かです。
なお、view でも同様の現象が起きます。
# at 2350 #060422 0:17:28 server id 1 end_log_pos 2564 Query thread_id=6 exec_tim e=0 error_code=0 SET TIMESTAMP=1145632648; CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `test_view` AS select `test_table1`.`no` AS `no` from `test_table1` */; # End of log file
20520