2025-06-09 java面试总结
问题:mysql的事务隔离级别和各自解决的问题?
事务的特性:原子性、一致性、隔离性、持久性
mysql的隔离级别分为
RU(read uncommitted)读未提交。
所有事务能看到其他事务未提交的数据,也称之为脏读(Dirty Read)。
RC (read committed) 读已提交。
满足事务的特性ACID,读到的是事务已经提交的数据。但是不可重复读:事务执行过程中,前后两次查询可能因为别的事务提交相同数据修改而结果不一致。也有MVCC,不过生成Read View的时机是每次查询时生成新的。
RR(repeat read) 可重复读
mysql默认的事务隔离级别,通过MVCC和锁机制实现可重复读。使用间隙锁和行锁解决幻读问题。只有在事务第一次进行select时生成read view视图。
Serializable 串行化
隔离级别最高的事务,每个事务排序执行,没有脏读、幻读、不可重复读的问题。
MVCC:多版本链并发控制,通过可见性规则和版本链实现事务的差异化访问。
行记录结构:
字段 | 含义 |
---|---|
DB_TRX_ID | 当前行的事务id |
DB_ROLL_PTR | 回滚指针,指向undo日志段 |
Read View:读视图
字段 | 含义 |
---|---|
m_ids | 活跃事务id列表 |
min_trx_id | 活跃事务中最小事务id |
max_trx_id | 系统下一个事务id,相当于max(m_ids)+1 |
creator_trx_id | 当前事务id |
Read View创建时机:
read committed:读提交事务隔离级别下,是在每次select时创建;
repeat read:可重复读事务隔离级别下,是在第一次select时创建。
注意:repeat read 模式下执行update流程如下:
1.不会使用快照读(Read view),而是读取最新的主键索引所在行的记录;
2.若存在行锁或间隙锁,需等待锁释放
3.加排他锁(X锁)阻止其他事务修改或读取该行,加间隙锁(Gap Lock)防止其他事务在索引间隙插入新数据(解决幻读)
4. 生成新版本:修改数据后,生成新的Undo Log记录旧版本,更新聚簇索引的DB_TRX_ID为当前事务ID
事务的状态:已提交、已回滚、运行中
MVCC工作流程:
- 事务A启动,指定事务A的id=108;(如果是只读事务,不分配id,id=0);
- 事务A执行select * from user where id = 3;
- 事务A创建Read view,如下:
字段 | 值 |
---|---|
m_ids | [90,104,110] |
min_trx_id | 90 |
max_trx_id | 111 |
creator_trx_id | 108 |
事务可见性:
行记录事务id | 是否可见 | 不可见原因 |
---|---|---|
110 | 不可见 | 事务id在m_ids活跃事务id数组中 |
108 | 可见 | 事务id为当前事务 |
111 | 不可见 | 事务id为max_trx_id,读视图创建后产生的事务 |
89 | 可见 | 事务id不在活跃事务数组中,并且小于最小活跃事务,说明是当前事务创建前已提交的事务 |
100 | 可见 | 事务id不在活跃事务数组中,并且小于最大活跃事务,说明是当前事务创建前已提交的事务 |
- 事务A执行update user set name= ‘newname‘ where id = 3;
此时在id=3的行记录中生成版本链(版本链中包含未提交的事务)。 - 事务提交
不可重复读和幻读
不可重复读:事务中的前后两次查询因为其他事务的修改导致查询结果不一致。使用MVCC解决。
幻读:事务中前后两次查询因为其他事务插入数据而导致查询结果不一致。使用行锁和间隙锁解决。
当前读和快照读
快照读在事务第一次查询的时候生成read view,后面每次查询结果一致。
当前读:读的是主键索引上已提交最新事务id。
innodb锁的实现
行锁基于索引实现。
主键索引上加锁,会作用于主键索引记录节点。
普通索引上加锁,先在普通索引上加锁,再去主键索引上加锁。
- 当使用主键索引或者唯一索引,并且条件精确匹配,只会加行锁,不会加间隙锁。
- 当使用普通索引时,会加行锁和间隙锁。
- 当没有命中索引时,加表锁。
sql调优技巧 - 优先使用主键索引,减少回表操作,降低锁竞争
- 确保命中索引,避免全表扫描