📖 数据密集型设计

ACID事务原理与实现

深入探讨ACID事务原理与数据库事务实现机制

一、ACID概述

ACID是数据库事务的四个基本特性,确保数据操作的可靠性和一致性。ACID是原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)的缩写。

二、ACID特性详解

2.1 原子性(Atomicity)

原子性确保事务中的所有操作要么全部执行,要么全部不执行:

graph TD A[事务开始] --> B[操作1] B --> C[操作2] C --> D{全部成功?} D --> E[是] D --> F[否] E --> G[提交事务] F --> H[回滚事务] G --> I[数据持久化] H --> J[数据恢复到事务前状态]

2.2 一致性(Consistency)

一致性确保事务执行前后数据的完整性约束不变:

-- 转账事务:保证总金额不变
BEGIN TRANSACTION;

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

-- 检查约束
IF (SELECT SUM(balance) FROM accounts) != @original_sum THEN
    ROLLBACK;
ELSE
    COMMIT;
END IF;

2.3 隔离性(Isolation)

隔离性确保多个事务并发执行时互不干扰:

sequenceDiagram participant T1 as 事务1 participant T2 as 事务2 participant DB as 数据库 T1->>DB: BEGIN TRANSACTION T2->>DB: BEGIN TRANSACTION T1->>DB: UPDATE accounts SET balance = balance - 100 WHERE id = 1 T2->>DB: UPDATE accounts SET balance = balance + 100 WHERE id = 2 T1->>DB: COMMIT T2->>DB: COMMIT

2.4 持久性(Durability)

持久性确保事务提交后数据永久保存:

graph TD A[事务提交] --> B[写入重做日志] B --> C[写入数据文件] C --> D[事务完成] D --> E[系统崩溃] E --> F[重启恢复] F --> G[从重做日志恢复] G --> H[数据恢复到提交状态]

三、事务隔离级别

3.1 隔离级别定义

隔离级别 脏读 不可重复读 幻读
READ UNCOMMITTED 允许 允许 允许
READ COMMITTED 禁止 允许 允许
REPEATABLE READ 禁止 禁止 允许
SERIALIZABLE 禁止 禁止 禁止

3.2 并发问题详解

脏读(Dirty Read)

一个事务读取到另一个未提交事务的数据:

sequenceDiagram participant T1 as 事务1 participant T2 as 事务2 T1->>T1: UPDATE balance = 200 (未提交) T2->>T1: SELECT balance (读到200) T1->>T1: ROLLBACK (balance恢复为100) T2->>T2: 使用了脏数据200

不可重复读(Non-Repeatable Read)

同一个事务中多次读取同一数据,结果不同:

sequenceDiagram participant T1 as 事务1 participant T2 as 事务2 T1->>T1: SELECT balance (读到100) T2->>T2: UPDATE balance = 200 T2->>T2: COMMIT T1->>T1: SELECT balance (读到200)

幻读(Phantom Read)

同一个事务中多次查询,返回的行数不同:

sequenceDiagram participant T1 as 事务1 participant T2 as 事务2 T1->>T1: SELECT COUNT(*) FROM users (返回10) T2->>T2: INSERT INTO users ... T2->>T2: COMMIT T1->>T1: SELECT COUNT(*) FROM users (返回11)

3.3 设置隔离级别

-- MySQL设置隔离级别
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;

-- PostgreSQL设置隔离级别
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

-- SQL Server设置隔离级别
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;

四、并发控制机制

4.1 锁机制

行级锁

-- MySQL行级锁
BEGIN TRANSACTION;
SELECT * FROM orders WHERE id = 1 FOR UPDATE;  -- 加排他锁
UPDATE orders SET status = 'completed' WHERE id = 1;
COMMIT;

表级锁

-- MySQL表级锁
LOCK TABLES orders WRITE;
UPDATE orders SET status = 'completed' WHERE id = 1;
UNLOCK TABLES;

意向锁

意向锁表示事务有意向对表中的某些行加锁:

锁类型 缩写 说明
意向共享锁 IS 意向对行加共享锁
意向排他锁 IX 意向对行加排他锁
共享锁 S 只读锁
排他锁 X 读写锁

