ACID 事务

在用户认证系统中,数据一致性至关重要。ACID 是数据库事务的四个核心特性。

什么是 ACID?

ACID 事务特性

Atomicity(原子性)

要么全部完成,要么全部失败。

Consistency(一致性)

事务前后数据状态都合法。

Isolation(隔离性)

并发事务互不干扰。

Durability(持久性)

提交后永久保存。

A - Atomicity 原子性

场景:用户转账

设计流程
场景:用户转账
  1. 步骤 1:在事务边界内校验并提交状态变化
  2. 步骤 2:在事务边界内校验并提交状态变化
  3. 步骤 3:读取 API Key、调用方信息和请求上下文
  4. 步骤 4:按一致性要求、数据温度和失败情况选择处理路径
关注点:一致性、查询性能、归档边界和可回滚性。

原子性保证

事务执行过程:

BEGIN
  ├─ 操作 1:A 账户 -100  ✓
  ├─ 操作 2:B 账户 +100  ✓
  └─ COMMIT               ← 全部提交

如果任何一步失败:
BEGIN
  ├─ 操作 1:A 账户 -100  ✓
  ├─ 操作 2:B 账户 +100  ✗
  └─ ROLLBACK             ← 全部回滚,A 账户恢复原状

C - Consistency 一致性

一致性示例

事务前状态:
用户 A: balance = 500
用户 B: balance = 300
总计:800

事务:A 转账 100 给 B

事务后状态:
用户 A: balance = 400
用户 B: balance = 400
总计:800  ← 守恒!

违反一致性的情况

设计流程
违反一致性的情况
  1. 步骤 1:在事务边界内校验并提交状态变化
  2. 步骤 2:在事务边界内校验并提交状态变化
  3. 步骤 3:读取 API Key、调用方信息和请求上下文
  4. 步骤 4:按一致性要求、数据温度和失败情况选择处理路径
关注点:一致性、查询性能、归档边界和可回滚性。

I - Isolation 隔离性

并发问题

场景:A 账户有 500 元,同时发起两笔转账

时间线:
T1: 事务 1 读取 A 余额 = 500
T2: 事务 2 读取 A 余额 = 500   ← 脏读
T3: 事务 1 转账 100 给 B,A = 400
T4: 事务 2 转账 200 给 C,A = 300  ← 错误!应该是 100

结果:A 的余额计算错误

隔离级别

隔离级别脏读不可重复读幻读性能
READ UNCOMMITTED可能可能可能最高
READ COMMITTED避免可能可能
REPEATABLE READ避免避免可能
SERIALIZABLE避免避免避免最低

MySQL 中的使用

设计流程
MySQL 中的使用:数据模型
  1. 步骤 1:按业务维度聚合统计结果
  2. 步骤 2:读取 API Key、调用方信息和请求上下文
  3. 步骤 3:按一致性要求、数据温度和失败情况选择处理路径
  4. 步骤 4:写入数据变更结果,并保留校验和回滚依据
关注点:一致性、查询性能、归档边界和可回滚性。

D - Durability 持久性

持久化机制

MySQL InnoDB 持久化保证

Redo Log(重做日志)

  • 记录所有修改
  • 先写日志,再写数据
  • 崩溃后可恢复

Binlog(二进制日志)

  • 记录 SQL 语句
  • 用于主从复制
  • 用于时间点恢复

Checkpoint(检查点)

  • 定期刷盘
  • 加速恢复

配置建议

设计流程
配置建议:运行配置
  1. 步骤 1:配置日志、监控和告警输出
  2. 步骤 2:读取 API Key、调用方信息和请求上下文
  3. 步骤 3:按一致性要求、数据温度和失败情况选择处理路径
  4. 步骤 4:写入数据变更结果,并保留校验和回滚依据
关注点:一致性、查询性能、归档边界和可回滚性。

事务最佳实践

1. 保持事务简短

设计流程
1. 保持事务简短
  1. 步骤 1:在事务边界内校验并提交状态变化
  2. 步骤 2:在事务边界内校验并提交状态变化
  3. 步骤 3:读取 API Key、调用方信息和请求上下文
  4. 步骤 4:按一致性要求、数据温度和失败情况选择处理路径
关注点:一致性、查询性能、归档边界和可回滚性。

2. 避免死锁

设计流程
2. 避免死锁
  1. 步骤 1:读取 API Key、调用方信息和请求上下文
  2. 步骤 2:按一致性要求、数据温度和失败情况选择处理路径
  3. 步骤 3:写入数据变更结果,并保留校验和回滚依据
关注点:一致性、查询性能、归档边界和可回滚性。

3. 使用连接池

设计流程
3. 使用连接池
  1. 步骤 1:在事务边界内校验并提交状态变化
  2. 步骤 2:读取 API Key、调用方信息和请求上下文
  3. 步骤 3:按一致性要求、数据温度和失败情况选择处理路径
  4. 步骤 4:写入数据变更结果,并保留校验和回滚依据
关注点:一致性、查询性能、归档边界和可回滚性。