Oracle 以交易 (Transaction) 保證 ACID。交易期間對修改資料列加排他鎖 (Exclusive Lock),直到 COMMIT/ROLLBACK 才釋放。長交易 = 長鎖定 = 高併發風險。
鎖定粒度預設為 Row。存取越多列 → 鎖定越多列 → 衝突機率指數上升。
-- ❌ 差:全表掃描更新
UPDATE accounts SET balance = balance - 100 WHERE cust_id = 123;
-- ✅ 好:精確主鍵定位 (單列鎖定)
UPDATE accounts SET balance = balance - 100 WHERE account_id = 456;
-- ✅ 好:批次作業分批提交
FOR r IN (SELECT account_id FROM accounts WHERE cust_id = 123) LOOP
UPDATE accounts SET balance = balance - 100 WHERE account_id = r.account_id;
IF sql%rowcount MOD 100 = 0 THEN COMMIT; END IF; -- 每 100 筆提交
END LOOP;
SELECT ... FOR UPDATE SKIP LOCKED (11g+) 非阻塞讀取鎖持有時間 = 交易執行時間。拆解長交易、延遲非必要工作。
-- ❌ 差:單一巨大交易更新多欄位
BEGIN
UPDATE emp SET sal = sal * 1.1, comm = sal * 0.1, bonus = 500 WHERE deptno = 10;
-- 假設 10 萬筆,耗時 30 秒,鎖定 30 秒
END;
-- ✅ 好:拆分為多個短交易
BEGIN
UPDATE emp SET sal = sal * 1.1 WHERE deptno = 10; -- 10 秒
COMMIT;
END;
BEGIN
UPDATE emp SET comm = sal * 0.1 WHERE deptno = 10; -- 8 秒
COMMIT;
END;
BEGIN
UPDATE emp SET bonus = 500 WHERE deptno = 10; -- 5 秒
COMMIT;
END;
DBMS_SCHEDULER / DBMS_JOB 排程批次作業-- ❌ 災難:前台彈窗確認在交易中間
BEGIN
UPDATE orders SET status = 'PROCESSING' WHERE order_id = :id;
-- 這裡停頓等待使用者按「確認」...
UPDATE orders SET status = 'CONFIRMED' WHERE order_id = :id;
COMMIT;
END;
使用者編輯畫面 → 修改欄位 → 點「儲存」 → 後台一個 MERGE 或多個 UPDATE 於單一交易提交 → 回傳結果。編輯過程完全不開啟資料庫交易。
| 技巧 | 說明 |
|---|---|
| 低隔離等級 | 非關鍵查詢可用 READ ONLY 交易或 SET TRANSACTION ISOLATION LEVEL READ COMMITTED (預設) |
| 部分回滾 | SAVEPOINT sp1; ... ROLLBACK TO sp1; 僅回滾部分操作 |
| NOWAIT/SKIP LOCKED | SELECT ... FOR UPDATE NOWAIT 立即失敗;SKIP LOCKED 跳過被鎖列 (佇列處理利器) |
| 避免不必要的 SELECT FOR UPDATE | 純查詢不加 FOR UPDATE,減少排他鎖 |
「交易像手術刀:精準、快速、一次切乾淨,縫合就走人。」