Mysql
 sql >> Teknologi Basis Data >  >> RDS >> Mysql

Berapa banyak baris yang akan dikunci dengan SELECT ... ORDER BY xxx LIMIT 1 FOR UPDATE?

Ini adalah pertanyaan yang bagus. InnoDB adalah mesin penguncian tingkat baris, tetapi harus mengatur kunci tambahan untuk memastikan keamanan dengan log biner (digunakan untuk replikasi; pemulihan titik waktu). Untuk mulai menjelaskannya, perhatikan contoh (naif) berikut:

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;

Karena pernyataan hanya ditulis ke log biner setelah dilakukan, pada sesi slave#2 akan diterapkan terlebih dahulu, dan akan menghasilkan hasil yang berbeda, mengakibatkan kerusakan data .

Jadi yang dilakukan InnoDB adalah menyetel kunci tambahan. Jika is_deleted diindeks, maka sebelum session1 dilakukan, tidak ada orang lain yang dapat mengubah atau memasukkan ke dalam rentang catatan di mana is_deleted=1 . Jika tidak ada indeks pada is_deleted , maka InnoDB perlu mengunci setiap baris di seluruh tabel untuk memastikan replay dalam urutan yang sama. Anda dapat menganggap ini sebagai mengunci kesenjangan , yang merupakan konsep berbeda untuk dipahami dari penguncian tingkat baris secara langsung .

Dalam kasus Anda dengan ORDER BY position ASC , InnoDB perlu memastikan bahwa tidak ada baris baru yang dapat dimodifikasi antara nilai kunci terendah dan nilai terendah "khusus". Jika Anda melakukan sesuatu seperti ORDER BY position DESC .. yah, maka tidak ada yang bisa memasukkan ke dalam kisaran ini.

Jadi inilah solusinya:

  • Pencatatan biner berbasis pernyataan menyebalkan. Saya sangat menantikan masa depan di mana kita semua beralih ke baris logging biner berbasis (tersedia dari MySQL 5.1, tetapi tidak aktif secara default).

  • Dengan replikasi berbasis baris, jika Anda mengubah tingkat isolasi menjadi read-commit, maka hanya satu baris yang cocok yang perlu dikunci.

  • Jika Anda ingin menjadi masokis, Anda juga dapat mengaktifkan innodb_locks_unsafe_for_binlog dengan replikasi berbasis pernyataan.

Pembaruan 22 April :Untuk menyalin + menempelkan versi yang ditingkatkan dari testcase Anda (tidak mencari 'di celah'):

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


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. BUAT JENIS di MySQL

  2. Bagaimana cara mengekspor database MySQL ke JSON?

  3. Menjalankan skrip .sql menggunakan MySQL dengan JDBC

  4. Menginstal MySQL di Docker gagal dengan pesan kesalahan Tidak dapat terhubung ke server MySQL lokal melalui soket

  5. Manajemen akun pengguna, peran, izin, otentikasi PHP dan MySQL - Bagian 6