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