Organized 50 interview questions into 12 categories: - 01-分布式系统 (9 files): 分布式事务, 分布式锁, 一致性哈希, CAP理论, etc. - 02-数据库 (2 files): MySQL索引优化, MyBatis核心原理 - 03-缓存 (5 files): Redis数据结构, 缓存问题, LRU算法, etc. - 04-消息队列 (1 file): RocketMQ/Kafka - 05-并发编程 (4 files): 线程池, 设计模式, 限流策略, etc. - 06-JVM (1 file): JVM和垃圾回收 - 07-系统设计 (8 files): 秒杀系统, 短链接, IM, Feed流, etc. - 08-算法与数据结构 (4 files): B+树, 红黑树, 跳表, 时间轮 - 09-网络与安全 (3 files): TCP/IP, 加密安全, 性能优化 - 10-中间件 (4 files): Spring Boot, Nacos, Dubbo, Nginx - 11-运维 (4 files): Kubernetes, CI/CD, Docker, 可观测性 - 12-面试技巧 (1 file): 面试技巧和职业规划 All files renamed to Chinese for better accessibility and organized into categorized folders for easier navigation. Generated with [Claude Code](https://claude.com/claude-code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>
1514 lines
43 KiB
Markdown
1514 lines
43 KiB
Markdown
# 数据库事务隔离级别面试指南
|
||
|
||
## 1. ACID 特性
|
||
|
||
### ACID 四大特性
|
||
|
||
**1. 原子性(Atomicity)**
|
||
- 事务是一个不可分割的工作单位
|
||
- 事务中的操作要么全部成功,要么全部失败
|
||
- 需要事务日志(Undo Log)实现
|
||
|
||
**2. 一致性(Consistency)**
|
||
- 事务必须使数据库从一个一致性状态变到另一个一致性状态
|
||
- 数据库的完整性约束不被破坏
|
||
- 需要业务逻辑和数据库约束保证
|
||
|
||
**3. 隔离性(Isolation)**
|
||
- 多个并发事务之间相互隔离
|
||
- 一个事务的执行不能被其他事务干扰
|
||
- 通过锁机制和MVCC实现
|
||
|
||
**4. 持久性(Durability)**
|
||
- 事务一旦提交,其对数据库的修改就是永久性的
|
||
- 即使系统发生故障,修改也不会丢失
|
||
- 通过Redo Log实现
|
||
|
||
### ACID 实现原理
|
||
|
||
```java
|
||
// ACID 在数据库中的实现示例
|
||
public class TransactionACIDExample {
|
||
|
||
// 1. 原子性实现
|
||
public void atomicTransfer(String fromAccount, String toAccount, BigDecimal amount) {
|
||
try {
|
||
// 开启事务
|
||
beginTransaction();
|
||
|
||
// 执行转账操作
|
||
deductAmount(fromAccount, amount);
|
||
addAmount(toAccount, amount);
|
||
|
||
// 提交事务
|
||
commitTransaction();
|
||
|
||
} catch (Exception e) {
|
||
// 回滚事务
|
||
rollbackTransaction();
|
||
throw new TransactionException("Transfer failed", e);
|
||
}
|
||
}
|
||
|
||
// 2. 一致性实现
|
||
public void consistentUpdate() {
|
||
// 事务前检查
|
||
if (!checkBusinessRule()) {
|
||
throw new BusinessRuleException("Business rule violated");
|
||
}
|
||
|
||
try {
|
||
beginTransaction();
|
||
|
||
// 执行更新
|
||
updateData();
|
||
|
||
// 事务后检查
|
||
if (!checkBusinessRule()) {
|
||
rollbackTransaction();
|
||
throw new BusinessRuleException("Business rule violated");
|
||
}
|
||
|
||
commitTransaction();
|
||
} catch (Exception e) {
|
||
rollbackTransaction();
|
||
throw e;
|
||
}
|
||
}
|
||
|
||
// 3. 隔离性实现
|
||
public void isolatedOperation() {
|
||
// 设置隔离级别
|
||
setTransactionIsolation(Isolation.READ_COMMITTED);
|
||
|
||
try {
|
||
beginTransaction();
|
||
|
||
// 执行操作
|
||
performOperation();
|
||
|
||
commitTransaction();
|
||
} catch (Exception e) {
|
||
rollbackTransaction();
|
||
throw e;
|
||
}
|
||
}
|
||
|
||
// 4. 持久性实现
|
||
public void durableOperation() {
|
||
try {
|
||
beginTransaction();
|
||
|
||
// 执行操作
|
||
performOperation();
|
||
|
||
// 提交到内存
|
||
commitTransaction();
|
||
|
||
// 刷入磁盘
|
||
forceWriteToDisk();
|
||
|
||
} catch (Exception e) {
|
||
rollbackTransaction();
|
||
throw e;
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### 事务日志实现
|
||
|
||
**Undo Log(回滚日志)**
|
||
```sql
|
||
-- Undo Log 结构
|
||
CREATE TABLE undo_log (
|
||
id BIGINT AUTO_INCREMENT,
|
||
branch_id VARCHAR(64) NOT NULL,
|
||
xid VARCHAR(100) NOT NULL,
|
||
rollback_info LONGBLOB NOT NULL,
|
||
log_status INT NOT NULL,
|
||
create_time DATETIME NOT NULL,
|
||
update_time DATETIME NOT NULL,
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY ux_undo_log_branch_id (xid, branch_id)
|
||
);
|
||
|
||
-- Undo Log 示例
|
||
INSERT INTO undo_log VALUES (
|
||
1,
|
||
'branch_id_001',
|
||
'xid_123456',
|
||
'{"before": {"name": "Alice", "age": 25}, "after": {"name": "Alice", "age": 26}}',
|
||
1,
|
||
NOW(),
|
||
NOW()
|
||
);
|
||
```
|
||
|
||
**Redo Log(重做日志)**
|
||
```sql
|
||
-- Redo Log 结构
|
||
CREATE TABLE redo_log (
|
||
id BIGINT AUTO_INCREMENT,
|
||
xid VARCHAR(100) NOT NULL,
|
||
branch_id VARCHAR(64) NOT NULL,
|
||
log_data LONGBLOB NOT NULL,
|
||
log_status INT NOT NULL,
|
||
create_time DATETIME NOT NULL,
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY ux_redo_log_branch_id (xid, branch_id)
|
||
);
|
||
|
||
-- Redo Log 示例
|
||
INSERT INTO redo_log VALUES (
|
||
1,
|
||
'xid_123456',
|
||
'branch_id_001',
|
||
'{"sql": "UPDATE user SET age = 26 WHERE id = 1", "before": "25", "after": "26"}',
|
||
1,
|
||
NOW()
|
||
);
|
||
```
|
||
|
||
## 2. 四种隔离级别
|
||
|
||
### 读未提交(Read Uncommitted)
|
||
|
||
**特点**:
|
||
- 最低的隔离级别
|
||
- 可能读到未提交的数据(脏读)
|
||
- 性能最好,一致性最差
|
||
|
||
**问题演示**:
|
||
```sql
|
||
-- 会话1(事务A)
|
||
BEGIN;
|
||
UPDATE user SET balance = balance - 100 WHERE id = 1; -- 余额:900
|
||
|
||
-- 会话2(事务B)
|
||
BEGIN;
|
||
SELECT balance FROM user WHERE id = 1; -- 读取到900(脏读)
|
||
-- 事务A未提交,事务B读取到未提交的数据
|
||
|
||
-- 会话1(事务A)
|
||
ROLLBACK; -- 回滚,余额恢复为1000
|
||
|
||
-- 会话2(事务B)
|
||
SELECT balance FROM user WHERE id = 1; -- 余额:1000(数据不一致)
|
||
```
|
||
|
||
**MySQL 配置**:
|
||
```sql
|
||
-- 设置隔离级别
|
||
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
|
||
|
||
-- 查看当前隔离级别
|
||
SELECT @@transaction_isolation;
|
||
|
||
-- 全局设置
|
||
SET GLOBAL transaction_isolation = 'READ-UNCOMMITTED';
|
||
```
|
||
|
||
### 读已提交(Read Committed)
|
||
|
||
**特点**:
|
||
- 只能读取到已提交的数据
|
||
- 不会出现脏读
|
||
- 可能出现不可重复读
|
||
|
||
**问题演示**:
|
||
```sql
|
||
-- 会话1(事务A)
|
||
BEGIN;
|
||
SELECT balance FROM user WHERE id = 1; -- 余额:1000
|
||
|
||
-- 会话2(事务B)
|
||
BEGIN;
|
||
UPDATE user SET balance = balance - 100 WHERE id = 1; -- 余额:900
|
||
COMMIT; -- 提交
|
||
|
||
-- 会话1(事务A)
|
||
SELECT balance FROM user WHERE id = 1; -- 余额:900(不可重复读)
|
||
-- 同一事务中两次读取结果不同
|
||
```
|
||
|
||
**MySQL 配置**:
|
||
```sql
|
||
-- 设置隔离级别
|
||
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
|
||
|
||
-- Oracle、SQL Server 默认隔离级别
|
||
```
|
||
|
||
### 可重复读(Repeatable Read)
|
||
|
||
**特点**:
|
||
- 同一事务中多次读取数据结果一致
|
||
- 不会出现脏读和不可重复读
|
||
- 可能出现幻读
|
||
|
||
**问题演示**:
|
||
```sql
|
||
-- 会话1(事务A)
|
||
BEGIN;
|
||
SELECT * FROM order WHERE user_id = 1 AND status = 'pending'; -- 0条记录
|
||
|
||
-- 会话2(事务B)
|
||
BEGIN;
|
||
INSERT INTO order (user_id, amount, status) VALUES (1, 100, 'pending');
|
||
COMMIT; -- 提交
|
||
|
||
-- 会话1(事务A)
|
||
SELECT * FROM order WHERE user_id = 1 AND status = 'pending'; -- 1条记录(幻读)
|
||
-- 同一事务中两次查询结果不同
|
||
```
|
||
|
||
**MySQL 配置**:
|
||
```sql
|
||
-- 设置隔离级别
|
||
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
|
||
|
||
-- MySQL 默认隔离级别
|
||
```
|
||
|
||
### 串行化(Serializable)
|
||
|
||
**特点**:
|
||
- 最高隔离级别
|
||
- 事务串行执行
|
||
- 完全隔离,性能最差
|
||
- 不会出现任何问题
|
||
|
||
**演示**:
|
||
```sql
|
||
-- 设置隔离级别
|
||
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
|
||
|
||
-- 事务串行执行
|
||
-- 性能最差,但一致性最好
|
||
```
|
||
|
||
### 隔离级别对比表
|
||
|
||
| 隔离级别 | 脏读 | 不可重复读 | 幻读 | 并发性能 | 适用场景 |
|
||
|---------|------|-----------|------|---------|---------|
|
||
| 读未提交 | 可能 | 可能 | 可能 | 最高 | 不重要数据 |
|
||
| 读已提交 | 不可能 | 可能 | 可能 | 高 | 普通业务 |
|
||
| 可重复读 | 不可能 | 不可能 | 可能 | 中 | 重要业务 |
|
||
| 串行化 | 不可能 | 不可能 | 不可能 | 低 | 极端重要 |
|
||
|
||
## 3. MVCC 原理
|
||
|
||
### MVCC 基本概念
|
||
|
||
**MVCC(Multi-Version Concurrency Control)**:
|
||
- 多版本并发控制
|
||
- 通过数据版本号实现并发控制
|
||
- 读写不阻塞
|
||
|
||
**核心组件**:
|
||
- **隐藏字段**:`DB_TRX_ID`(事务ID)、`DB_ROLL_PTR`(回滚指针)
|
||
- **版本链**:通过回滚指针连接历史版本
|
||
- **Read View**:读视图,用于确定可见性
|
||
|
||
### MVCC 实现原理
|
||
|
||
**1. 数据行结构**
|
||
```sql
|
||
-- InnoDB 行结构示意图
|
||
┌─────────┬─────────────────┬─────────────────┬─────────────────┐
|
||
| Row Header | Transaction ID | Rollback Pointer | Column Data │
|
||
└─────────┴─────────────────┴─────────────────┴─────────────────┘
|
||
```
|
||
|
||
**2. 版本链构建**
|
||
```sql
|
||
-- 版本链构建过程
|
||
-- 初始状态
|
||
UPDATE user SET name = 'Alice' WHERE id = 1; -- TRX_ID = 100
|
||
┌─────────┬─────────────────┬─────────────────┬─────────────────┐
|
||
| Row Header | TRX_ID: 100 | NULL | name: 'Alice' │
|
||
└─────────┴─────────────────┴─────────────────┴─────────────────┘
|
||
|
||
-- 更新
|
||
UPDATE user SET name = 'Alice Smith' WHERE id = 1; -- TRX_ID = 200
|
||
┌─────────┬─────────────────┬─────────────────┬─────────────────┐
|
||
| Row Header | TRX_ID: 200 | PTR → TRX_ID:100 | name: 'Alice Smith'│
|
||
└─────────┴─────────────────┴─────────────────┴─────────────────┘
|
||
```
|
||
|
||
**3. Read View 机制**
|
||
```java
|
||
// Read View 实现
|
||
public class ReadView {
|
||
private long creatorTrxId; // 创建ReadView的事务ID
|
||
private long[] trxIds; // 活跃事务ID列表
|
||
private long upLimitId; // 最小活跃事务ID
|
||
private long lowLimitId; // 最大活跃事务ID + 1
|
||
private long creatorTrxId; // 创建ReadView的事务ID
|
||
|
||
public boolean isVisible(long trxId, long rollPointer) {
|
||
// 1. 如果当前事务在创建ReadView之后,不可见
|
||
if (trxId >= lowLimitId) {
|
||
return false;
|
||
}
|
||
|
||
// 2. 如果事务在活跃列表中,不可见
|
||
if (isInActiveTrxIds(trxId)) {
|
||
return false;
|
||
}
|
||
|
||
// 3. 如果事务在创建ReadView之前且已提交,可见
|
||
return trxId < upLimitId;
|
||
}
|
||
}
|
||
```
|
||
|
||
### MVCC 工作流程
|
||
|
||
**1. 读操作流程**
|
||
```java
|
||
// MVCC 读操作
|
||
public class MVCCReader {
|
||
public Object read(Object row, long trxId) {
|
||
// 1. 创建ReadView
|
||
ReadView readView = createReadView(trxId);
|
||
|
||
// 2. 从最新版本开始遍历
|
||
while (row != null) {
|
||
long rowTrxId = getRowTrxId(row);
|
||
|
||
// 3. 判断可见性
|
||
if (readView.isVisible(rowTrxId, getRowRollPointer(row))) {
|
||
return row;
|
||
}
|
||
|
||
// 4. 沿着回滚指针查找历史版本
|
||
row = getHistoricalVersion(row);
|
||
}
|
||
|
||
// 5. 未找到合适版本,返回null
|
||
return null;
|
||
}
|
||
}
|
||
```
|
||
|
||
**2. 写操作流程**
|
||
```java
|
||
// MVCC 写操作
|
||
public class MVCCWriter {
|
||
public void update(Object row, Object newValue, long trxId) {
|
||
// 1. 创建新版本
|
||
Object newRow = createNewVersion(row, newValue, trxId);
|
||
|
||
// 2. 更新版本链
|
||
Object current = getLatestVersion(row);
|
||
setRowRollPointer(current, newRow);
|
||
|
||
// 3. 更新数据行
|
||
updateRowData(row, newValue);
|
||
}
|
||
}
|
||
```
|
||
|
||
### MySQL MVCC 实现细节
|
||
|
||
**1. 隐藏字段**
|
||
```sql
|
||
-- 查看隐藏字段
|
||
SHOW CREATE TABLE user;
|
||
-- 输出包含:
|
||
-- `DB_TRX_ID` 6 byte
|
||
-- `DB_ROLL_PTR` 7 byte
|
||
-- `DB_ROW_ID` 6 byte
|
||
|
||
-- 隐藏字段含义:
|
||
-- DB_TRX_ID: 最近修改该行的事务ID
|
||
-- DB_ROLL_PTR: 回滚指针,指向该行的历史版本
|
||
-- DB_ROW_ID: 行ID,6字节
|
||
```
|
||
|
||
**2. Undo Log 结构**
|
||
```sql
|
||
-- Undo Log 示例
|
||
CREATE TABLE undo_log (
|
||
id BIGINT AUTO_INCREMENT,
|
||
branch_id VARCHAR(64) NOT NULL,
|
||
xid VARCHAR(100) NOT NULL,
|
||
rollback_info LONGBLOB NOT NULL,
|
||
log_status INT NOT NULL,
|
||
create_time DATETIME NOT NULL,
|
||
update_time DATETIME NOT NULL,
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY ux_undo_log_branch_id (xid, branch_id)
|
||
);
|
||
|
||
-- INSERT 操作的 Undo Log
|
||
-- 记录插入前的数据(实际上是空,因为插入前没有数据)
|
||
-- UPDATE 操作的 Undo Log
|
||
-- 记录更新前的数据
|
||
-- DELETE 操作的 Undo Log
|
||
-- 记录被删除的数据
|
||
```
|
||
|
||
**3. MVCC 演示**
|
||
```sql
|
||
-- 创建测试表
|
||
CREATE TABLE user (
|
||
id INT PRIMARY KEY,
|
||
name VARCHAR(50),
|
||
age INT,
|
||
INDEX idx_name (name)
|
||
);
|
||
|
||
-- 插入初始数据
|
||
INSERT INTO user VALUES (1, 'Alice', 25);
|
||
|
||
-- 事务A:可重复读
|
||
START TRANSACTION;
|
||
SELECT * FROM user WHERE id = 1; -- 结果:(1, 'Alice', 25)
|
||
|
||
-- 事务B:更新数据
|
||
START TRANSACTION;
|
||
UPDATE user SET name = 'Alice Smith' WHERE id = 1;
|
||
COMMIT;
|
||
|
||
-- 事务A:再次查询
|
||
SELECT * FROM user WHERE id = 1; -- 结果:(1, 'Alice', 25) -- 可重复读
|
||
-- 事务A仍然读取到旧版本
|
||
|
||
-- 事务A:提交后再次查询
|
||
COMMIT;
|
||
SELECT * FROM user WHERE id = 1; -- 结果:(1, 'Alice Smith', 25) -- 读取到最新版本
|
||
```
|
||
|
||
## 4. 脏读、幻读、不可重复读
|
||
|
||
### 脏读(Dirty Read)
|
||
|
||
**定义**:读取到未提交的事务数据
|
||
|
||
**问题场景**:
|
||
```java
|
||
// 脏读问题演示
|
||
public class DirtyReadExample {
|
||
public void dirtyReadDemo() {
|
||
// 线程1:事务A
|
||
new Thread(() -> {
|
||
Connection conn1 = getDataSource().getConnection();
|
||
try {
|
||
conn1.setAutoCommit(false);
|
||
|
||
// 执行更新
|
||
conn1.createStatement().executeUpdate(
|
||
"UPDATE account SET balance = balance - 100 WHERE id = 1");
|
||
|
||
// 模拟长时间处理
|
||
Thread.sleep(5000);
|
||
|
||
// 回滚
|
||
conn1.rollback();
|
||
|
||
} catch (Exception e) {
|
||
try {
|
||
conn1.rollback();
|
||
} catch (SQLException ex) {
|
||
ex.printStackTrace();
|
||
}
|
||
}
|
||
}).start();
|
||
|
||
// 线程2:事务B
|
||
new Thread(() -> {
|
||
try {
|
||
Thread.sleep(1000); // 等待事务A开始
|
||
|
||
Connection conn2 = getDataSource().getConnection();
|
||
conn2.setAutoCommit(false);
|
||
|
||
// 读取未提交的数据
|
||
ResultSet rs = conn2.createStatement().executeQuery(
|
||
"SELECT balance FROM account WHERE id = 1");
|
||
if (rs.next()) {
|
||
double balance = rs.getDouble("balance");
|
||
System.out.println("读取到余额: " + balance); // 900(脏读)
|
||
}
|
||
|
||
conn2.commit();
|
||
|
||
} catch (Exception e) {
|
||
e.printStackTrace();
|
||
}
|
||
}).start();
|
||
}
|
||
}
|
||
```
|
||
|
||
**解决方案**:
|
||
- 使用 READ COMMITTED 或更高隔离级别
|
||
- 在数据库层面设置隔离级别
|
||
|
||
### 不可重复读(Non-repeatable Read)
|
||
|
||
**定义**:同一事务中多次读取同一数据,结果不同
|
||
|
||
**问题场景**:
|
||
```java
|
||
// 不可重复读问题演示
|
||
public class NonRepeatableReadExample {
|
||
public void nonRepeatableReadDemo() {
|
||
// 线程1:事务A
|
||
new Thread(() -> {
|
||
Connection conn1 = getDataSource().getConnection();
|
||
try {
|
||
conn1.setAutoCommit(false);
|
||
|
||
// 第一次读取
|
||
ResultSet rs1 = conn1.createStatement().executeQuery(
|
||
"SELECT balance FROM account WHERE id = 1");
|
||
if (rs1.next()) {
|
||
double balance1 = rs1.getDouble("balance");
|
||
System.out.println("第一次读取余额: " + balance1); // 1000
|
||
}
|
||
|
||
// 模拟长时间处理
|
||
Thread.sleep(5000);
|
||
|
||
// 第二次读取
|
||
ResultSet rs2 = conn1.createStatement().executeQuery(
|
||
"SELECT balance FROM account WHERE id = 1");
|
||
if (rs2.next()) {
|
||
double balance2 = rs2.getDouble("balance");
|
||
System.out.println("第二次读取余额: " + balance2); // 900(不可重复读)
|
||
}
|
||
|
||
conn1.commit();
|
||
|
||
} catch (Exception e) {
|
||
try {
|
||
conn1.rollback();
|
||
} catch (SQLException ex) {
|
||
ex.printStackTrace();
|
||
}
|
||
}
|
||
}).start();
|
||
|
||
// 线程2:事务B
|
||
new Thread(() -> {
|
||
try {
|
||
Thread.sleep(2000); // 等待事务A第一次读取
|
||
|
||
Connection conn2 = getDataSource().getConnection();
|
||
conn2.setAutoCommit(false);
|
||
|
||
// 更新数据
|
||
conn2.createStatement().executeUpdate(
|
||
"UPDATE account SET balance = balance - 100 WHERE id = 1");
|
||
|
||
conn2.commit();
|
||
|
||
} catch (Exception e) {
|
||
e.printStackTrace();
|
||
}
|
||
}).start();
|
||
}
|
||
}
|
||
```
|
||
|
||
**解决方案**:
|
||
- 使用 REPEATABLE READ 或 SERIALIZABLE 隔离级别
|
||
- 使用 MVCC 机制
|
||
|
||
### 幻读(Phantom Read)
|
||
|
||
**定义**:同一事务中多次查询,返回的行数不同
|
||
|
||
**问题场景**:
|
||
```java
|
||
// 幻读问题演示
|
||
public class PhantomReadExample {
|
||
public void phantomReadDemo() {
|
||
// 线程1:事务A
|
||
new Thread(() -> {
|
||
Connection conn1 = getDataSource().getConnection();
|
||
try {
|
||
conn1.setAutoCommit(false);
|
||
|
||
// 第一次查询
|
||
ResultSet rs1 = conn1.createStatement().executeQuery(
|
||
"SELECT COUNT(*) FROM account WHERE balance > 500");
|
||
if (rs1.next()) {
|
||
int count1 = rs1.getInt(1);
|
||
System.out.println("第一次查询结果: " + count1); // 1
|
||
}
|
||
|
||
// 模拟长时间处理
|
||
Thread.sleep(5000);
|
||
|
||
// 第二次查询
|
||
ResultSet rs2 = conn1.createStatement().executeQuery(
|
||
"SELECT COUNT(*) FROM account WHERE balance > 500");
|
||
if (rs2.next()) {
|
||
int count2 = rs2.getInt(1);
|
||
System.out.println("第二次查询结果: " + count2); // 2(幻读)
|
||
}
|
||
|
||
conn1.commit();
|
||
|
||
} catch (Exception e) {
|
||
try {
|
||
conn1.rollback();
|
||
} catch (SQLException ex) {
|
||
ex.printStackTrace();
|
||
}
|
||
}
|
||
}).start();
|
||
|
||
// 线程2:事务B
|
||
new Thread(() -> {
|
||
try {
|
||
Thread.sleep(2000); // 等待事务A第一次查询
|
||
|
||
Connection conn2 = getDataSource().getConnection();
|
||
conn2.setAutoCommit(false);
|
||
|
||
// 插入新数据
|
||
conn2.createStatement().executeUpdate(
|
||
"INSERT INTO account (id, balance) VALUES (2, 600)");
|
||
|
||
conn2.commit();
|
||
|
||
} catch (Exception e) {
|
||
e.printStackTrace();
|
||
}
|
||
}).start();
|
||
}
|
||
}
|
||
```
|
||
|
||
**解决方案**:
|
||
- 使用 SERIALIZABLE 隔离级别
|
||
- 使用间隙锁(Gap Lock)
|
||
|
||
### 问题解决策略
|
||
|
||
**1. 代码层面解决**
|
||
```java
|
||
// 使用乐观锁解决并发问题
|
||
public class OptimisticLockExample {
|
||
public void updateWithOptimisticLock() {
|
||
try {
|
||
Connection conn = getDataSource().getConnection();
|
||
conn.setAutoCommit(false);
|
||
|
||
// 1. 读取数据
|
||
ResultSet rs = conn.createStatement().executeQuery(
|
||
"SELECT * FROM user WHERE id = 1");
|
||
if (rs.next()) {
|
||
User user = new User();
|
||
user.setId(rs.getLong("id"));
|
||
user.setName(rs.getString("name"));
|
||
user.setVersion(rs.getInt("version"));
|
||
}
|
||
|
||
// 2. 模拟业务处理
|
||
Thread.sleep(1000);
|
||
|
||
// 3. 更新数据(包含版本号检查)
|
||
int affected = conn.createStatement().executeUpdate(
|
||
"UPDATE user SET name = ?, version = version + 1 " +
|
||
"WHERE id = ? AND version = ?",
|
||
new Object[]{"New Name", 1L, user.getVersion()});
|
||
|
||
if (affected == 0) {
|
||
throw new OptimisticLockException("数据已被其他事务修改");
|
||
}
|
||
|
||
conn.commit();
|
||
|
||
} catch (Exception e) {
|
||
throw new RuntimeException("更新失败", e);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 使用悲观锁解决并发问题
|
||
public class PessimisticLockExample {
|
||
public void updateWithPessimisticLock() {
|
||
try {
|
||
Connection conn = getDataSource().getConnection();
|
||
conn.setAutoCommit(false);
|
||
|
||
// 1. 加锁
|
||
ResultSet rs = conn.createStatement().executeQuery(
|
||
"SELECT * FROM user WHERE id = 1 FOR UPDATE");
|
||
|
||
// 2. 更新数据
|
||
conn.createStatement().executeUpdate(
|
||
"UPDATE user SET name = 'New Name' WHERE id = 1");
|
||
|
||
conn.commit();
|
||
|
||
} catch (Exception e) {
|
||
try {
|
||
conn.rollback();
|
||
} catch (SQLException ex) {
|
||
ex.printStackTrace();
|
||
}
|
||
throw new RuntimeException("更新失败", e);
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
**2. 数据库层面解决**
|
||
```sql
|
||
-- 设置隔离级别解决并发问题
|
||
-- 1. 避免脏读
|
||
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
|
||
|
||
-- 2. 避免不可重复读
|
||
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
|
||
|
||
-- 3. 避免幻读
|
||
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
|
||
|
||
-- 使用锁机制
|
||
-- 1. 共享锁(读锁)
|
||
SELECT * FROM user WHERE id = 1 LOCK IN SHARE MODE;
|
||
|
||
-- 2. 排他锁(写锁)
|
||
SELECT * FROM user WHERE id = 1 FOR UPDATE;
|
||
```
|
||
|
||
## 5. 实际项目中的隔离级别选择
|
||
|
||
### 不同业务场景的隔离级别选择
|
||
|
||
**1. 电商系统**
|
||
```java
|
||
// 电商系统事务管理
|
||
@Configuration
|
||
@EnableTransactionManagement
|
||
public class EcommerceTransactionConfig {
|
||
|
||
@Bean
|
||
public PlatformTransactionManager transactionManager(DataSource dataSource) {
|
||
return new DataSourceTransactionManager(dataSource);
|
||
}
|
||
|
||
// 订单服务 - 使用可重复读
|
||
@Service
|
||
public class OrderService {
|
||
|
||
@Transactional(isolation = Isolation.REPEATABLE_READ)
|
||
public Order createOrder(OrderDTO orderDTO) {
|
||
// 1. 减库存
|
||
inventoryService.reduceStock(orderDTO.getItems());
|
||
|
||
// 2. 创建订单
|
||
Order order = new Order();
|
||
BeanUtils.copyProperties(orderDTO, order);
|
||
order.setStatus("PENDING");
|
||
order.setCreateTime(LocalDateTime.now());
|
||
|
||
orderRepository.save(order);
|
||
|
||
// 3. 预扣账户余额
|
||
paymentService.reserveAmount(order.getUserId(), order.getTotalAmount());
|
||
|
||
return order;
|
||
}
|
||
|
||
@Transactional(isolation = Isolation.READ_COMMITTED)
|
||
public void cancelOrder(Long orderId) {
|
||
// 读取已提交的数据
|
||
Order order = orderRepository.findById(orderId);
|
||
|
||
if ("PENDING".equals(order.getStatus())) {
|
||
// 取消订单
|
||
order.setStatus("CANCELLED");
|
||
order.setUpdateTime(LocalDateTime.now());
|
||
orderRepository.save(order);
|
||
|
||
// 释放库存和余额
|
||
inventoryService.releaseStock(order.getItems());
|
||
paymentService.releaseAmount(order.getUserId(), order.getTotalAmount());
|
||
}
|
||
}
|
||
}
|
||
|
||
// 支付服务 - 使用串行化
|
||
@Service
|
||
public class PaymentService {
|
||
|
||
@Transactional(isolation = Isolation.SERIALIZABLE)
|
||
public void processPayment(Long orderId) {
|
||
// 加锁确保数据一致性
|
||
Order order = orderRepository.findByIdWithLock(orderId);
|
||
|
||
// 防止重复支付
|
||
if ("PROCESSING".equals(order.getStatus())) {
|
||
throw new PaymentException("订单已在处理中");
|
||
}
|
||
|
||
// 处理支付
|
||
order.setStatus("PROCESSING");
|
||
order.setUpdateTime(LocalDateTime.now());
|
||
orderRepository.save(order);
|
||
|
||
// 调用支付网关
|
||
paymentGateway.process(order);
|
||
|
||
// 更新订单状态
|
||
order.setStatus("PAID");
|
||
order.setPayTime(LocalDateTime.now());
|
||
orderRepository.save(order);
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
**2. 金融系统**
|
||
```java
|
||
// 金融系统事务管理
|
||
@Service
|
||
public class FinancialTransactionService {
|
||
|
||
@Transactional(isolation = Isolation.SERIALIZABLE)
|
||
public void transferMoney(String fromAccount, String toAccount, BigDecimal amount) {
|
||
try {
|
||
// 1. 加锁账户
|
||
Account from = accountRepository.findByIdForLock(fromAccount);
|
||
Account to = accountRepository.findByIdForLock(toAccount);
|
||
|
||
// 2. 检查余额
|
||
if (from.getBalance().compareTo(amount) < 0) {
|
||
throw new InsufficientBalanceException("余额不足");
|
||
}
|
||
|
||
// 3. 执行转账
|
||
from.setBalance(from.getBalance().subtract(amount));
|
||
to.setBalance(to.getBalance().add(amount));
|
||
|
||
// 4. 记录交易
|
||
Transaction transaction = new Transaction();
|
||
transaction.setFromAccount(fromAccount);
|
||
transaction.setToAccount(toAccount);
|
||
transaction.setAmount(amount);
|
||
transaction.setStatus("COMPLETED");
|
||
transaction.setTime(LocalDateTime.now());
|
||
|
||
accountRepository.saveAll(Arrays.asList(from, to));
|
||
transactionRepository.save(transaction);
|
||
|
||
} catch (Exception e) {
|
||
throw new FinancialException("转账失败", e);
|
||
}
|
||
}
|
||
|
||
@Transactional(isolation = Isolation.READ_COMMITTED)
|
||
public BigDecimal getAccountBalance(String accountNumber) {
|
||
// 读取已提交的数据
|
||
Account account = accountRepository.findByAccountNumber(accountNumber);
|
||
return account.getBalance();
|
||
}
|
||
}
|
||
```
|
||
|
||
**3. 社交系统**
|
||
```java
|
||
// 社交系统事务管理
|
||
@Service
|
||
public class SocialService {
|
||
|
||
@Transactional(isolation = Isolation.REPEATABLE_READ)
|
||
public void createPost(PostDTO postDTO) {
|
||
// 1. 创建帖子
|
||
Post post = new Post();
|
||
BeanUtils.copyProperties(postDTO, post);
|
||
post.setStatus("PUBLISHED");
|
||
post.setCreateTime(LocalDateTime.now());
|
||
|
||
postRepository.save(post);
|
||
|
||
// 2. 更新用户统计
|
||
User user = userRepository.findById(post.getUserId());
|
||
user.setPostCount(user.getPostCount() + 1);
|
||
user.setLastPostTime(LocalDateTime.now());
|
||
|
||
userRepository.save(user);
|
||
}
|
||
|
||
@Transactional(isolation = Isolation.READ_COMMITTED)
|
||
public List<Post> getFeedByUserId(Long userId, int page, int size) {
|
||
// 读取已提交的数据
|
||
return postRepository.findByUserIdOrderByCreateTimeDesc(userId, page, size);
|
||
}
|
||
}
|
||
```
|
||
|
||
### 隔离级别调优策略
|
||
|
||
**1. 性能优化**
|
||
```java
|
||
// 隔离级别性能测试
|
||
@SpringBootTest
|
||
public class IsolationLevelPerformanceTest {
|
||
|
||
@Autowired
|
||
private JdbcTemplate jdbcTemplate;
|
||
|
||
@Test
|
||
public void testReadUncommittedPerformance() {
|
||
// 测试读未提交性能
|
||
long startTime = System.currentTimeMillis();
|
||
|
||
for (int i = 0; i < 10000; i++) {
|
||
jdbcTemplate.query("SELECT * FROM user WHERE id = 1", rs -> {
|
||
return rs.getString("name");
|
||
});
|
||
}
|
||
|
||
long endTime = System.currentTimeMillis();
|
||
System.out.println("READ UNCOMMITTED: " + (endTime - startTime) + "ms");
|
||
}
|
||
|
||
@Test
|
||
public void testSerializablePerformance() {
|
||
// 测试串行化性能
|
||
long startTime = System.currentTimeMillis();
|
||
|
||
for (int i = 0; i < 10000; i++) {
|
||
jdbcTemplate.query("SELECT * FROM user WHERE id = 1 FOR UPDATE", rs -> {
|
||
return rs.getString("name");
|
||
});
|
||
}
|
||
|
||
long endTime = System.currentTimeMillis();
|
||
System.out.println("SERIALIZABLE: " + (endTime - startTime) + "ms");
|
||
}
|
||
}
|
||
```
|
||
|
||
**2. 动态隔离级别切换**
|
||
```java
|
||
// 动态隔离级别切换
|
||
@Service
|
||
public class DynamicIsolationService {
|
||
|
||
@Autowired
|
||
private TransactionManager transactionManager;
|
||
|
||
public void executeWithIsolation(Runnable operation, Isolation isolation) {
|
||
TransactionTemplate template = new TransactionTemplate(transactionManager);
|
||
template.setIsolationLevel(isolation);
|
||
|
||
template.execute(status -> {
|
||
operation.run();
|
||
return null;
|
||
});
|
||
}
|
||
|
||
// 根据业务场景选择隔离级别
|
||
public void processOrder(Order order) {
|
||
if (order.getType() == OrderType.NORMAL) {
|
||
// 普通订单使用可重复读
|
||
executeWithIsolation(() -> {
|
||
processNormalOrder(order);
|
||
}, Isolation.REPEATABLE_READ);
|
||
} else if (order.getType() == OrderType.PREMIUM) {
|
||
// 高级订单使用串行化
|
||
executeWithIsolation(() -> {
|
||
processPremiumOrder(order);
|
||
}, Isolation.SERIALIZABLE);
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
**3. 事务监控和告警**
|
||
```java
|
||
// 事务监控
|
||
@Component
|
||
public class TransactionMonitor {
|
||
|
||
@Autowired
|
||
private TransactionManager transactionManager;
|
||
|
||
@Scheduled(fixedRate = 60000) // 每分钟监控一次
|
||
public void monitorTransactions() {
|
||
// 监控长事务
|
||
List<ActiveTransaction> activeTransactions = getActiveTransactions();
|
||
|
||
for (ActiveTransaction tx : activeTransactions) {
|
||
if (tx.getDuration() > 300000) { // 5分钟
|
||
monitorService.alert("长事务: " + tx.getSql() + ", 持续时间: " + tx.getDuration());
|
||
}
|
||
}
|
||
|
||
// 监控死锁
|
||
int deadlockCount = getDeadlockCount();
|
||
if (deadlockCount > 0) {
|
||
monitorService.alert("检测到死锁,数量: " + deadlockCount);
|
||
}
|
||
}
|
||
|
||
// 监控隔离级别使用情况
|
||
public Map<Isolation, Integer> getIsolationLevelUsage() {
|
||
Map<Isolation, Integer> result = new HashMap<>();
|
||
|
||
// 查询数据库获取隔离级别使用情况
|
||
String sql = "SELECT variable_name, variable_value " +
|
||
"FROM information_schema.global_status " +
|
||
"WHERE variable_name IN ('Com_select', 'Com_update', 'Com_delete')";
|
||
|
||
List<Map<String, Object>> stats = jdbcTemplate.queryForList(sql);
|
||
|
||
for (Map<String, Object> stat : stats) {
|
||
String name = (String) stat.get("variable_name");
|
||
String value = (String) stat.get("variable_value");
|
||
|
||
if ("Com_select".equals(name)) {
|
||
result.put(Isolation.READ_COMMITTED, Integer.parseInt(value));
|
||
} else if ("Com_update".equals(name)) {
|
||
result.put(Isolation.REPEATABLE_READ, Integer.parseInt(value));
|
||
} else if ("Com_delete".equals(name)) {
|
||
result.put(Isolation.SERIALIZABLE, Integer.parseInt(value));
|
||
}
|
||
}
|
||
|
||
return result;
|
||
}
|
||
}
|
||
```
|
||
|
||
### 最佳实践总结
|
||
|
||
**1. 隔离级别选择原则**
|
||
- **READ UNCOMMITTED**:极少使用,主要用于临时性、不重要的数据
|
||
- **READ COMMITTED**:通用场景,Oracle、PostgreSQL默认级别
|
||
- **REPEATABLE READ**:MySQL默认,大多数业务场景适用
|
||
- **SERIALIZABLE**:极端重要的金融场景,性能要求低
|
||
|
||
**2. 事务管理最佳实践**
|
||
```java
|
||
// 事务管理最佳实践
|
||
@Service
|
||
public class BestPracticeService {
|
||
|
||
// 1. 合理设置事务边界
|
||
@Transactional
|
||
public void processOrder(OrderDTO orderDTO) {
|
||
// 单个事务处理完整业务流程
|
||
Order order = createOrder(orderDTO);
|
||
processPayment(order);
|
||
updateInventory(order);
|
||
notifyUser(order);
|
||
}
|
||
|
||
// 2. 避免长事务
|
||
@Transactional
|
||
public void processOrderInParts(OrderDTO orderDTO) {
|
||
// 分步骤处理,避免长事务
|
||
Order order = createOrder(orderDTO);
|
||
|
||
// 立即提交事务
|
||
commitTransaction();
|
||
|
||
// 异步处理后续流程
|
||
asyncProcessPayment(order);
|
||
asyncUpdateInventory(order);
|
||
}
|
||
|
||
// 3. 正确处理异常
|
||
@Transactional(rollbackFor = {BusinessException.class, RuntimeException.class})
|
||
public void processOrderWithErrorHandling(OrderDTO orderDTO) {
|
||
try {
|
||
Order order = createOrder(orderDTO);
|
||
processPayment(order);
|
||
updateInventory(order);
|
||
} catch (BusinessException e) {
|
||
// 业务异常,记录日志但不回滚
|
||
log.error("业务处理失败", e);
|
||
throw e;
|
||
} catch (Exception e) {
|
||
// 系统异常,回滚
|
||
throw e;
|
||
}
|
||
}
|
||
|
||
// 4. 只读事务优化
|
||
@Transactional(readOnly = true)
|
||
public List<OrderDTO> getUserOrders(Long userId) {
|
||
// 只读操作,使用只读事务提高性能
|
||
return orderRepository.findByUserId(userId);
|
||
}
|
||
}
|
||
```
|
||
|
||
**3. 性能优化建议**
|
||
- **批量操作**:使用批量插入和更新减少事务数量
|
||
- **异步处理**:耗时操作异步化,避免阻塞主事务
|
||
- **缓存策略**:合理使用缓存减少数据库访问
|
||
- **连接池配置**:优化连接池大小避免连接耗尽
|
||
|
||
## 6. 阿里 P7 加分项
|
||
|
||
### 分布式事务管理
|
||
|
||
**1. Seata 分布式事务**
|
||
```java
|
||
// Seata 分布式事务配置
|
||
@Configuration
|
||
public class SeataConfig {
|
||
|
||
@Bean
|
||
public GlobalTransactionScanner globalTransactionScanner() {
|
||
return new GlobalTransactionScanner(
|
||
"demo-service",
|
||
"my_test_tx_group");
|
||
}
|
||
|
||
@Bean
|
||
public XADataSource xaDataSource(DataSource dataSource) {
|
||
// 配置XA数据源
|
||
return new AtomikosDataSourceBean();
|
||
}
|
||
}
|
||
|
||
// Seata 服务调用示例
|
||
@Service
|
||
public class OrderServiceWithSeata {
|
||
|
||
@Autowired
|
||
private OrderRepository orderRepository;
|
||
|
||
@Autowired
|
||
private InventoryService inventoryService;
|
||
|
||
@Autowired
|
||
private PaymentService paymentService;
|
||
|
||
@GlobalTransactional(timeoutMills = 300000)
|
||
public void createOrder(OrderDTO orderDTO) {
|
||
// 1. 创建订单
|
||
Order order = createOrder(orderDTO);
|
||
|
||
// 2. 减库存
|
||
inventoryService.reduceStock(orderDTO.getItems());
|
||
|
||
// 3. 扣除余额
|
||
paymentService.deductAmount(order.getUserId(), order.getTotalAmount());
|
||
}
|
||
}
|
||
```
|
||
|
||
**2. RocketMQ 分布式事务**
|
||
```java
|
||
// RocketMQ 分布式事务消息
|
||
@Service
|
||
public class RocketMQTransactionService {
|
||
|
||
@Autowired
|
||
private RocketMQTemplate rocketMQTemplate;
|
||
|
||
public void sendMessageWithTransaction(String topic, String message) {
|
||
// 1. 准备本地事务
|
||
Transaction transaction = prepareLocalTransaction();
|
||
|
||
// 2. 发送事务消息
|
||
TransactionSendResult result = rocketMQTemplate.sendMessageInTransaction(
|
||
topic,
|
||
MessageBuilder.withBody(message.getBytes()).build(),
|
||
transaction);
|
||
|
||
// 3. 处理结果
|
||
if (result.getSendStatus() != SendStatus.SEND_OK) {
|
||
throw new RuntimeException("发送事务消息失败");
|
||
}
|
||
}
|
||
|
||
@TransactionalEventListener
|
||
public void handleTransactionEvent(TransactionEvent event) {
|
||
// 处理事务事件
|
||
if (event.isSuccess()) {
|
||
commitTransaction(event.getTxId());
|
||
} else {
|
||
rollbackTransaction(event.getTxId());
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### 高级并发控制
|
||
|
||
**1. 乐观锁实现**
|
||
```java
|
||
// 乐观锁实现
|
||
@Component
|
||
public class OptimisticLockExecutor {
|
||
|
||
public <T> T executeWithOptimisticLock(Supplier<T> operation,
|
||
Supplier<T> fallback,
|
||
int maxRetries) {
|
||
int retryCount = 0;
|
||
RuntimeException lastException = null;
|
||
|
||
while (retryCount < maxRetries) {
|
||
try {
|
||
// 1. 执行操作
|
||
T result = operation.get();
|
||
|
||
// 2. 检查结果
|
||
if (result != null) {
|
||
return result;
|
||
}
|
||
|
||
} catch (OptimisticLockException e) {
|
||
lastException = e;
|
||
retryCount++;
|
||
|
||
// 指数退避
|
||
try {
|
||
Thread.sleep(100 * (1 << retryCount));
|
||
} catch (InterruptedException ie) {
|
||
Thread.currentThread().interrupt();
|
||
throw new RuntimeException("线程被中断", ie);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 重试次数用完,执行回退操作
|
||
return fallback.get();
|
||
}
|
||
}
|
||
|
||
// 使用示例
|
||
@Service
|
||
public class UserService {
|
||
|
||
@Autowired
|
||
private UserRepository userRepository;
|
||
|
||
@Autowired
|
||
private OptimisticLockExecutor optimisticLockExecutor;
|
||
|
||
public User updateUserWithOptimisticLock(Long userId, String newName) {
|
||
return optimisticLockExecutor.executeWithOptimisticLock(
|
||
() -> {
|
||
User user = userRepository.findById(userId);
|
||
if (user == null) {
|
||
return null;
|
||
}
|
||
|
||
user.setName(newName);
|
||
user.setVersion(user.getVersion() + 1);
|
||
|
||
userRepository.save(user);
|
||
return user;
|
||
},
|
||
() -> {
|
||
// 回退操作
|
||
User user = userRepository.findById(userId);
|
||
if (user != null) {
|
||
user.setName("Fallback_" + newName);
|
||
user.setVersion(user.getVersion() + 1);
|
||
userRepository.save(user);
|
||
}
|
||
return user;
|
||
},
|
||
3 // 最大重试次数
|
||
);
|
||
}
|
||
}
|
||
```
|
||
|
||
**2. 悲观锁优化**
|
||
```java
|
||
// 悲观锁优化
|
||
@Service
|
||
public class PessimisticLockService {
|
||
|
||
@Autowired
|
||
private JdbcTemplate jdbcTemplate;
|
||
|
||
// 使用间隙锁优化
|
||
public List<User> getUsersWithGapLock(String minAge, String maxAge) {
|
||
String sql = "SELECT * FROM user WHERE age BETWEEN ? AND ? FOR UPDATE";
|
||
return jdbcTemplate.query(sql, new Object[]{minAge, maxAge},
|
||
(rs, rowNum) -> {
|
||
User user = new User();
|
||
user.setId(rs.getLong("id"));
|
||
user.setName(rs.getString("name"));
|
||
user.setAge(rs.getInt("age"));
|
||
return user;
|
||
});
|
||
}
|
||
|
||
// 使用Next-Key Lock优化
|
||
public User getUserWithNextKeyLock(Long userId) {
|
||
String sql = "SELECT * FROM user WHERE id >= ? LIMIT 1 FOR UPDATE";
|
||
return jdbcTemplate.queryForObject(sql, new Object[]{userId},
|
||
(rs, rowNum) -> {
|
||
User user = new User();
|
||
user.setId(rs.getLong("id"));
|
||
user.setName(rs.getString("name"));
|
||
user.setAge(rs.getInt("age"));
|
||
return user;
|
||
});
|
||
}
|
||
}
|
||
```
|
||
|
||
### 性能监控和调优
|
||
|
||
**1. 事务性能监控**
|
||
```java
|
||
// 事务性能监控
|
||
@Component
|
||
public class TransactionPerformanceMonitor {
|
||
|
||
@Autowired
|
||
private MeterRegistry meterRegistry;
|
||
|
||
@Autowired
|
||
private TransactionManager transactionManager;
|
||
|
||
// 监控事务执行时间
|
||
@Around("execution(* com.example.service..*(..)) && @annotation(transactional)")
|
||
public Object monitorTransactionTime(ProceedingJoinPoint joinPoint, Transactional transactional) throws Throwable {
|
||
String methodName = joinPoint.getSignature().getName();
|
||
long startTime = System.currentTimeMillis();
|
||
|
||
try {
|
||
Object result = joinPoint.proceed();
|
||
|
||
// 记录成功事务
|
||
recordTransactionMetric(methodName, true, System.currentTimeMillis() - startTime);
|
||
|
||
return result;
|
||
} catch (Exception e) {
|
||
// 记录失败事务
|
||
recordTransactionMetric(methodName, false, System.currentTimeMillis() - startTime);
|
||
throw e;
|
||
}
|
||
}
|
||
|
||
private void recordTransactionMetric(String methodName, boolean success, long duration) {
|
||
// 记录事务指标
|
||
meterRegistry.counter("transaction.count",
|
||
"method", methodName,
|
||
"success", String.valueOf(success))
|
||
.increment();
|
||
|
||
meterRegistry.timer("transaction.duration",
|
||
"method", methodName,
|
||
"success", String.valueOf(success))
|
||
.record(duration, TimeUnit.MILLISECONDS);
|
||
}
|
||
|
||
// 监控隔离级别使用情况
|
||
@Scheduled(fixedRate = 60000)
|
||
public void monitorIsolationLevels() {
|
||
Map<Isolation, AtomicInteger> usage = new ConcurrentHashMap<>();
|
||
|
||
// 收集正在执行的事务隔离级别
|
||
collectActiveTransactionIsolationLevels(usage);
|
||
|
||
// 上报指标
|
||
usage.forEach((isolation, count) -> {
|
||
meterRegistry.gauge("transaction.isolation.count",
|
||
"level", isolation.name(),
|
||
count);
|
||
});
|
||
}
|
||
}
|
||
```
|
||
|
||
**2. 动态调优策略**
|
||
```java
|
||
// 动态调优策略
|
||
@Component
|
||
public class DynamicOptimizationStrategy {
|
||
|
||
@Autowired
|
||
private JdbcTemplate jdbcTemplate;
|
||
|
||
@Autowired
|
||
private TransactionTemplate transactionTemplate;
|
||
|
||
// 根据系统负载动态调整隔离级别
|
||
public void adjustIsolationLevelBasedOnLoad() {
|
||
SystemLoad load = getSystemLoad();
|
||
|
||
if (load.getCpuUsage() > 80) {
|
||
// 高负载时使用较低隔离级别
|
||
setDefaultIsolation(Isolation.READ_COMMITTED);
|
||
} else if (load.getMemoryUsage() > 70) {
|
||
// 中等负载使用可重复读
|
||
setDefaultIsolation(Isolation.REPEATABLE_READ);
|
||
} else {
|
||
// 低负载使用串行化
|
||
setDefaultIsolation(Isolation.SERIALIZABLE);
|
||
}
|
||
}
|
||
|
||
// 自动优化事务超时时间
|
||
public void optimizeTransactionTimeout() {
|
||
String sql = "SELECT AVG(duration) FROM transaction_metrics WHERE success = 1";
|
||
Double avgDuration = jdbcTemplate.queryForObject(sql, Double.class);
|
||
|
||
if (avgDuration != null) {
|
||
// 设置超时时间为平均执行时间的2倍
|
||
int timeout = (int) (avgDuration * 2);
|
||
setDefaultTransactionTimeout(timeout);
|
||
}
|
||
}
|
||
|
||
// 自适应重试策略
|
||
public <T> T executeWithAdaptiveRetry(Supplier<T> operation,
|
||
Class<? extends Exception> retryException,
|
||
int maxRetries) {
|
||
int retryCount = 0;
|
||
long baseDelay = 100;
|
||
|
||
while (retryCount < maxRetries) {
|
||
try {
|
||
return operation.get();
|
||
} catch (Exception e) {
|
||
if (retryException.isInstance(e)) {
|
||
retryCount++;
|
||
|
||
// 指数退避
|
||
long delay = baseDelay * (1 << retryCount);
|
||
try {
|
||
Thread.sleep(delay);
|
||
} catch (InterruptedException ie) {
|
||
Thread.currentThread().interrupt();
|
||
throw new RuntimeException("线程被中断", ie);
|
||
}
|
||
} else {
|
||
throw e;
|
||
}
|
||
}
|
||
}
|
||
|
||
throw new RetryExhaustedException("重试次数已用完");
|
||
}
|
||
}
|
||
```
|
||
|
||
### 总结
|
||
|
||
数据库事务隔离级别是并发控制的核心,需要根据业务场景选择合适的隔离级别:
|
||
|
||
1. **理解 ACID 特性**:原子性、一致性、隔离性、持久性
|
||
2. **掌握四种隔离级别**:读未提交、读已提交、可重复读、串行化
|
||
3. **理解 MVCC 机制**:多版本并发控制的基本原理
|
||
4. **识别并发问题**:脏读、不可重复读、幻读
|
||
5. **选择合适隔离级别**:根据业务需求和性能要求
|
||
6. **优化事务性能**:避免长事务、合理使用锁
|
||
7. **监控和调优**:持续监控事务性能,动态调整策略
|
||
|
||
在实际项目中,需要平衡数据一致性和系统性能,选择合适的隔离级别和并发控制策略。 |