分布式事务 #
一、事务概述 #
1.1 ACID保证 #
text
ACID 特性
┌─────────────────────────────────────────────────────────────┐
│ │
│ Atomicity (原子性) │
│ └── 事务要么全部成功,要么全部失败 │
│ │
│ Consistency (一致性) │
│ └── 事务前后数据保持一致状态 │
│ │
│ Isolation (隔离性) │
│ └── 并发事务互不干扰 │
│ │
│ Durability (持久性) │
│ └── 事务提交后数据永久保存 │
│ │
└─────────────────────────────────────────────────────────────┘
1.2 分布式事务挑战 #
text
分布式事务挑战
┌─────────────────────────────────────────────────────────────┐
│ │
│ 网络分区: │
│ ├── 节点间通信可能失败 │
│ └── 需要容错机制 │
│ │
│ 节点故障: │
│ ├── 节点可能随时宕机 │
│ └── 需要故障恢复机制 │
│ │
│ 数据分布: │
│ ├── 数据分布在多个节点 │
│ └── 需要协调多个节点 │
│ │
│ 性能问题: │
│ ├── 跨节点事务延迟高 │
│ └── 需要优化事务处理 │
│ │
└─────────────────────────────────────────────────────────────┘
二、事务模型 #
2.1 事务生命周期 #
sql
-- 基本事务
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT;
-- 带参数的事务
BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE, PRIORITY HIGH;
-- SQL语句
COMMIT;
-- 只读事务
BEGIN TRANSACTION READ ONLY;
SELECT * FROM accounts;
COMMIT;
2.2 事务状态 #
text
事务状态机
┌─────────────────────────────────────────────────────────────┐
│ │
│ ┌─────────┐ │
│ │ Pending │ 事务开始 │
│ └────┬────┘ │
│ │ │
│ ▼ │
│ ┌─────────┐ │
│ │ Staging │ 准备提交 │
│ └────┬────┘ │
│ │ │
│ ├─────────────────────┐ │
│ ▼ ▼ │
│ ┌─────────┐ ┌─────────┐ │
│ │Committed│ │ Aborted │ │
│ └─────────┘ └─────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
三、两阶段提交 #
3.1 2PC流程 #
text
两阶段提交流程
┌─────────────────────────────────────────────────────────────┐
│ │
│ Phase 1: Prepare │
│ │
│ Coordinator │
│ │ │
│ ├──► Range 1: Prepare │
│ │ │ │
│ │ └── 锁定资源 │
│ │ └── 返回 Ready │
│ │ │
│ ├──► Range 2: Prepare │
│ │ │ │
│ │ └── 锁定资源 │
│ │ └── 返回 Ready │
│ │ │
│ └──► Range 3: Prepare │
│ │ │
│ └── 锁定资源 │
│ └── 返回 Ready │
│ │
│ Phase 2: Commit │
│ │
│ Coordinator (所有参与者 Ready) │
│ │ │
│ ├──► Range 1: Commit │
│ ├──► Range 2: Commit │
│ └──► Range 3: Commit │
│ │
│ 返回成功 │
│ │
└─────────────────────────────────────────────────────────────┘
3.2 事务记录 #
sql
-- 查看事务记录
SELECT * FROM crdb_internal.cluster_transactions;
-- 查看特定事务
SELECT * FROM crdb_internal.cluster_transactions
WHERE id = 'transaction-id';
-- 事务字段说明
SELECT
id, -- 事务ID
status, -- 状态
start, -- 开始时间
num_retries, -- 重试次数
num_statements, -- 语句数量
last_error, -- 最后错误
priority -- 优先级
FROM crdb_internal.cluster_transactions;
四、隔离级别 #
4.1 支持的隔离级别 #
sql
-- READ COMMITTED (默认)
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
-- SERIALIZABLE
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
-- 查看当前隔离级别
SHOW TRANSACTION ISOLATION LEVEL;
4.2 隔离级别对比 #
text
隔离级别对比
┌─────────────────────────────────────────────────────────────┐
│ │
│ 问题 │ READ COMMITTED │ SERIALIZABLE │
│ ──────────────────────────────────────────────────────── │
│ 脏读 │ ✗ │ ✗ │
│ 不可重复读 │ ✓ │ ✗ │
│ 幻读 │ ✓ │ ✗ │
│ 写偏斜 │ ✓ │ ✗ │
│ │
│ 性能 │ 较高 │ 较低 │
│ 一致性 │ 较弱 │ 最强 │
│ │
└─────────────────────────────────────────────────────────────┘
4.3 隔离级别示例 #
sql
-- READ COMMITTED 示例
-- 会话1
BEGIN ISOLATION LEVEL READ COMMITTED;
SELECT balance FROM accounts WHERE id = 1; -- 1000
-- 会话2
BEGIN;
UPDATE accounts SET balance = 500 WHERE id = 1;
COMMIT;
-- 会话1
SELECT balance FROM accounts WHERE id = 1; -- 500 (不可重复读)
COMMIT;
-- SERIALIZABLE 示例
-- 会话1
BEGIN ISOLATION LEVEL SERIALIZABLE;
SELECT balance FROM accounts WHERE id = 1; -- 1000
-- 会话2
BEGIN;
UPDATE accounts SET balance = 500 WHERE id = 1;
COMMIT;
-- 会话1
SELECT balance FROM accounts WHERE id = 1; -- 1000 (可重复读)
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
-- 可能触发重试
COMMIT;
五、并发控制 #
5.1 乐观并发控制 #
text
乐观并发控制
┌─────────────────────────────────────────────────────────────┐
│ │
│ 阶段: │
│ │
│ 1. 读取阶段 │
│ ├── 读取数据到本地 │
│ └── 记录读取的时间戳 │
│ │
│ 2. 验证阶段 │
│ ├── 检查是否有冲突 │
│ └── 冲突则重试 │
│ │
│ 3. 写入阶段 │
│ ├── 提交事务 │
│ └── 写入新版本数据 │
│ │
│ 优点: │
│ ├── 无锁开销 │
│ ├── 适合读多写少 │
│ └── 高并发性能好 │
│ │
│ 缺点: │
│ ├── 冲突时需要重试 │
│ └── 写多读少性能差 │
│ │
└─────────────────────────────────────────────────────────────┘
5.2 冲突检测 #
sql
-- 冲突检测示例
-- 两个事务同时更新同一行
-- 事务1
BEGIN;
SELECT balance FROM accounts WHERE id = 1; -- 1000
-- 事务2
BEGIN;
SELECT balance FROM accounts WHERE id = 1; -- 1000
UPDATE accounts SET balance = 900 WHERE id = 1;
COMMIT; -- 成功
-- 事务1
UPDATE accounts SET balance = 800 WHERE id = 1;
COMMIT; -- 可能失败,需要重试
-- Error: restart transaction
5.3 重试机制 #
sql
-- 自动重试示例
DO $$
BEGIN
DECLARE
retry_count INT DEFAULT 0;
BEGIN
WHILE retry_count < 3 LOOP
BEGIN
-- 事务逻辑
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT;
EXIT; -- 成功退出
EXCEPTION WHEN serialization_failure THEN
retry_count := retry_count + 1;
ROLLBACK;
-- 可以添加延迟
-- PERFORM pg_sleep(0.1);
END;
END LOOP;
IF retry_count >= 3 THEN
RAISE EXCEPTION 'Transaction failed after 3 retries';
END IF;
END;
END;
$$;
六、事务优先级 #
6.1 设置优先级 #
sql
-- 高优先级
SET TRANSACTION PRIORITY HIGH;
-- 低优先级
SET TRANSACTION PRIORITY LOW;
-- 正常优先级 (默认)
SET TRANSACTION PRIORITY NORMAL;
-- 在事务开始时设置
BEGIN TRANSACTION PRIORITY HIGH;
-- SQL语句
COMMIT;
6.2 优先级影响 #
text
事务优先级影响
┌─────────────────────────────────────────────────────────────┐
│ │
│ HIGH: │
│ ├── 优先执行 │
│ ├── 较少重试 │
│ └── 可能导致其他事务重试 │
│ │
│ NORMAL: │
│ ├── 默认优先级 │
│ └── 公平竞争 │
│ │
│ LOW: │
│ ├── 最后执行 │
│ ├── 更多重试 │
│ └── 适合后台任务 │
│ │
│ 使用场景: │
│ ├── HIGH: 重要业务事务 │
│ ├── NORMAL: 普通事务 │
│ └── LOW: 后台批处理任务 │
│ │
└─────────────────────────────────────────────────────────────┘
七、分布式事务最佳实践 #
7.1 设计原则 #
text
事务设计原则
┌─────────────────────────────────────────────────────────────┐
│ │
│ 1. 保持事务简短 │
│ ├── 减少事务持有时间 │
│ └── 降低冲突概率 │
│ │
│ 2. 避免热点 │
│ ├── 分散写入 │
│ └── 使用UUID代替自增ID │
│ │
│ 3. 合理设置隔离级别 │
│ ├── 默认READ COMMITTED │
│ └── 需要强一致性用SERIALIZABLE │
│ │
│ 4. 实现重试逻辑 │
│ ├── 捕获serialization_failure │
│ └── 设置合理重试次数 │
│ │
│ 5. 使用合适的优先级 │
│ ├── 重要事务HIGH │
│ └── 后台任务LOW │
│ │
└─────────────────────────────────────────────────────────────┘
7.2 事务模式 #
sql
-- 模式1: 简单事务
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT;
-- 模式2: 带重试的事务
DO $$
BEGIN
DECLARE
retry INT DEFAULT 0;
BEGIN
WHILE retry < 3 LOOP
BEGIN
-- 事务逻辑
COMMIT;
EXIT;
EXCEPTION WHEN serialization_failure THEN
retry := retry + 1;
ROLLBACK;
END;
END LOOP;
END;
END;
$$;
-- 模式3: 只读事务
BEGIN TRANSACTION READ ONLY;
SELECT * FROM accounts;
COMMIT;
-- 模式4: 时间旅行查询
SELECT * FROM accounts
AS OF SYSTEM TIME '-10s';
八、事务监控 #
8.1 监控指标 #
sql
-- 查看活跃事务
SELECT * FROM crdb_internal.cluster_transactions
WHERE status = 'pending';
-- 查看事务统计
SELECT
database,
COUNT(*) as transaction_count,
AVG(num_statements) as avg_statements,
AVG(num_retries) as avg_retries
FROM crdb_internal.cluster_transactions
GROUP BY database;
-- 查看长时间运行的事务
SELECT
id,
start,
NOW() - start AS duration,
num_statements
FROM crdb_internal.cluster_transactions
WHERE status = 'pending'
ORDER BY duration DESC;
8.2 事务问题排查 #
sql
-- 查看重试次数多的事务
SELECT
id,
num_retries,
last_error
FROM crdb_internal.cluster_transactions
WHERE num_retries > 0
ORDER BY num_retries DESC;
-- 查看锁等待
SELECT * FROM crdb_internal.cluster_locks;
-- 查看阻塞的事务
SELECT
blocked.id AS blocked_txn,
blocking.id AS blocking_txn,
blocked.num_retries
FROM crdb_internal.cluster_transactions blocked
JOIN crdb_internal.cluster_transactions blocking
ON blocked.id != blocking.id
WHERE blocked.num_retries > 0;
九、总结 #
分布式事务要点:
| 特性 | 说明 |
|---|---|
| ACID | 完整的事务保证 |
| 两阶段提交 | 分布式事务协调 |
| 隔离级别 | READ COMMITTED, SERIALIZABLE |
| 并发控制 | 乐观并发控制 |
| 重试机制 | 自动重试 |
下一步,让我们学习数据复制!
最后更新:2026-03-27