MySQL事务-隔离级别
事务的并发问题
- 脏读:事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据
- 不可重复读:事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果不一致。
- 幻读:事务A首先根据条件索引得到N条数据,然后事务B改变了这N条数据之外的M条或者增添了M条符合事务A搜索条件的数据,导致事务A再次搜索发现有N+M条数据了,就产生了幻读。例如系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样。
不可重复读侧重于修改,幻读侧重于新增或删除。解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表 。
SQL标准定义的四个隔离级别
- 未提交读(read-uncommitted):最低的隔离级别,事务中的修改,即使没有提交,其他事务也可以看得到,会导致“脏读”、“幻读”和“不可重复读取”。
- 提交读(read-committed):大多数主流数据库的默认事务等级,保证了一个事务不会读到另一个并行事务已修改但未提交的数据,避免了“脏读”,但不能避免“幻读”和“不可重复读取”。该级别适用于大多数系统。
- 可重复读(repeatable-read):保证了一个事务不会修改已经由另一个事务读取但未提交(回滚)的数据。避免了“脏读”和“不可重复读”的情况,但不能避免“幻读”,但是带来了更多的性能损失。
- 串行化(serializable):最为严格的隔离级别,所有的事务操作都必须依次顺序执行,可以避免其他隔离级别遇到的所有问题,是最为安全的隔离级别, 但同时也是性能最差的隔离级别,因为所有的事务在该隔离级别下都需要依次顺序执行,所以,并发度下降,吞吐量上不去,性能自然就下来了。 因为该隔离级别极大的影响系统性能,所以,很少场景会使用它。通常情况下,我们会使用其他隔离级别加上相应的并发锁的机制来控制对数据的访问,这样既保证了系统性能不会损失太大,也能够一定程度上保证数据的一致性。
事务隔离级别 | 脏读 | 不可重复读 | 幻读 |
未提交读(read-uncommitted) | 是 | 是 | 是 |
提交读(read-committed) | 否 | 是 | 是 |
可重复读(repeatable-read) | 否 | 否 | 是 |
串行化(serializable) | 否 | 否 | 否 |
InnoDB存储引擎默认支持的隔离级别是可重复读(repeatable-read),但与标准SQL不同的是,InnoDB存储引擎使用Next-Key Lock锁的算法避免幻读的产生。
Record lock
单条索引记录上加锁,record lock锁住的永远是索引,而非记录本身,即使该表上没有任何索引,那么innodb会在后台创建一个隐藏的聚集主键索引,那么锁住的就是这个隐藏的聚集主键索引。所以说当一条sql没有走任何索引时,那么将会在每一条聚集索引后面加X锁,这个类似于表锁,但原理上和表锁应该是完全不同的。
Gap lock
在索引记录之间的间隙中加锁,或者是在某一条索引记录之前或者之后加锁,并不包括该索引记录本身。
所谓Next-Key Locks,就是Record lock和Gap lock的结合,即除了锁住记录本身,还要再锁住索引之间的间隙。
查看隔离级别
SELECT @@tx_isolation
设置隔离级别
SET [GLOBAL | SESSION] TRANSACTION ISOLATION LEVEL
{
READ UNCOMMITTED
| READ COMMITTED
| REPEATABLE READ
| SERIALIZABLE
}
GLOBAL将设置全局默认的隔离级别。
SESSION将设置本次会话(可以理解为你连接到mysql服务器的这个窗口,别人也会开窗口但是你影响不到别人的窗口中去)的所有事务的隔离级别并在当前这个事务中起效。
如果GLOBAL | SESSION都不用,就是设置本次命令及以后的隔离级别,但是如果下个事务没有使用set设置的话就会调用默认的隔离级别。
还没有评论,来说两句吧...