各務 洋
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*****