Mysql
 sql >> Baza danych >  >> RDS >> Mysql

Ile wierszy zostanie zablokowanych przez WYBIERZ... ZAMÓW WEDŁUG xxx LIMIT 1 DO AKTUALIZACJI?

To świetne pytanie. InnoDB jest mechanizmem blokowania na poziomie wiersza, ale musi ustawić dodatkowe blokady, aby zapewnić bezpieczeństwo za pomocą dziennika binarnego (używanego do replikacji; odzyskiwanie punktu w czasie). Aby zacząć to wyjaśniać, rozważ następujący (naiwny) przykład:

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;

Ponieważ instrukcje są zapisywane w dzienniku binarnym dopiero po ich zatwierdzeniu, sesja podrzędna nr 2 miałaby zastosowanie jako pierwsza i dałaby inny wynik, prowadząc do uszkodzenia danych .

Więc to, co robi InnoDB, to ustawia dodatkowe blokady. Jeśli is_deleted jest indeksowany, to przed zatwierdzeniem sesji 1 nikt inny nie będzie mógł modyfikować lub wstawiać do zakresu rekordów, gdzie is_deleted=1 . Jeśli nie ma żadnych indeksów na is_deleted , InnoDB musi zablokować każdy wiersz w całej tabeli, aby upewnić się, że powtórka jest w tej samej kolejności. Możesz myśleć o tym jako o zamknięciu luki , co jest inną koncepcją niż bezpośrednie blokowanie na poziomie wiersza .

W twoim przypadku z tym ORDER BY position ASC , InnoDB musi upewnić się, że nie można modyfikować żadnych nowych wierszy między najniższą wartością klucza a „specjalną” najniższą możliwą wartością. Jeśli zrobiłeś coś takiego jak ORDER BY position DESC .. no cóż, nikt nie mógł wstawić do tego zakresu.

Oto rozwiązanie:

  • Logowanie binarne na podstawie instrukcji jest do niczego. Naprawdę nie mogę się doczekać przyszłości, w której wszyscy przejdziemy do row oparte na logowaniu binarnym (dostępny w MySQL 5.1, ale nie jest domyślnie włączony).

  • W przypadku replikacji opartej na wierszach, jeśli zmienisz poziom izolacji na zatwierdzony do odczytu, tylko jeden pasujący wiersz musi zostać zablokowany.

  • Jeśli chcesz być masochistą, możesz także włączyć innodb_locks_unsafe_for_binlog z replikacją opartą na instrukcjach.

Aktualizacja 22 kwietnia :Aby skopiować i wkleić moją ulepszoną wersję swojego przypadku testowego (nie szukano „w przerwie”):

Test
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. jak załadować plik danych w amazon RDS?

  2. Czy MySQL może używać indeksów, gdy między warunkami jest OR?

  3. Jak wybrać pierwszy wiersz dla każdej grupy w MySQL?

  4. Połącz się ze zdalną bazą danych MySQL za pomocą Pythona

  5. Wyzwalacz MySQL przed wstawieniem wartości Sprawdzanie