16-bo‘lim

Tranzaksiyalar va MVCC

BEGIN, COMMIT, ROLLBACK, SAVEPOINT, ajratish darajalari, PostgreSQL MVCC mexanizmi, qulflar va deadlock.

🕑 14 daqiqa o‘qish 📄 1 019 so‘z 👁 0 marta ko‘rilgan
Ushbu bo‘lim mundarijasi
  1. ACID
  2. BEGIN, COMMIT, ROLLBACK
  3. Xato bo'lsa nima bo'ladi
  4. SAVEPOINT
  5. MVCC - ko'p versiyali boshqaruv
  6. Ajratish darajalari
  7. Qulflar
  8. Tranzaksiya qancha davom etishi kerak
  9. Xulosa

Pul o'tkazmasi ikki amaldan iborat: birida pul kamayadi, ikkinchisida ko'payadi. Ikkinchisi bajarilmasa - pul yo'qoladi. Tranzaksiya aynan shuni oldini oladi.

SQL
CREATE TABLE hisob (
    id      int GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    egasi   text NOT NULL,
    balans  numeric(10,2) NOT NULL CHECK (balans >= 0)
);

INSERT INTO hisob (egasi, balans) VALUES
    ('Husanboy', 500000.00),
    ('Malika',   120000.00),
    ('Nodira',    80000.00);

ACID #

Tranzaksiya to'rtta kafolat beradi:

HarfNomiMa'nosi
AAtomarlikYo hammasi, yo hech nima
CIzchillikCheklovlar oxirida ham buzilmaydi
IAjratilganlikBoshqa tranzaksiya yarim ishni ko'rmaydi
DBardoshlilikCOMMIT dan keyin diskda qoladi

BEGIN, COMMIT, ROLLBACK #

SQL
BEGIN;

UPDATE hisob SET balans = balans - 50000 WHERE egasi = 'Husanboy';
UPDATE hisob SET balans = balans + 50000 WHERE egasi = 'Malika';

COMMIT;

SELECT egasi, balans FROM hisob ORDER BY id;
Natija
BEGIN
UPDATE 1
UPDATE 1
COMMIT
  egasi   |  balans
----------+-----------
 Husanboy | 450000.00
 Malika   | 170000.00
 Nodira   |  80000.00
(3 rows)

Ikkala UPDATE birga saqlandi. Endi ROLLBACK ni ko'ramiz:

SQL
BEGIN;

UPDATE hisob SET balans = 0 WHERE egasi = 'Husanboy';

ROLLBACK;

SELECT egasi, balans FROM hisob ORDER BY id;
Natija
BEGIN
UPDATE 1
ROLLBACK
  egasi   |  balans
----------+-----------
 Husanboy | 500000.00
 Malika   | 120000.00
 Nodira   |  80000.00
(3 rows)

ROLLBACK dan keyin hech narsa o'zgarmadi - go'yo UPDATE umuman bo'lmagan.

Tranzaksiyaning faqat ikkita yakuni bor BEGIN UPDATE ... -50000 UPDATE ... +50000 COMMIT ROLLBACK Ikkala amal ham saqlandi Diskda doimiy qoldi Ikkalasi ham bekor bo'ldi Baza avvalgi holatda
Yarim bajarilgan tranzaksiya degan holat yo'q

Xato bo'lsa nima bo'ladi #

PostgreSQL da muhim xususiyat bor: tranzaksiya ichida xato bo'lsa, u butunlay to'xtaydi.

SQL
BEGIN;

UPDATE hisob SET balans = balans - 100000 WHERE egasi = 'Malika';

UPDATE hisob SET balans = balans - 999999 WHERE egasi = 'Nodira';

UPDATE hisob SET balans = balans + 100000 WHERE egasi = 'Husanboy';

COMMIT;

