MySQL / Transaction

Transaction

Transaction нь олон SQL үйлдлийг нэг бүлэг болгон гүйцэтгэдэг — бүгд амжилттай болно, эсвэл бүгд цуцлагдана. Банкны шилжүүлэг, захиалга зэрэг алдаа зөвшөөрөхгүй үйлдэлд зайлшгүй шаардлагатай.

Яагаад transaction хэрэгтэй вэ?

sql
-- Transaction үгүй: аюултай!
UPDATE accounts SET balance = balance - 50000 WHERE id = 1;  -- Болдын данс
-- ← Яг энд серверийн алдаа гарлаа!
UPDATE accounts SET balance = balance + 50000 WHERE id = 2;  -- Оюуны данс

-- Үр дүн: Болдын данснаас мөнгө гарсан, Оюуны данс нэмэгдээгүй

Transaction ашиглавал бүгд хамт амжиж, эсвэл хамт цуцлагдана.

BEGIN / COMMIT / ROLLBACK

sql
-- Transaction эхлүүлэх
START TRANSACTION;
-- эсвэл:
BEGIN;

UPDATE accounts SET balance = balance - 50000 WHERE id = 1;
UPDATE accounts SET balance = balance + 50000 WHERE id = 2;

-- Бүх зүйл зөв болсон бол хадгалах
COMMIT;
sql
START TRANSACTION;

UPDATE accounts SET balance = balance - 50000 WHERE id = 1;
UPDATE accounts SET balance = balance + 50000 WHERE id = 2;

-- Алдаа гарсан тул бүгдийг цуцлах
ROLLBACK;
-- Хоёр UPDATE аль аль нь буцаагдана

ACID шинж чанар

Transaction нь дараах 4 шинж чанарыг баталгаажуулдаг:

| Үсэг | Нэр | Утга | | ----- | ------------------------------ | ------------------------------------------- | | A | Atomicity (Бүтнэ чанар) | Бүх үйлдэл биелнэ, эсвэл нэг ч биелэхгүй | | C | Consistency (Нийцэл) | Өгөгдлийн сан дүрмийг зөрчихгүй | | I | Isolation (Тусгаарлалт) | Зэрэгцээ transaction бие биедээ нөлөөлөхгүй | | D | Durability (Тогтвортой байдал) | COMMIT хийсэн өгөгдөл алдагдахгүй |

SAVEPOINT — завсрын цэг

Transaction дотор завсрын цэг тавьж, зөвхөн тухайн цэг хүртэл буцаах боломжтой:

sql
START TRANSACTION;

INSERT INTO orders (customer, amount) VALUES ('Болд', 15000);
SAVEPOINT after_order;  -- завсрын цэг

INSERT INTO order_items (order_id, product_id) VALUES (LAST_INSERT_ID(), 5);
SAVEPOINT after_items;

UPDATE inventory SET stock = stock - 1 WHERE product_id = 5;
-- Inventory алдаа гарлаа!

ROLLBACK TO SAVEPOINT after_items;  -- зөвхөн inventory-г буцаах
-- order болон order_items хэвээр байна

-- Гараар зассаны дараа:
UPDATE inventory SET stock = stock - 1 WHERE product_id = 5;
COMMIT;
sql
-- SAVEPOINT устгах
RELEASE SAVEPOINT after_order;

Stored procedure дотор transaction

sql
DELIMITER //

CREATE PROCEDURE TransferMoney(
  IN from_id INT,
  IN to_id   INT,
  IN amount  DECIMAL(10,2),
  OUT result VARCHAR(100)
)
BEGIN
  DECLARE v_balance DECIMAL(10,2);

  -- Алдааг барьж авах (MySQL error handler)
  DECLARE EXIT HANDLER FOR SQLEXCEPTION
  BEGIN
    ROLLBACK;
    SET result = 'Алдаа гарлаа — transaction цуцлагдлаа';
  END;

  START TRANSACTION;

  -- Үлдэгдэл шалгах
  SELECT balance INTO v_balance FROM accounts WHERE id = from_id FOR UPDATE;

  IF v_balance < amount THEN
    ROLLBACK;
    SET result = 'Үлдэгдэл хүрэлцэхгүй';
  ELSE
    UPDATE accounts SET balance = balance - amount WHERE id = from_id;
    UPDATE accounts SET balance = balance + amount WHERE id = to_id;
    COMMIT;
    SET result = 'Шилжүүлэг амжилттай';
  END IF;
END //

DELIMITER ;

CALL TransferMoney(1, 2, 50000, @res);
SELECT @res;

FOR UPDATE нь transaction дуусах хүртэл тухайн мөрийг lock хийнэ — өөр transaction зэрэгцэн өөрчлөхөөс хамгаална.

AUTO_COMMIT

MySQL нь анхдагчаар AUTO_COMMIT = 1 — мэдэгдэл болгон автоматаар COMMIT хийнэ:

sql
-- Одоогийн AUTO_COMMIT тохиргоо
SELECT @@autocommit;

-- AUTO_COMMIT унтраах
SET autocommit = 0;
-- Одоо COMMIT хийх хүртэл өөрчлөлт хадгалагдахгүй

-- Буцаах
SET autocommit = 1;

START TRANSACTION ашиглавал AUTO_COMMIT тохиргооноос үл хамааран тухайн transaction-д хяналт авна.

InnoDB vs MyISAM

MySQL-д хүснэгтийн storage engine нь transaction дэмжих эсэхийг тодорхойлно:

| | InnoDB | MyISAM | | -------------- | --------------- | ------------------- | | Transaction | ✓ Дэмждэг | ✗ Дэмждэггүй | | Foreign key | ✓ Дэмждэг | ✗ Дэмждэггүй | | Row-level lock | ✓ Мөрийн түгжээ | ✗ Хүснэгтийн түгжээ | | Crash recovery | ✓ Байдаг | ✗ Байхгүй | | Хурд (read) | Хурдан | Арай хурдан |

MySQL 5.5-аас хойш InnoDB нь анхдагч engine тул ихэнх тохиолдолд тусгайлан тохируулах шаардлагагүй.

sql
-- Хүснэгтийн engine шалгах
SHOW TABLE STATUS WHERE Name = 'orders';

-- Engine өөрчлөх (анхаар: том хүснэгтэд удаан!)
ALTER TABLE old_table ENGINE = InnoDB;

Дараагийн хичээлд:

Trigger — хүснэгтийн өөрчлөлтөд автоматаар ажилладаг тусгай procedure, audit log хэрхэн бичих аргыг үзнэ.