dirty read(脏读):一个事务B中修改了一些数据但没有提交,而事务A却读取该数据并在其基础上进行操作,如果事务B进行回滚,那么事务A读到的数据是无效的,不符合数据一致性。
unrepeatable read(不可重复读):是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。事务A在执行过程中读到了另一个事务B中已经修改并提交的数据,这不符合隔离性。例如:事务A开始时,读取事务B中数据a为10,在A结束时,又读一次a,发现变为20。
phantom read(幻读): 第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。事务A读取到了事务B新增的数据,也是不符合隔离性的。
四种不同的事务隔离级别:
RU(READ UNCOMMITTED),读未提交:一个事务可以去读取其他事务未提交的变更,这种隔离级别允许脏读发生。在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少。
RC(READ COMMITTED),读已提交:一个事务可以去读取其他事务提交的变更,这种隔离级别允许不可重复读、幻读的发生。(ORACAL默认的隔离级别) 这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。因为同一事务的其他实例在该实例处理其间可能会有新的commit,所以同一select可能返回不同结果
RR(REPEATABLE READ),可重复读:一个事务中,直到事务结束前,都可以反复读取事务一开始看到的数据,该数据是不会有变化的,这种隔离级别保证了事务的一致性。(Mysql的默认隔离级别) 这是MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。不过理论上,这会导致另一个棘手的问题:幻读 (Phantom Read)。简单的说,幻读指当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行。InnoDB和Falcon存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)机制解决了该问题。所以mysql不存在幻读,mysql实现和定义的RR级别和标准的不一样。
串行化读(SERIALIZABLE):每次读都需要获得表级的共享锁,每次写都要为表加上排他锁,两个会话间的读写都会相互堵塞,该隔离级别会导致InnoDB表并发性的丧失,与MyISAM类似。这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。
隔离级别 | 脏读(Dirty Read) | 不可重复读(NonRepeatable Read) | 幻读(Phantom Read) |
---|---|---|---|
未提交读(Read uncommitted) | 可能 | 可能 | 可能 |
已提交读(Read committed) | 不可能 | 可能 | 可能 |
可重复读(Repeatable read) | 不可能 | 不可能 | 可能 |
可串行化(Serializable ) | 不可能 | 不可能 | 不可能 |
InnoDB行锁
InnoDB的锁有三种:
Record lock:行记录锁,该锁是基于InnoDB索引实现的,也称索引行锁。例:两条记录a和b,这两条记录上都有一个Record lock。
Gap lock:间隙锁,锁定一定的范围,但不含记录的本身。记录a和b,a前有一个Gap lock,a与b之前有一个Gap lock,b之后也有一个Gap lock。
Next-key lock:锁定一个放单位和本身,即Record lock+Gap lock,Next-key lock解决了幻读的问题。
基于主键的行锁是只有record lock的,因为主键的行是唯一的,不会有冲突的,如果在有锁的情况下修改改记录是会直接冲突,不需要范围锁定,即没有gap lock。
基于辅助索引的行锁,除了record lock外,可能也要有gap lock,即next-key lock。唯一的辅助索引只有record lock,而非唯一索引中的辅助索引是有next-key lock的。
如果会话是基于非索引的条件修改数据(删除与更新,注意没有插入)时,在RR级别下,有可能会导致所有的行记录都被锁住,相当于全表锁。
InnoDB死锁:
死锁只存在并发的情况,串行是不会发生死锁的。会话A等待会话B释放资源,而会话B也在等待A的释放,此时就会发生死锁,即只要构成等待回路,则均认为会发生死锁。
发生死锁的相关信息,可以通过“show engine innodb status\G”发现死锁的相关信息