SELECT egasi, balans FROM hisob ORDER BY id;
Natija
BEGIN
UPDATE 1
ERROR:  new row for relation "hisob" violates check constraint "hisob_balans_check"
DETAIL:  Failing row contains (3, Nodira, -919999.00).
ERROR:  current transaction is aborted, commands ignored until end of transaction block
ROLLBACK
  egasi   |  balans
----------+-----------
 Husanboy | 500000.00
 Malika   | 120000.00
 Nodira   |  80000.00
(3 rows)

Ikkinchi UPDATE CHECK (balans >= 0) cheklovini buzdi. Shundan keyin uchinchi buyruq ham bajarilmadi, va COMMIT aslida ROLLBACK ga aylandi.

COMMIT har doim ham saqlaydi degani emas

Yuqoridagi chiqishda COMMIT yozuvi emas, ROLLBACK yozuvi chiqdi.

Bu PostgreSQL ning qat'iy qoidasi: xatodan keyin tranzaksiya "to'xtatilgan" holatga o'tadi va undan keyingi har bir buyruq shunday javob beradi:

Natija
ERROR:  current transaction is aborted, commands
        ignored until end of transaction block

Ba'zi boshqa bazalar xatoli buyruqni tashlab, qolganini saqlab qo'yadi. PostgreSQL bunday qilmaydi - va bu yaxshi, chunki yarim bajarilgan o'tkazma pul yo'qolishiga olib keladi.

Bitta buyruq xato bo'lishi mumkinligini bilsangiz va qolganini saqlab qolmoqchi bo'lsangiz - SAVEPOINT ishlating.

SAVEPOINT #

SAVEPOINT - tranzaksiya ichidagi oraliq nuqta:

SQL
BEGIN;

UPDATE hisob SET balans = balans - 10000 WHERE egasi = 'Husanboy';

SAVEPOINT nuqta1;

UPDATE hisob SET balans = balans - 999999 WHERE egasi = 'Nodira';

ROLLBACK TO SAVEPOINT nuqta1;

UPDATE hisob SET balans = balans + 10000 WHERE egasi = 'Malika';

COMMIT;

SELECT egasi, balans FROM hisob ORDER BY id;
Natija
BEGIN
UPDATE 1
SAVEPOINT
ERROR:  new row for relation "hisob" violates check constraint "hisob_balans_check"
DETAIL:  Failing row contains (3, Nodira, -919999.00).
ROLLBACK
UPDATE 1
COMMIT
  egasi   |  balans
----------+-----------
 Husanboy | 490000.00
 Malika   | 130000.00
 Nodira   |  80000.00
(3 rows)

Xatoli buyruq bekor bo'ldi, lekin undan oldingi va keyingi ishlar saqlanib qoldi.

MVCC - ko'p versiyali boshqaruv #

PostgreSQL da UPDATE qatorni joyida o'zgartirmaydi. U yangi versiya yozadi va eskisini "eskirgan" deb belgilaydi.

Bu mexanizm MVCC (Multi-Version Concurrency Control) deb ataladi.

UPDATE eski qatorni o'chirmaydi UPDATE dan oldin balans = 500000 xmin=100 xmax=yo'q UPDATE UPDATE dan keyin balans = 500000 xmin=100 xmax=101 eskirgan versiya balans = 450000 xmin=101 xmax=yo'q yangi versiya Har tranzaksiya o'z raqamiga mos versiyani ko'radi xmin - qatorni yaratgan tranzaksiya raqami xmax - qatorni eskirgan deb belgilagan tranzaksiya raqami O'quvchi hech qachon yozuvchini kutmaydi Eskirgan versiyalarni keyinroq VACUUM tozalaydi
Eski versiya hali ham eski tranzaksiyalarga kerak bo'lishi mumkin

Har qatorda yashirin ustunlar bor. UPDATE dan oldin va keyin ularni solishtiring:

Natija
SELECT xmin, xmax, egasi, balans FROM hisob WHERE egasi = 'Nodira';

 xmin | xmax | egasi  |  balans
------+------+--------+----------
  742 |    0 | Nodira | 80000.00