4.2 MVCC(多版本并发控制)

MVCC通过维护数据的多个版本来实现并发控制:

graph TD A[数据行] --> B[版本1: 事务1创建] A --> C[版本2: 事务2修改] A --> D[版本3: 事务3修改] E[事务1读取] --> B F[事务2读取] --> C G[事务3读取] --> D

4.3 MVCC实现原理

-- InnoDB MVCC字段
-- DB_TRX_ID: 事务ID
-- DB_ROLL_PTR: 回滚指针
-- DB_ROW_ID: 行ID

-- 读已提交:读取最新提交的版本
-- 可重复读:读取事务开始时的版本

五、事务实现机制

5.1 事务日志

重做日志(Redo Log)

重做日志用于崩溃恢复:

-- InnoDB重做日志
-- 记录数据变更,确保持久性

-- 写入流程:缓冲池 -> Redo Log Buffer -> Redo Log File

-- innodb_flush_log_at_trx_commit:
-- 0: 每秒刷盘
-- 1: 每次提交刷盘(默认,最安全)
-- 2: 每次提交写入OS缓存,每秒刷盘

回滚日志(Undo Log)

回滚日志用于事务回滚:

-- InnoDB回滚日志
-- 记录数据变更前的状态,用于回滚

-- Undo Log类型:
-- INSERT Undo Log: 记录插入操作
-- UPDATE Undo Log: 记录更新操作

-- Undo Log生命周期:事务提交后保留,用于MVCC读取

5.2 两阶段提交

两阶段提交确保多个资源的事务一致性:

sequenceDiagram participant TM as 事务管理器 participant RM1 as 资源管理器1 participant RM2 as 资源管理器2 TM->>RM1: Prepare RM1-->>TM: Prepared TM->>RM2: Prepare RM2-->>TM: Prepared alt 所有资源准备成功 TM->>RM1: Commit RM1-->>TM: Committed TM->>RM2: Commit RM2-->>TM: Committed else 有资源准备失败 TM->>RM1: Rollback RM1-->>TM: Rolled back TM->>RM2: Rollback RM2-->>TM: Rolled back end

六、事务优化

6.1 事务大小控制

尽量缩小事务范围,减少锁竞争:

// 不好:事务范围太大
using (var transaction = db.BeginTransaction())
{
    // 长时间操作
    Thread.Sleep(5000);
    db.SaveChanges();
    transaction.Commit();
}

// 好:事务范围小
using (var transaction = db.BeginTransaction())
{
    db.SaveChanges();
    transaction.Commit();
}

// 长时间操作在事务外
DoLongOperation();

6.2 避免死锁

按照固定顺序访问资源,避免死锁:

// 不好:顺序不一致
// 事务1: 先锁A,再锁B
// 事务2: 先锁B,再锁A

// 好:统一顺序
// 两个事务都先锁A,再锁B

6.3 锁等待优化

-- 设置锁等待超时
SET innodb_lock_wait_timeout = 50;

-- 查看锁等待
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS;

七、事务实践案例

7.1 转账事务

BEGIN TRANSACTION;

DECLARE @fromBalance DECIMAL(10,2);
DECLARE @toBalance DECIMAL(10,2);

SELECT @fromBalance = balance FROM accounts WHERE id = 1;
IF @fromBalance < 100
BEGIN
    ROLLBACK;
    RETURN;
END;

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

COMMIT;

7.2 分布式事务

// 使用分布式事务协调器
var transaction = new TransactionScope();

try
{
    // 操作数据库1
    db1.SaveChanges();
    
    // 操作数据库2
    db2.SaveChanges();
    
    // 操作消息队列
    messageQueue.Send();
    
    transaction.Complete();
}
finally
{
    transaction.Dispose();
}

八、总结

ACID事务是数据库可靠性的基础,理解事务的四个特性和实现机制,能够构建稳定可靠的数据密集型应用。合理设置隔离级别、优化事务大小、避免死锁,能够提升系统的并发性能和可靠性。