sql >> Databáze >  >> RDS >> Mysql

Kolik řádků bude uzamčeno pomocí SELECT ... ORDER BY xxx LIMIT 1 PRO AKTUALIZACI?

To je skvělá otázka. InnoDB je modul zamykání na úrovni řádků, ale musí nastavit další zámky, aby byla zajištěna bezpečnost s binárním protokolem (používá se pro replikaci; obnovení v určitém okamžiku). Chcete-li to začít vysvětlovat, zvažte následující (naivní) příklad:

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;

Vzhledem k tomu, že příkazy se zapisují do binárního protokolu až po provedení, na podřízené relaci č. 2 by se použilo jako první a přineslo by jiný výsledek, vedlo by k poškození dat .

Co tedy InnoDB dělá, je nastavování dalších zámků. Pokud is_deleted je indexováno, pak před potvrzením session1 nikdo jiný nebude moci upravit nebo vložit do rozsahu záznamů, kde is_deleted=1 . Pokud na is_deleted nejsou žádné indexy , pak InnoDB potřebuje uzamknout každý řádek v celé tabulce, aby bylo zajištěno, že přehrávání bude ve stejném pořadí. Můžete si to představit jako uzamčení mezery , což je odlišný koncept, který lze pochopit od přímého zamykání na úrovni řádků .

Ve vašem případě s tímto ORDER BY position ASC , InnoDB se musí ujistit, že mezi nejnižší hodnotou klíče a „speciální“ nejnižší možnou hodnotou nelze upravit žádné nové řádky. Pokud jste udělali něco jako ORDER BY position DESC .. no, pak nikdo nemohl vložit do tohoto rozsahu.

Takže zde přichází řešení:

  • Binární protokolování založené na příkazech je na hovno. Opravdu se těším na budoucnost, kdy všichni přejdeme na řádek založené na binárním protokolování (dostupné od MySQL 5.1, ale není ve výchozím nastavení zapnuto).

  • U replikace založené na řádcích, pokud změníte úroveň izolace na read-committed, pak je třeba uzamknout pouze jeden odpovídající řádek.

  • Pokud chcete být masochistou, můžete si také zapnout innodb_locks_unsafe_for_binlog s replikací založenou na příkazech.

Aktualizace z 22. dubna :Chcete-li zkopírovat a vložit moji vylepšenou verzi vašeho testovacího případu (nehledalo se „v mezeře“):

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. Odstranění dotazu Oracle trvá příliš dlouho

  2. Virtualmin:Po změně hesla nemáte přístup k této databázi MySQL

  3. Přenos dat mezi databázemi pomocí PostgreSQL

  4. Vrácení oprávnění sloupců z propojeného serveru v SQL Server (příklady T-SQL)