16-bo‘lim
Tranzaksiyalar va MVCC
BEGIN, COMMIT, ROLLBACK, SAVEPOINT, ajratish darajalari, PostgreSQL MVCC mexanizmi, qulflar va deadlock.
Ushbu bo‘lim mundarijasi
Pul o'tkazmasi ikki amaldan iborat: birida pul kamayadi, ikkinchisida ko'payadi. Ikkinchisi bajarilmasa - pul yo'qoladi. Tranzaksiya aynan shuni oldini oladi.
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:
| Harf | Nomi | Ma'nosi |
|---|---|---|
| A | Atomarlik | Yo hammasi, yo hech nima |
| C | Izchillik | Cheklovlar oxirida ham buzilmaydi |
| I | Ajratilganlik | Boshqa tranzaksiya yarim ishni ko'rmaydi |
| D | Bardoshlilik | COMMIT dan keyin diskda qoladi |
BEGIN, COMMIT, ROLLBACK #
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;
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:
BEGIN;
UPDATE hisob SET balans = 0 WHERE egasi = 'Husanboy';
ROLLBACK;
SELECT egasi, balans FROM hisob ORDER BY id;
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.
Xato bo'lsa nima bo'ladi #
PostgreSQL da muhim xususiyat bor: tranzaksiya ichida xato bo'lsa, u butunlay to'xtaydi.
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;
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 emasYuqoridagi 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:
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:
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;
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.
Har qatorda yashirin ustunlar bor. UPDATE dan oldin va
keyin ularni solishtiring:
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.
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.
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:
| Daraja | Iflos o'qish | Takrorlanmas o'qish | Arvoh qator |
|---|---|---|---|
READ COMMITTED | Yo'q | Bo'ladi | Bo'ladi |
REPEATABLE READ | Yo'q | Yo'q | Yo'q |
SERIALIZABLE | Yo'q | Yo'q | Yo'q |
Uch atamaning ma'nosi:
| Atama | Ma'nosi |
|---|---|
| Iflos o'qish | Saqlanmagan o'zgarishni ko'rish |
| Takrorlanmas o'qish | Bir so'rov ikki marta har xil javob berishi |
| Arvoh qator | Ikkinchi o'qishda yangi qatorlar paydo bo'lishi |
SHOW default_transaction_isolation;
default_transaction_isolation
-------------------------------
read committed
(1 row)
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:
BEGIN ISOLATION LEVEL REPEATABLE READ;
SELECT sum(balans) AS jami_balans FROM hisob;
COMMIT;
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:
| Yozuv | Ma'nosi |
|---|---|
FOR UPDATE | Qatorni qulflab o'qish |
FOR UPDATE NOWAIT | Band bo'lsa xato ber, kutma |
FOR UPDATE SKIP LOCKED | Band qatorni tashlab ket |
FOR SHARE | Boshqalar o'qishi mumkin, yozishi yo'q |
BEGIN;
SELECT id, egasi, balans FROM hisob
WHERE egasi = 'Husanboy'
FOR UPDATE;
COMMIT;
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.
Ikki tranzaksiya bir-birining qulfini kutib qolsa, deadlock yuz beradi:
| Vaqt | 1-tranzaksiya | 2-tranzaksiya |
|---|---|---|
| 1 | Husanboy ni qulfladi | Malika ni qulfladi |
| 2 | Malika ni kutadi | Husanboy ni kutadi |
| 3 | Kutish cheksiz | Kutish cheksiz |
PostgreSQL buni bir necha soniyada aniqlaydi va bittasini majburan to'xtatadi:
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'lsin | Tranzaksiya ichida bo'lmasin |
|---|---|
Bir-biriga bog'liq UPDATE lar | Tashqi API ga so'rov |
| Balans tekshiruvi va yechish | Fayl yuklash |
| Buyurtma va uning qatorlari | Foydalanuvchidan javob kutish |
Sabab: ochiq tranzaksiya qulflarni ushlab turadi va
VACUUM ga eskirgan versiyalarni tozalashga to'sqinlik
qiladi.
BEGINbilan ikkitaUPDATEqiling vaCOMMITqiling.- Xuddi shuni
ROLLBACKbilan takrorlang va farqni ko'ring. - Tranzaksiya ichida ataylab xato buyruq yozing.
- Undan keyingi buyruq bajarildimi - tekshiring.
COMMITo'rniga nima chiqqanini yozing.SAVEPOINTqo'yib, xatodan keyin unga qayting.xminniUPDATEdan oldin va keyin solishtiring.SHOW default_transaction_isolationbilan darajani biling.FOR UPDATEbilan qatorni qulflab ko'ring.- Deadlock dan qochish qoidasini o'z so'zingiz bilan yozing.
Xulosa #
- Tranzaksiya - yo hammasi, yo hech nima kafolati.
BEGINboshlaydi,COMMITsaqlaydi,ROLLBACKbekor qiladi.- PostgreSQL da xatodan keyin tranzaksiya butunlay to'xtaydi.
- Shunda
COMMITaslidaROLLBACKga aylanadi. SAVEPOINTxatoli qismni bekor qilib, qolganini saqlab qolish imkonini beradi.- MVCC:
UPDATEqatorni 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 uchunREPEATABLE READbor. FOR UPDATE SKIP LOCKEDnavbat 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.
O‘qish tarixini saqlamoqchimisiz?
Tizimga kirsangiz, tugatgan bo‘limlaringiz saqlanadi va qoldirgan joyingizdan davom etasiz.
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.