Kartais dirbdami su MySQL galite pamatyti šią keistoką klaidą:
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction
Ši klaida praneša apie aklavietę – tai mes bandysime aptarti šiame įraše.
Kas yra Aklavietė?
Aklavietė atsiranda tada, kai nei viena iš skirtingų MySQL užklausų negali įvykti, nes abi iš jų turi po užraktą, reikalingą kitai užklausai. Šis paveiksliukas iliustruoja aklavietę:
![](https://breachdirectory.com/blog/wp-content/uploads/2020/07/Alphonsegaston.jpg)
Verta paminėti, kad aklavietės nereikėtų painioti su užraktu – įrašai yra užrakinami tada, kai išteklius bando pasiekti keli procesai tuo pačiu metu, tačiau aklavietė atsiranda tada, kai dvi ar daugiau užklausų laukia kol viena kitos pabaigos.
Aklaviečių Aptikimas
Naudojant InnoDB variklį aklavietės aptinkamos automatiškai – kai aklavietė aptinkama, variklis automatiškai grąžina užklausą į buvusią vietą tam, kad ši nepapultų į aklavietę. Tikslų atšauktų operacijų kiekį yra sunku nustatyti, nes variklis pasirenka operacijas kurios turi būti atšauktos pagal tai, kiek įrašų paveikia INSERT
, UPDATE
ar DELETE
užklausos.
Aklaviečių Pavyzdžiai
Tam, kad sumodeliuotume aklavietės situaciją, panaudosime dvi lenteles su dviejomis sesijomis toje pačioje duomenų bazėje.
Vienoje sesijoje mes paleisime šias užklausas:
START TRANSACTION;
UPDATE `table_1` SET `id` = ID WHERE `id` = 1;
Kitoje sesijoje mes taip pat paleisime panašias užklausas:
START TRANSACTION;
UPDATE `table_2` SET `id` = ID WHERE `id` = 1;
Tada vienu metu paleisime žemiau esančias užklausas. Pirmoje lentelėje paleisime šią užklausą:
UPDATE `table_1` SET `id` = ID WHERE `id` = 1;
Antroje lentelėje paleisime šią užklausą:
UPDATE `table_2` SET `id` = ID WHERE `id` = 1;
Įvykdę šias užklausas MySQL turėtume gauti tokią klaidą:
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction
Kitokį aklavietės pavyzdį galite rasti InnoDB dokumentacijoje.
Ar Aklavietės Pavojingos?
Kai kurie žmonės gali manyti, kad aklavietės yra pavojingos todėl, kad aklavietės gali neigiamai paveikti programos našumą, o tai gali tapti tiesiogine vartotojų skundų priežastimi: vartotojai ir MySQL duomenų bazių administratoriai dėl šios problemos gali kaltinti programuotojus, o programuotojai gali kaltinti duomenų bazių administratorius.
Tačiau aklaviečių neturėtume laikyti pavojingomis – net MySQL dokumentacijoje teigiama, kad aklavietės nėra pavojingos. Atsidūrus aklavietėje operacija gali būti papraščiausiai paleista iš naujo – operacijos paleidimas iš naujo yra vienas iš būdų, padedančių susidoroti su aklavietėmis MySQL ekosistemoje.
Kaip Išvengti Aklaviečių MySQL?
Išvengti MySQL aklaviečių nėra tik duomenų bazių administratorių darbas – gali atsitikti ir taip, kad į šį procesą reiks įsitraukti ir programuotojams. Aklavietės atsiranda tada, kai atsiranda kelios Coffman’o sąlygos – norint išvengti aklaviečių reiktų vengti šių sąlygų. Šias sąlygas sudaro:
- Tarpusavio atskirtis: bent viena iš užklausų turi būti atskirta nuo kitų užklausų;
- Resursų užklaikymas: viena užklausa turi užlaikyti bent vieną užklausą ir reikalauti papildomų išteklių, kuriuos užlaiko kiti procesai;
- Jokių išimčių (laikinų pertraukimų): užklausą gali būti “paleista” tik tos užklausos, kuri jį užlaiko;
- Apskritasis laukimas: kiekvienas procesas turi laukti resurso, kurį laiko kitas procesas, kuris laukia, kol bus paleistas pirmasis procesas.
Kitaip tariant, turime kelis variantus. Galime:
- Padalinti ilgas transakcijas į trumpesnes. Tai darydami galėsime leisti užraktams greičiau atsilaisvinti;
- Peržiūrėti užklausas, dirbančias su tais pačiais duomenimis – galbūt galite išvengti tų pačių dalykų prašymo tuo pačiu metu?
- Optimizuoti savo užklausas taip, kad jos prieitų prie mažiau duomenų – gerai optimizuotos užklausos gali baigtis greičiau, ir, savo ruožtu, nesudaryti aklavietės;
- Padaryti pertrauką prieš iš naujo paleisdami užklausą – aklavietėje esančios užklausos gali baigtis mums belaukiant;
- Visiškai išjungti aklavietes – MySQL užraktai ir aklavietės yra mechanizmai, kurie mums padeda išlaikyti turimų duomenų nuoseklumą ir jie neturėtų būti apeinami nebent mes norime pakenkti savo duomenims, nors kai kuriuose scenarijuose juos išjungti gali būti prasminga, nes tai padarius paspartintume savo užklausas (kai kur gali būti išvengta užklausų, kurios laukia to paties užrakto “paleidimo”) – MySQL turi parinktį, leidžiančią išjungti aklavietes: aklaviečių aptikimą galima išjungti nustatant kintamąjį
innodb_deadlock_detect
įOFF
. Aklavietes taip pat galima išjungti per komandinę eilutę, tokiu atveju reiktų naudoti--innodb-deadlock-detect
komandą. Šios komandos priimtinos reikšmės yraON
irOFF
.
Santrauka
Kaip ir minėta ankščiau, užraktai ir aklavietės yra mechanizmai, skirti užtikrinti mūsų turimų duomenų saugumą – šių mechanizmų išjungimas yra naudingas tik labai retais atvejais. Į aklavietę turėtų būti žiūrima kaip į pagalbininkę, kuri padeda mums padaryti mūsų aplikacijas efektyvesnimėmis. Norėdami užkirsti kelią aklavietėms programuotojai turėti vengti Coffmano sąlygų – jei bent vienos iš šių sąlygų bus išvengta, aklavietės neturėtų būti problema.