これは素晴らしい質問です。 InnoDBは行レベルのロックエンジンですが、バイナリログ(レプリケーションに使用されます。ポイントインタイムリカバリ)で安全性を確保するために、追加のロックを設定する必要があります。説明を始めるには、次の(単純な)例を検討してください。
session1> START TRANSACTION;
session1> DELETE FROM users WHERE is_deleted = 1; # 1 row matches (user_id 10), deleted.
session2> START TRANSACTION;
session2> UPDATE users SET is_deleted = 1 WHERE user_id = 5; # 1 row matches.
session2> COMMIT;
session1> COMMIT;
ステートメントはコミットされるとバイナリログにのみ書き込まれるため、スレーブセッション#2が最初に適用され、異なる結果が生成され、データの破損につながります 。
つまり、InnoDBが行うことは、追加のロックを設定することです。 is_deleted
の場合 がインデックスに登録されている場合、session1がコミットする前に、他の誰も変更したり、範囲に挿入したりすることはできません。 is_deleted=1
であるレコードの 。 is_deleted
にインデックスがない場合 、次に、InnoDBは、リプレイが同じ順序であることを確認するために、テーブル全体のすべての行をロックする必要があります。これは、ギャップをロックすると考えることができます。 、行レベルのロックから直接把握するのとは異なる概念 。
そのORDER BY position ASC
の場合 、InnoDBは、最小のキー値と「特別な」最小の値の間で新しい行が変更されないようにする必要があります。 ORDER BY position DESC
のようなことをした場合 ..そうですね、誰もこの範囲に挿入できませんでした。
解決策は次のとおりです:
-
ステートメントベースのバイナリロギングは最悪です。私たち全員が
rowに切り替える未来を本当に楽しみにしています。ベースのバイナリロギング (MySQL 5.1から利用できますが、デフォルトではオンになりません)。 -
行ベースのレプリケーションでは、分離レベルを読み取りコミットに変更すると、一致する1つの行のみをロックする必要があります。
-
マゾヒストになりたい場合は、 innodb_locks_unsafe_for_binlog ステートメントベースのレプリケーションを使用します。
4月22日更新 :テストケースの改善されたバージョンをコピーして貼り付けるには(「ギャップ内」を検索していませんでした):
session1> CREATE TABLE test (id int not null primary key auto_increment, data1 int, data2 int, INDEX(data1)) engine=innodb;
Query OK, 0 rows affected (0.00 sec)
session1> INSERT INTO test VALUES (NULL, 1, 2), (NULL, 2, 1), (5, 2, 2), (6, 3, 3), (3, 3, 4), (4, 4, 3);
Query OK, 6 rows affected (0.00 sec)
Records: 6 Duplicates: 0 Warnings: 0
session1> start transaction;
Query OK, 0 rows affected (0.00 sec)
session1> SELECT id FROM test ORDER BY data1 LIMIT 1 FOR UPDATE;
+----+
| id |
+----+
| 1 |
+----+
1 row in set (0.00 sec)
session2> INSERT INTO test values (NULL, 0, 99); # blocks - 0 is in the gap between the lowest value found (1) and the "special" lowest value.
# At the same time, from information_schema:
localhost information_schema> select * from innodb_locks\G
*************************** 1. row ***************************
lock_id: 151A1C:1735:4:2
lock_trx_id: 151A1C
lock_mode: X,GAP
lock_type: RECORD
lock_table: `so5694658`.`test`
lock_index: `data1`
lock_space: 1735
lock_page: 4
lock_rec: 2
lock_data: 1, 1
*************************** 2. row ***************************
lock_id: 151A1A:1735:4:2
lock_trx_id: 151A1A
lock_mode: X
lock_type: RECORD
lock_table: `so5694658`.`test`
lock_index: `data1`
lock_space: 1735
lock_page: 4
lock_rec: 2
lock_data: 1, 1
2 rows in set (0.00 sec)
# Another example:
select * from test where id < 1 for update; # blocks