sql >> Database teknologi >  >> RDS >> Mysql

Hvor mange rækker vil blive låst af SELECT ... BESTIL EFTER xxx LIMIT 1 FOR OPDATERING?

Dette er et godt spørgsmål. InnoDB er en låsemaskine på rækkeniveau, men den skal indstille yderligere låse for at sikre sikkerheden med den binære log (bruges til replikering; gendannelse af tidspunkter). For at begynde at forklare det, overvej følgende (naive) eksempel:

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;

Fordi udsagn kun skrives til den binære log, når først de er forpligtet, vil slavesession#2 gælde først og ville producere et andet resultat, førende til datakorruption .

Så hvad InnoDB gør, er at sætte yderligere låse. Hvis is_deleted er indekseret, før session1 forpligter sig, vil ingen andre være i stand til at ændre eller indsætte i området af poster, hvor is_deleted=1 . Hvis der ikke er nogen indekser på is_deleted , så skal InnoDB låse hver række i hele tabellen for at sikre, at genafspilningen er i samme rækkefølge. Du kan tænke på dette som at låse hullet , hvilket er et andet koncept at forstå fra låsning på rækkeniveau direkte .

I dit tilfælde med den ORDER BY position ASC , skal InnoDB sørge for, at ingen nye rækker kan ændres mellem den laveste nøgleværdi og en "særlig" lavest mulige værdi. Hvis du gjorde noget som ORDER BY position DESC .. ja, så kunne ingen indsætte i dette interval.

Så her kommer løsningen:

  • Udsagn baseret binær logning stinker. Jeg ser virkelig frem til en fremtid, hvor vi alle skifter til rækken baseret binær logning (tilgængelig fra MySQL 5.1, men ikke slået til som standard).

  • Med Rækkebaseret replikering, hvis du ændrer isolationsniveauet til read-committed, er det kun den ene række, der matcher, der skal låses.

  • Hvis du vil være masochist, kan du også slå innodb_locks_unsafe_for_binlog med sætningsbaseret replikering.

Opdatering 22. april :For at kopiere + indsætte min forbedrede version af din testcase (den søgte ikke 'in the gap'):

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. DATE() Eksempler – MySQL

  2. De brugte SELECT-sætninger har et andet antal kolonner

  3. Python3.4 kan ikke installere mysql-python

  4. Sådan trækkes aktuel dato klokkeslæt fra dato klokkeslæt i databasen - PHP