1. 什么是隔离级别
在数据库中,当多个事务同时进行时,需要对其进行隔离以避免相互之间的干扰。隔离级别定义了多个事务之间相互影响的程度,高隔离级别可以避免一些并发问题,但可能会带来性能的降低。一般来说,数据库支持的隔离级别有四种:未提交读(read uncommitted)、提交读(read committed)、可重复读(repeatable read)和串行化(serializable)。
2. MySQL 的事务隔离级别
MySQL 默认使用的事务隔离级别是可重复读(repeatable read),也支持提交读(read committed)和串行化(serializable)隔离级别。
2.1 可重复读隔离级别
可重复读隔离级别是 MySQL 默认的隔离级别,该级别下,事务可以多次读取同一行数据而不会被其他事务修改。
为了实现可重复读隔离级别,MySQL 使用了 MVCC(多版本并发控制)的机制。MVCC 记录了每行数据在不同时间点的版本,事务在读取数据时,会读取在其事务开启前已经提交的数据行的一个快照,因此当其他事务修改数据时,不会影响当前事务的读取结果。
-- 查看当前隔离级别
SELECT @@tx_isolation;
-- 设置隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL 可选隔离级别;
2.2 提交读隔离级别
提交读隔离级别下,事务在读取数据时,会读取已经提交的数据行,因此可以避免脏读,但因为读取的是当前已提交的最新数据,所以可能会出现不可重复读的问题。
在提交读隔离级别下,事务之间还会出现幻读的现象。幻读指的是同一个事务在不同时间点中读取的相同范围的数据行数量不同的现象,如在两个查询语句之间有另外一个事务插入了一些符合查询条件的数据,则第二次查询中将会出现更多的数据。
-- 设置隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
2.3 串行化隔离级别
串行化隔离级别是最高的隔离级别,在该级别下,事务之间是完全隔离的,也就是说每个事务都必须等待其他事务完成才能进行下一步操作,因此可以避免所有并发问题,但会带来较大的性能开销。
-- 设置隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
3. MVCC 的具体实现
MVCC 的实现机制是为每一行数据在表中维护一个版本链表,每当有事务对数据进行修改时,会为该数据生成一个新版本,并将该版本添加到版本链表中。当读取数据时,会在版本链表中查找该数据对应的版本号,如果该版本号在事务的隔离级别内,则读取该版本的数据。
3.1 记录版本号
在 MVCC 机制下,每个数据行都要记录一个版本号,该版本号随着数据的更新而递增,用于标识该版本的创建时间。MySQL 中记录版本号的方式是在 InnoDB 存储引擎中为每个数据行都添加两个隐藏字段:trx_id 和 roll_pointer。其中,trx_id 表示该数据行最后一次变化的事务编号,roll_pointer 表示该数据行的回滚指针,用于回滚数据行中某个版本的数据。
3.2 查找数据
在读取数据时,MySQL 使用快照来读取已提交的数据行的一个版本。该版本是在该事务启动的时间点确定的,并且在事务的生命周期中保持不变,直到该事务结束。在读取数据时,MySQL 首先会查找该数据行中 commit_id 小于等于当前事务 ID 的最新版本,如果找到则返回该版本的数据,如果找不到则说明该数据已被删除。
3.3 处理并发冲突
在 MVCC 机制下,可以处理一些并发冲突的问题,如可重复读隔离级别下,如果事务 A 在读取数据行时,数据行被事务 B 修改了,则事务 A 读取到的依然是原来的快照,而不会受到事务 B 的干扰。
如果两个事务同时修改同一行数据,则会使用锁来保证并发冲突的问题。行锁是在存储引擎层实现的,可以通过以下命令查看当前锁状态:
SHOW ENGINE INNODB STATUS;
4. 总结
MySQL 实现了多种不同的事务隔离级别,可以根据应用场景来选择合适的隔离级别。
MVCC 机制可以实现读取数据时的快照读,避免读取过程中其他事务的干扰,同时使用锁来协调并发操作,保证多个事务同时修改同一行数据的正确性。