各務 洋
kagam****@outwa*****
2015年 12月 24日 (木) 19:37:27 JST
お世話になります、本日サンタではなくベイマックスと言われた各務です。 なんとか TIMESTAMP 型のインデックスが破損するパターンを掴めたようですので ご報告させていただきます。 条件: Mroonga のレコードを削除する事。 InnoDB と Mroonga のレコード操作が同一のトランザクション内で行われている事。 同様のトランザクション処理がいくつか同時に実行されている事。 そのいずれかの処理中になんらかの理由でエラーになる事。(Roll Back の発生) を満たした際に発生するようです。 (ありゃ、トランザクション内に削除処理がありました) ただ、下記の再現手順を作っていて分かったのですが、トランザクションは関 係ない比較的単純な DELETE でも処理件数が多いと発生するのかもしれません。 というのも 再現手順での run.sql を作成する際、Mroonga のテーブルから一 旦消しながら削除用SQLを作成しているのですが、指定件数分 DELETE 文が生 成されない事があります。 Null が返る時があるのです。 (最後に Null を足しているのはその名残) innodb 側のテーブルで同じ処理を行うと期待通りの件数が出力され、また 対象レコードも 0 件になります。 環境: CentOS 7.1 MySQL 5.7.9 Mroonga 5.10 (CentOS 6.4, MySQL 5.6.26 でも同様です) 再現手順: CREATE DATABASE db_test; USE db_test; DROP TABLE IF EXISTS `tbl_mroonga`; CREATE TABLE `tbl_mroonga` ( `id` BIGINT(20) NOT NULL AUTO_INCREMENT, `t1_date` TIMESTAMP NOT NULL DEFAULT current_timestamp, `t2_date` TIMESTAMP NOT NULL DEFAULT '0000-00-00', `a_id` BIGINT(20) NOT NULL Default 0, `t_text` LONGTEXT, PRIMARY KEY (`id`), UNIQUE KEY `a_id` (`a_id`), INDEX `t2_date` (`t2_date`), FULLTEXT INDEX `t_text` (`t_text` ) COMMENT 'normalizer "NormalizerMySQLUnicodeCIExceptKanaCIKanaWithVoicedSoundMark"' ) ENGINE=mroonga DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; DROP TABLE IF EXISTS `tbl_innodb`; CREATE TABLE `tbl_innodb` ( `id` BIGINT(20) NOT NULL AUTO_INCREMENT, `t1_date` TIMESTAMP NOT NULL DEFAULT current_timestamp, `t2_date` TIMESTAMP NOT NULL DEFAULT '0000-00-00', `oa_id` BIGINT(20) NOT NULL Default 0, PRIMARY KEY (`id`), INDEX `oa_id` (`oa_id`), INDEX `t2_date` (`t2_date`) ) ENGINE=Innodb DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; DROP TABLE IF EXISTS `tbl_dummy`; CREATE TABLE `tbl_dummy` ( `id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT, `t2_date` TIMESTAMP NOT NULL DEFAULT current_timestamp ) ENGINE=Innodb DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -- レコードを増やす INSERT INTO tbl_dummy (t2_date) VALUES ((SELECT ADDTIME(CONCAT_WS(' ','2015-06-01' + INTERVAL RAND() * 365 DAY, '00:00:00'), SEC_TO_TIME(FLOOR(0 + (RAND() * 86401)))))); INSERT INTO tbl_dummy (t2_date) SELECT ADDTIME(CONCAT_WS(' ','2015-06-01' + INTERVAL RAND() * 365 DAY, '00:00:00'), SEC_TO_TIME(FLOOR(0 + (RAND() * 86401)))) FROM tbl_dummy; INSERT INTO tbl_dummy (t2_date) SELECT ADDTIME(CONCAT_WS(' ','2015-06-01' + INTERVAL RAND() * 365 DAY, '00:00:00'), SEC_TO_TIME(FLOOR(0 + (RAND() * 86401)))) FROM tbl_dummy; INSERT INTO tbl_dummy (t2_date) SELECT ADDTIME(CONCAT_WS(' ','2015-06-01' + INTERVAL RAND() * 365 DAY, '00:00:00'), SEC_TO_TIME(FLOOR(0 + (RAND() * 86401)))) FROM tbl_dummy; INSERT INTO tbl_dummy (t2_date) SELECT ADDTIME(CONCAT_WS(' ','2015-06-01' + INTERVAL RAND() * 365 DAY, '00:00:00'), SEC_TO_TIME(FLOOR(0 + (RAND() * 86401)))) FROM tbl_dummy; INSERT INTO tbl_dummy (t2_date) SELECT ADDTIME(CONCAT_WS(' ','2015-06-01' + INTERVAL RAND() * 365 DAY, '00:00:00'), SEC_TO_TIME(FLOOR(0 + (RAND() * 86401)))) FROM tbl_dummy; INSERT INTO tbl_dummy (t2_date) SELECT ADDTIME(CONCAT_WS(' ','2015-06-01' + INTERVAL RAND() * 365 DAY, '00:00:00'), SEC_TO_TIME(FLOOR(0 + (RAND() * 86401)))) FROM tbl_dummy; INSERT INTO tbl_dummy (t2_date) SELECT ADDTIME(CONCAT_WS(' ','2015-06-01' + INTERVAL RAND() * 365 DAY, '00:00:00'), SEC_TO_TIME(FLOOR(0 + (RAND() * 86401)))) FROM tbl_dummy; INSERT INTO tbl_dummy (t2_date) SELECT ADDTIME(CONCAT_WS(' ','2015-06-01' + INTERVAL RAND() * 365 DAY, '00:00:00'), SEC_TO_TIME(FLOOR(0 + (RAND() * 86401)))) FROM tbl_dummy; INSERT INTO tbl_dummy (t2_date) SELECT ADDTIME(CONCAT_WS(' ','2015-06-01' + INTERVAL RAND() * 365 DAY, '00:00:00'), SEC_TO_TIME(FLOOR(0 + (RAND() * 86401)))) FROM tbl_dummy; INSERT INTO tbl_dummy (t2_date) SELECT ADDTIME(CONCAT_WS(' ','2015-06-01' + INTERVAL RAND() * 365 DAY, '00:00:00'), SEC_TO_TIME(FLOOR(0 + (RAND() * 86401)))) FROM tbl_dummy; INSERT INTO tbl_dummy (t2_date) SELECT ADDTIME(CONCAT_WS(' ','2015-06-01' + INTERVAL RAND() * 365 DAY, '00:00:00'), SEC_TO_TIME(FLOOR(0 + (RAND() * 86401)))) FROM tbl_dummy; INSERT INTO tbl_dummy (t2_date) SELECT ADDTIME(CONCAT_WS(' ','2015-06-01' + INTERVAL RAND() * 365 DAY, '00:00:00'), SEC_TO_TIME(FLOOR(0 + (RAND() * 86401)))) FROM tbl_dummy; INSERT INTO tbl_dummy (t2_date) SELECT ADDTIME(CONCAT_WS(' ','2015-06-01' + INTERVAL RAND() * 365 DAY, '00:00:00'), SEC_TO_TIME(FLOOR(0 + (RAND() * 86401)))) FROM tbl_dummy; INSERT INTO tbl_dummy (t2_date) SELECT ADDTIME(CONCAT_WS(' ','2015-06-01' + INTERVAL RAND() * 365 DAY, '00:00:00'), SEC_TO_TIME(FLOOR(0 + (RAND() * 86401)))) FROM tbl_dummy; TRUNCATE TABLE tbl_mroonga; INSERT INTO tbl_mroonga (a_id, t2_date) SELECT id, t2_date FROM tbl_dummy ORDER BY id; TRUNCATE TABLE tbl_innodb; INSERT INTO tbl_innodb (oa_id, t2_date) SELECT id, t2_date FROM tbl_dummy ORDER BY id; ## 概ね総レコード数の半分程度を削除対象にする。本番環境を模して連番で消さないようにする。 cat << '__EOF__' >while.sql SET @a_id:=(SELECT a_id FROM tbl_mroonga WHERE t2_date <= current_timestamp ORDER BY RAND() LIMIT 1); SELECT CONCAT('START TRANSACTION; UPDATE tbl_innodb SET oa_id = 0 WHERE oa_id =', @ a_id,'; DELETE FROM tbl_mroonga WHERE a_id = ', @ a_id,'; COMMIT WORK;') AS ''; DELETE FROM tbl_mroonga WHERE a_id = @a_id; __EOF__ SELECT COUNT(id) FROM tbl_dummy WHERE t2_date <= current_timestamp; ## ↑の count の数(対象レコード分)だけ回す rm -f run.sql;i=0; while [ ${i} -lt 4625 ]; do ((i ++)); mysql -N db_test < while.sql >> run.sql; echo ${i}; done echo "Null">> run.sql echo "Null">> run.sql ## ↑SQLエラーにする -- いったんレコードを戻す TRUNCATE TABLE tbl_mroonga; INSERT INTO tbl_mroonga (a_id, t2_date) SELECT id, t2_date FROM tbl_dummy ORDER BY id; ## どん! mysql db_test < run.sql を実行しながら、別セッションで rollback するSQLをガンガン実行 START TRANSACTION; UPDATE tbl_innodb SET oa_id = 0 WHERE oa_id =2756; DELETE FROM tbl_mroonga WHERE a_id = 2756; ROLLBACK WORK; START TRANSACTION; UPDATE tbl_innodb SET oa_id = 0 WHERE oa_id =0; DELETE FROM tbl_mroonga WHERE a_id = 2756; ROLLBACK WORK; -- run.sql の処理が完了頃に Indexの使用/未使用で SELECT 結果に差がでる SELECT a_id FROM tbl_mroonga AS tav_ignore IGNORE INDEX (t2_date) WHERE t2_date <= current_timestamp ; SELECT a_id FROM tbl_mroonga AS tav_origin WHERE tav_origin.t2_date <= current_timestamp ; 完全ではないのですが、再現性はそこそこあります。 GTID を使用する順同期レプリケーション時に怒られてしまうので、トランザ クション処理内からは Mroonga を外す処理に切替中です。 よろしくお願いします。 ---- 各務 kagam****@outwa*****