[groonga-dev,03808] Re: Mroonga で timestamp 型の index が破損するパターンがある(ストレージモード)

Back to archive index

Kouhei Sutou kou****@clear*****
2015年 12月 26日 (土) 23:12:12 JST


須藤です。

報告ありがとうございます!
まだ大丈夫ではないので教えてください!

> -- 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
> ;

このときの具体的な結果を教えてもらえますか?たぶん、件数が違
うとか、片方にはあってもう片方にはないレコードがあるというこ
とだと思うんですが。。。

手元でも試してみたのですが、たしかに、このSELECTだと違いがあ
ります。しかし、次のようにORDER BY a_idとすると違いがありま
せん。

--
SELECT a_id
FROM tbl_mroonga AS tav_ignore
IGNORE INDEX (t2_date)
WHERE t2_date <= current_timestamp
ORDER BY a_id;

SELECT a_id
FROM tbl_mroonga AS tav_origin
WHERE tav_origin.t2_date <= current_timestamp
ORDER BY a_id;
--

なお、ORDER BYがないときの順序は不定なので、順序が違うだけの
違いは仕様です。

In <20151****@domai*****>
  "[groonga-dev,03802] Mroonga で timestamp 型の index が破損するパターンがある(ストレージモード)" on Thu, 24 Dec 2015 19:37:27 +0900,
  各務 洋 <kagam****@outwa*****> wrote:

> お世話になります、本日サンタではなくベイマックスと言われた各務です。
> 
> なんとか 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*****
> 
> _______________________________________________
> groonga-dev mailing list
> groon****@lists*****
> http://lists.osdn.me/mailman/listinfo/groonga-dev




groonga-dev メーリングリストの案内
Back to archive index