Incidentu reaģēšana

PostgreSQL "could not serialize access due to concurrent update": kāpēc tas notiek un ko darīt

SQLSTATE 40001 nav PostgreSQL kļūda. Tā datubāze atsakās dot jums nepareizu atbildi. Kur tā rodas, kuri izolācijas līmeņi to izraisa un kā pareizi atkārtot un samazināt šīs kļūdas, neatgriežoties pie nedroša koda.

· 12 min lasīšanai · AG Data komanda

PostgreSQL "could not serialize access due to concurrent update" nozīmē, ka jūsu transakcija mēģināja mainīt rindu, kuru cita transakcija izmainīja un apstiprināja pēc tam, kad jūsējā jau bija paņēmusi savu momentuzņēmumu (snapshot). PostgreSQL var vai nu ļaut jums pārrakstīt izmaiņas, kuras jūs nekad neredzējāt, vai pārtraukt jūsu transakciju. Repeatable Read un Serializable līmenī tas to pārtrauc. Risinājums gandrīz nekad nav panākt, lai kļūda pazūd. Jāpanāk, lai lietotne pareizi atkārto transakciju un sadursmes būtu retas.

Ziņojumi un to kopīgais

Jūs redzēsiet kādu no šiem, parasti ORM ietērptu savā izņēmuma tipā:

ERROR:  could not serialize access due to concurrent update

ERROR:  could not serialize access due to read/write dependencies among transactions
DETAIL:  Reason code: Canceled on identification as a pivot, during commit attempt.
HINT:  The transaction might succeed if retried.

Ir arī radniecīgs ziņojums could not serialize access due to concurrent delete rindai, kas tika izdzēsta jums zem kājām. Visiem tiem ir SQLSTATE 40001 (serialization_failure). Tieši pēc šī koda, nevis pēc ziņojuma teksta, jāvadās atkārtošanas loģikai.

Kad parādās katrs no tiem

Minimāls Repeatable Read piemērs:

-- sesija A                                 -- sesija B
BEGIN ISOLATION LEVEL REPEATABLE READ;
SELECT stock FROM items WHERE id = 7;   -- 10
                                            UPDATE items SET stock = 9 WHERE id = 7;
                                            -- autocommit, pabeigts
UPDATE items SET stock = stock - 1 WHERE id = 7;
-- ERROR:  could not serialize access due to concurrent update

1. solis: atkārtojiet visu transakciju, pareizi

Dokumentācija to saka tieši: lietotnēm, kas izmanto šos līmeņus, jābūt gatavām atkārtot. Atkārtošanas noteikumi ir īsi:

  1. Atkārtojiet visu transakciju no BEGIN, ieskaitot lietotnes loģiku, kas izlēma, ko rakstīt. Ja atkārtojat tikai neizdevušos UPDATE ar vērtībām, kas aprēķinātas no vecā momentuzņēmuma, jūs atjaunojat tieši to zaudēto atjauninājumu kļūdu, no kuras šī kļūda jūs sargāja.
  2. Vadieties pēc SQLSTATE. 40001 var atkārtot bez nosacījumiem. Arī 40P01 (deadlock, skatiet mūsu deadlock detected ceļvedi) ir vērts atkārtot. Unikalitātes pārkāpumus (23505) un izslēgšanas ierobežojumu pārkāpumus (23P01) drīkst atkārtot tikai uzmanīgāk, jo tās var būt pastāvīgas kļūdas.
  3. Pievienojiet limitu un pauzi. Dokumentācija brīdina, ka lielas konkurences apstākļos var būt vajadzīgi daudzi mēģinājumi. Ierobežojiet mēģinājumu skaitu, pievienojiet nejaušinātu pauzi (backoff) un reģistrējiet katru atkārtojumu žurnālā, lai redzētu biežumu.
  4. Neveiciet ārējas darbības transakcijas iekšienē. E-pasts, maksājumu API izsaukums vai ziņojums, kas nosūtīts pirms commit, atkārtosies ar katru mēģinājumu. Pārnesiet šīs darbības uz laiku pēc commit vai izmantojiet outbox tabulu.
Nekad "nelabojiet" 40001, to noķerot un turpinot. Nekas šajā transakcijā netika apstiprināts, un tās nolasītajiem datiem nedrīkst uzticēties. Norijot izņēmumu, droša pārtraukšana pārvēršas klusā datu zudumā lietotnes slānī.

2. solis: noskaidrojiet, kāpēc sadursmes ir biežas

-- kādu izolācijas līmeni sesijas patiesībā izmanto?
SHOW default_transaction_isolation;
SELECT rolname, setconfig FROM pg_roles WHERE setconfig::text LIKE '%isolation%';

-- SERIALIZABLE gadījumā: cik predikātu slēdzeņu ir tieši tagad?
SELECT locktype, count(*) FROM pg_locks
WHERE mode = 'SIReadLock' GROUP BY locktype;

-- garas transakcijas paplašina konfliktu logu
SELECT pid, now() - xact_start AS xact_age, state, left(query, 60)
FROM pg_stat_activity
WHERE xact_start IS NOT NULL ORDER BY xact_start LIMIT 10;

Servera žurnāli parāda neizdevušos vaicājumu, jo log_min_error_statement noklusējums ir error. Sagrupējiet tos pēc vaicājuma teksta, un parasti atradīsiet vienu vai divus karstos punktus: skaitītāja rindu, "pēdējoreiz atjaunināts" kolonnu vecākierakstā, rindas tabulu, bilanci.

3. solis: samaziniet kļūmju biežumu

Lielās datubāzēs

Galvenās atziņas

PostgreSQL Transakcijas Izolācija Problēmu novēršana
Sadarbība ar mums

Serializācijas kļūdu kļūst arvien vairāk?

Mēs atrodam transakcijas, kas saduras, salabojam atkārtošanas loģiku un indeksus un noskaņojam PostgreSQL, lai stingra izolācija paliktu droša un ātra produkcijas slodzē.

Sazināties Vairāk no emuāra