sql >> Database >  >> RDS >> Oracle

Verhindert SELECT FOR UPDATE dat andere verbindingen worden ingevoegd wanneer de rij niet aanwezig is?

MySQL

SELECTEER ... VOOR UPDATE met UPDATE

Transacties gebruiken met InnoDB (auto-commit uitgeschakeld), een SELECT ... FOR UPDATE staat één sessie toe om een ​​bepaald record (of records) tijdelijk te vergrendelen, zodat geen enkele andere sessie het kan bijwerken. Vervolgens kan de sessie binnen dezelfde transactie daadwerkelijk een UPDATE . uitvoeren op hetzelfde record en de transactie vastleggen of terugdraaien. Dit zou u in staat stellen om het record te vergrendelen, zodat geen enkele andere sessie het kan bijwerken terwijl u misschien een andere zakelijke logica doet.

Dit wordt bereikt met vergrendeling. InnoDB gebruikt indexen voor het vergrendelen van records, dus het vergrendelen van een bestaand record lijkt eenvoudig:vergrendel gewoon de index voor dat record.

SELECTEER ... VOOR UPDATE met INSERT

Om echter SELECT ... FOR UPDATE . te gebruiken met INSERT , hoe vergrendel je een index voor een record dat nog niet bestaat? Als u het standaard isolatieniveau van REPEATABLE READ gebruikt , InnoDB zal ook gebruik maken van gap sloten. Zolang je de id . weet (of zelfs een reeks id's) om te vergrendelen, dan kan InnoDB de opening vergrendelen zodat er geen ander record in die opening kan worden ingevoegd totdat we er klaar mee zijn.

Als uw id kolom een ​​auto-increment kolom was, dan SELECT ... FOR UPDATE met INSERT INTO zou problematisch zijn omdat je niet zou weten wat de nieuwe id was totdat u het invoegde. Aangezien u echter de id die u wilt invoegen, SELECT ... FOR UPDATE met INSERT zal werken.

WAARSCHUWING

Op het standaard isolatieniveau, SELECT ... FOR UPDATE op een niet-bestaand record doet niet andere transacties blokkeren. Dus, als twee transacties beide een SELECT ... FOR UPDATE op hetzelfde niet-bestaande indexrecord krijgen ze allebei de vergrendeling, en geen van beide transacties kan het record bijwerken. Als ze het proberen, wordt er zelfs een impasse gedetecteerd.

Daarom, als u niet met een impasse wilt omgaan, kunt u het volgende doen:

INVOEREN IN ...

Start een transactie en voer de INSERT . uit . Voer uw bedrijfslogica uit en voer de transactie door of maak een rollback. Zodra u de INSERT op de niet-bestaande recordindex bij de eerste transactie, worden alle andere transacties geblokkeerd als ze proberen INSERT een record met dezelfde unieke index. Als de tweede transactie probeert een record met dezelfde index in te voegen nadat de eerste transactie de invoeging heeft uitgevoerd, krijgt deze een "duplicaatsleutel"-fout. Handel dienovereenkomstig.

SELECTEER ... VERGRENDELEN IN DE DELENMODUS

Als u selecteert met LOCK IN SHARE MODE voor de INSERT , als een eerdere transactie dat record heeft ingevoegd maar nog niet is vastgelegd, de SELECT ... LOCK IN SHARE MODE wordt geblokkeerd totdat de vorige transactie is voltooid.

Dus om de kans op dubbele sleutelfouten te verkleinen, vooral als u de vergrendelingen een tijdje vasthoudt terwijl u bedrijfslogica uitvoert voordat u ze vastlegt of terugdraait:

  1. SELECT bar FROM FooBar WHERE foo = ? LOCK FOR UPDATE
  2. Als er geen records zijn geretourneerd,
  3. INSERT INTO FooBar (foo, bar) VALUES (?, ?)


  1. Wat betekent het om aan een string te ontsnappen?

  2. MySQL-prestaties:MySQL converteren naar MariaDB

  3. Indexen in MySQL begrijpen:deel twee

  4. Top 9 databasebeheersystemen voor Joomla's sjablonen