質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
DELETE

ファイルシステムからファイル、データベースからレコードを削除することまたはメモリ内のオブジェクトの割り当てを取り消すことをさします。もしくは、HTTPプロトコルのDELETEを指すこともあります。

MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

Q&A

解決済

2回答

1388閲覧

mysql5.7のInnoDBテーブルに対する、delete文によるセカンダリインデックスカラムへの排他ネクストキーロック(exclusive next-key lock)のロック範囲

tannukimaru

総合スコア13

DELETE

ファイルシステムからファイル、データベースからレコードを削除することまたはメモリ内のオブジェクトの割り当てを取り消すことをさします。もしくは、HTTPプロトコルのDELETEを指すこともあります。

MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

0グッド

1クリップ

投稿2017/10/24 04:40

編集2017/10/24 05:20

###前提・実現したいこと

mysql5.7のInnoDBテーブルをノンユニークなセカンダリインデックスカラムで、並列処理にて行を一括削除したいが、排他ネクストキーロックのおそらく前方へのギャップロックにより、WHERE句条件の対象外のレコードもロックされ、並列処理で異なるWHERE句条件を指定してもロック待ちとなってしまう(最終的には、削除後にinsertまで実行しデータを入れ替えしたい)
上記を、並列処理でロック待ちを発生させずに処理したい。

※テーブルイメージ:売上明細情報テーブル
シーケンスID INT、年月 INT(YYYYMM)、、、その他の売上明細単位のカラム、、、
⇒ IDではもちろんユニークだが、年月ではユニークにはならない。
それを、並列処理にて、年月カラム(セカンダリインデックスあり)で削除して、データを入れ替えたい。

###発生している問題・エラーメッセージ

Aプロセスが、トランザクションを開始
201709を削除(delete from 売上明細情報テーブル where 年月 = 201709)、OK
(この後insert用のデータ加工に時間がかかる)
Aプロセスのトランザクションの完了を待たずに、Bプロセスが、トランザクションを開始
201708を削除(delete from 売上明細情報テーブル where 年月 = 201708)、ここでロック待ち★

###試したこと

トランザクション分離レベルは mysql> SELECT @@GLOBAL.tx_isolation, @@tx_isolation; +-----------------------+-----------------+ | @@GLOBAL.tx_isolation | @@tx_isolation | +-----------------------+-----------------+ | REPEATABLE-READ | REPEATABLE-READ | +-----------------------+-----------------+ 1 row in set (0.00 sec) これだと、リファレンスを見る限り難しそうだったので、、 トランザクション分離レベルを、"READ COMMITTED"に変更して打開策を探す。 デフォルトのautocommitを念のため、変更しておく mysql> set autocommit = 0; Query OK, 0 rows affected (0.00 sec) mysql> SELECT @@autocommit; +--------------+ | @@autocommit | +--------------+ | 0 | +--------------+ 1 row in set (0.00 sec) mysql> SET TRANSACTION ISOLATION LEVEL READ COMMITTED; Query OK, 0 rows affected (0.00 sec) mysql> START TRANSACTION; Query OK, 0 rows affected (0.00 sec) mysql> delete from 売上明細情報テーブル where 年月 = 201709; Query OK, 8345 rows affected (2.18 sec) ここで、trx_rows_lockedを確認、16690(倍になる、、嫌な予感) mysql> use information_schema; Database changed mysql> SELECT trx_rows_locked FROM INNODB_TRX; +-----------------+ | trx_rows_locked | +-----------------+ | 16690 | +-----------------+ 1 row in set (0.00 sec) 以下、別窓から デフォルトのautocommitを念のため、変更しておく mysql> set autocommit = 0; Query OK, 0 rows affected (0.00 sec) mysql> SELECT @@autocommit; +--------------+ | @@autocommit | +--------------+ | 0 | +--------------+ 1 row in set (0.00 sec) mysql> SET TRANSACTION ISOLATION LEVEL READ COMMITTED; Query OK, 0 rows affected (0.00 sec) mysql> START TRANSACTION; Query OK, 0 rows affected (0.00 sec) mysql> delete from 売上明細情報テーブル where 年月 = 201708; ロック待ち★ 疑問点:トランザクション分離レベルを"READ COMMITED"に変更することで ギャップロックが無効化されるのを期待し、つまり排他ネクストキーロック のインデックスレコードに対するレコードロックのみが適用されることを 意図した(その場合、異なるWHERE句条件で削除ができるはず)が依然として ギャップロックされているように見える。。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

すでに解決済みにされてしまったのですが、ちょっとだけ補足を。
私が提案したのは副問い合わせではなく、クエリをMySQLに投げるプログラム側で売上明細情報テーブルのプライマリキーを取得してからwhere inのSQLを生成してdeleteをかけるという意味です。

sql

1select id from 売上明細情報テーブル where 年月 = 201709;

上記のクエリでidを取得してから、

sql

1delete from 売上明細情報テーブル where id in (1,3,5,6,10,...);

のように、IN句の中身を作ってクエリを投げると。
実際にやってみたところ、デフォルトのREPEATABLE-READでもokでした。
あとは一度にdeleteする件数を現実的な値(1000件ずつとか)にしてあげればいいでしょう。

対象テーブルinfo

sql

1mysql> select * from info; 2+----+----------+-------+ 3| id | date | value | 4+----+----------+-------+ 5| 1 | 20171025 | aaa | 6| 2 | 20171025 | bbb | 7| 3 | 20171024 | ccc | 8| 4 | 20171024 | ddd | 9+----+----------+-------+ 104 rows in set (0.00 sec)

接続その1(対象:date=20171025)

sql

1mysql> set autocommit = 0; 2Query OK, 0 rows affected (0.00 sec) 3 4mysql> START TRANSACTION; 5Query OK, 0 rows affected (0.00 sec) 6 7mysql> delete from info where id in (1,2); 8Query OK, 2 rows affected (0.00 sec)

接続その2(対象:date=20171024)

sql

1mysql> set autocommit = 0; 2Query OK, 0 rows affected (0.00 sec) 3 4mysql> START TRANSACTION; 5Query OK, 0 rows affected (0.00 sec) 6 7mysql> delete from info where id in (3,4); 8Query OK, 2 rows affected (0.01 sec)

投稿2017/10/25 10:29

ooeok

総合スコア469

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

ベストアンサー

対象レコードのプライマリキーを取得してから、プライマリキーでdeleteしたらどうでしょうか?
プライマリインデックスに対するwhere inならレコードロックになるので、デッドロックは発生しないと思います。
試してないので憶測ですが。

投稿2017/10/24 12:14

ooeok

総合スコア469

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tannukimaru

2017/10/24 13:29

ご回答ありがとうございます。 PKカラムの指定でユニークに1件ずつ削除すれば、他方でロック待ちは発生しないようです(分離レベルがREPEATABLE-READであっても)。 また、where in句で動的(... where id in (select id from (select id from hoge where non_unique_secondary_index_column = value) tmp))にPKカラムを取得指定して削除しようとすると、全行ロックされているような動きとなりました。 個別削除だと、削除に時間がかかりすぎてしまうのと、別問題としてその後のinsertで再度ロック待ちが発生してしまい、、結果、del&insertでデータの入れ替えを並列で行うのは難しいという結論となりました。 重ねてご回答感謝いたします。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問