UPDATE hisob SET balans = balans + 1000 WHERE egasi = 'Nodira';
UPDATE 1

SELECT xmin, xmax, egasi, balans FROM hisob WHERE egasi = 'Nodira';

 xmin | xmax | egasi  |  balans
------+------+--------+----------
  743 |    0 | Nodira | 81000.00

Ikkinchi so'rovda xmin boshqacha - bu boshqa qator versiyasi. Eskisi hali diskda yotibdi, lekin endi hech kim uni ko'rmaydi.

Bu sonlar sizda boshqacha bo'ladi

xmin - butun klaster bo'ylab o'suvchi tranzaksiya raqami. U necha marta tranzaksiya ochilganiga bog'liq, shuning uchun sizning bazangizda 742 emas, mutlaqo boshqa son chiqadi.

Muhimi son emas - UPDATE dan keyin u o'zgarganligi. Aynan shu MVCC yangi versiya yaratganini isbotlaydi.

MVCC ning asosiy foydasi

Bitta jumlada: o'qish yozishni to'smaydi, yozish o'qishni to'smaydi.

Hisobot 10 daqiqa davom etsa ham, u boshlangan paytdagi holatni ko'raveradi va shu vaqtda boshqalar bemalol yozishi mumkin.

Buning narxi bor: eskirgan versiyalar joy egallaydi va ularni tozalash kerak. Bu ish VACUUM zimmasida - 19-bobda ko'ramiz.

Ajratish darajalari #

Ikki tranzaksiya bir vaqtda ishlaganda bir-birini qanchalik ko'rishi - shu daraja bilan boshqariladi:

DarajaIflos o'qishTakrorlanmas o'qishArvoh qator
READ COMMITTEDYo'qBo'ladiBo'ladi
REPEATABLE READYo'qYo'qYo'q
SERIALIZABLEYo'qYo'qYo'q

Uch atamaning ma'nosi:

AtamaMa'nosi
Iflos o'qishSaqlanmagan o'zgarishni ko'rish
Takrorlanmas o'qishBir so'rov ikki marta har xil javob berishi
Arvoh qatorIkkinchi o'qishda yangi qatorlar paydo bo'lishi
SQL
SHOW default_transaction_isolation;
Natija
 default_transaction_isolation
-------------------------------
 read committed
(1 row)
PostgreSQL da iflos o'qish umuman yo'q

Standart to'rtta darajani belgilaydi, ularning eng pasti - READ UNCOMMITTED.

PostgreSQL uni qabul qiladi, lekin aslida READ COMMITTED kabi ishlaydi. Sabab: MVCC da saqlanmagan versiyani ko'rsatish uchun alohida mexanizm kerak bo'lar edi, va u hech kimga foyda keltirmaydi.

Shuning uchun PostgreSQL da amalda uchta daraja bor.

Darajani bitta tranzaksiya uchun o'zgartirish mumkin:

SQL
BEGIN ISOLATION LEVEL REPEATABLE READ;

SELECT sum(balans) AS jami_balans FROM hisob;

COMMIT;
Natija
BEGIN
 jami_balans
-------------
   700000.00
(1 row)

COMMIT

REPEATABLE READ da bu SELECT tranzaksiya ichida necha marta takrorlansa ham, bir xil javob beradi - boshqalar shu orada yozgan bo'lsa ham.

Qulflar #

UPDATE qatorni qulflaydi: ikkinchi tranzaksiya shu qatorni o'zgartirmoqchi bo'lsa, birinchisi tugagunicha kutadi.

Navbatni o'zingiz boshqarmoqchi bo'lsangiz:

YozuvMa'nosi
FOR UPDATEQatorni qulflab o'qish
FOR UPDATE NOWAITBand bo'lsa xato ber, kutma
FOR UPDATE SKIP LOCKEDBand qatorni tashlab ket
FOR SHAREBoshqalar o'qishi mumkin, yozishi yo'q
SQL
BEGIN;

SELECT id, egasi, balans FROM hisob
WHERE egasi = 'Husanboy'
FOR UPDATE;

COMMIT;
Natija
BEGIN
 id |  egasi   |  balans
----+----------+-----------
  1 | Husanboy | 500000.00
(1 row)

COMMIT

SKIP LOCKED navbat jadvallari uchun juda qulay: bir necha ishchi jarayon bitta jadvaldan vazifa olayotganda, hech biri boshqasini kutmaydi va bir vazifa ikki marta olinmaydi.

Deadlock - ikki tomonlama kutish

Ikki tranzaksiya bir-birining qulfini kutib qolsa, deadlock yuz beradi:

Vaqt1-tranzaksiya2-tranzaksiya
1Husanboy ni qulfladiMalika ni qulfladi
2Malika ni kutadiHusanboy ni kutadi
3Kutish cheksizKutish cheksiz

PostgreSQL buni bir necha soniyada aniqlaydi va bittasini majburan to'xtatadi:

Natija
ERROR:  deadlock detected
DETAIL: Process 1234 waits for ShareLock on transaction 567

Oldini olishning eng ishonchli usuli: qatorlarni har doim bir xil tartibda qulflang. Masalan id bo'yicha o'sish tartibida. Shunda ikkinchi tranzaksiya birinchisidan oldinga o'tib keta olmaydi va halqa hosil bo'lmaydi.

Tranzaksiya qancha davom etishi kerak #

Qisqa. Iloji boricha qisqa.

Tranzaksiya ichida bo'lsinTranzaksiya ichida bo'lmasin
Bir-biriga bog'liq UPDATE larTashqi API ga so'rov
Balans tekshiruvi va yechishFayl yuklash
Buyurtma va uning qatorlariFoydalanuvchidan javob kutish

Sabab: ochiq tranzaksiya qulflarni ushlab turadi va VACUUM ga eskirgan versiyalarni tozalashga to'sqinlik qiladi.

Amaliy topshiriq
  1. BEGIN bilan ikkita UPDATE qiling va COMMIT qiling.
  2. Xuddi shuni ROLLBACK bilan takrorlang va farqni ko'ring.
  3. Tranzaksiya ichida ataylab xato buyruq yozing.
  4. Undan keyingi buyruq bajarildimi - tekshiring.
  5. COMMIT o'rniga nima chiqqanini yozing.
  6. SAVEPOINT qo'yib, xatodan keyin unga qayting.
  7. xmin ni UPDATE dan oldin va keyin solishtiring.
  8. SHOW default_transaction_isolation bilan darajani biling.
  9. FOR UPDATE bilan qatorni qulflab ko'ring.
  10. Deadlock dan qochish qoidasini o'z so'zingiz bilan yozing.

Xulosa #

  • Tranzaksiya - yo hammasi, yo hech nima kafolati.
  • BEGIN boshlaydi, COMMIT saqlaydi, ROLLBACK bekor qiladi.
  • PostgreSQL da xatodan keyin tranzaksiya butunlay to'xtaydi.
  • Shunda COMMIT aslida ROLLBACK ga aylanadi.
  • SAVEPOINT xatoli qismni bekor qilib, qolganini saqlab qolish imkonini beradi.
  • MVCC: UPDATE qatorni joyida emas, yangi versiya qilib yozadi.
  • Shuning uchun o'qish yozishni to'smaydi, yozish o'qishni to'smaydi.
  • Odatiy daraja - READ COMMITTED; qat'iyroq kafolat uchun REPEATABLE READ bor.
  • FOR UPDATE SKIP LOCKED navbat jadvallari uchun eng qulay yechim.
  • Deadlock dan qochish uchun qatorlarni doim bir xil tartibda qulflang.

Keyingi bo'limda sxemalar, rollar va qator darajasidagi xavfsizlikni ko'ramiz.

Xatolik topdingizmi?

Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.