数据库事务隔离级别
数据库事务的隔离级别有4个,由低到高依次为Read uncommitted、Read committed、Repeatable read、Serializable
这四个级别可以逐个解决脏读、不可重复读、幻读这几类问题。
READ UNCOMMITTED (未提交读):
事务中的修改,即使没提交,对其他事务也是可见的。
事务可以读取未提交的数据,也叫脏读,一般很少使用。
READ COMITTED (提交读):
一个事务开始时,只能看见已经提交的修改,并且所做的修改对其他事务不可见。
这个级别有时候也叫不可重复读,因为两次执行同样的查询,可能会得到不同的结果。
事务A前后执行两次查询,前一次读取某条记录之后,事务B对其进行了修改并提交,这时当A再次读取该数据的时候就会发现与之前读取的结果不一样。
是大多数数据库默认的隔离级别(但MySQL不是)。
REPEATABLE READ (可重复读):
保证了同一个事务多次读取同样记录的结果是一致的。但无法解决幻读。
幻读:事务A读取某个范围内的记录时,事务B又在该范围内插入新的记录,当事务A再次读取时,会产生幻行。
另一种幻读情况:事务A对数据库所有行做了修改时,事务B对向数据库中插入了一行新的数据。这时A发现还有没有修改的记录,就像发生幻觉一样。
InnoDB存储引擎通过间隙锁策略防止幻读:间隙锁使得InnoDB不仅仅锁定查询涉及的行,还会对索引中的间隙进行锁定,防止幻影行的插入。
这是MySQL (InnoDB引擎)的默认事务隔离级别。
SERIALIZABLE (可串行化):
强制事务串行执行,在读取每行数据上都加锁,可能产生大量的超时和锁争用问题。
只有在非常需要确保数据的一致性,且可以接受没有并发的情况下才考虑使用该级别。
脏读(Drity Read):脏读是指在一个事务处理过程里读取了另一个未提交的事务中的数据。当一个事务正在多次修改某个数据,而在这个事务中这多次的修改都还未提交,这时一个并发的事务来访问该数据,就会造成两个事务得到的数据不一致。(事务没有提交)
不可重复读(Non-repeatable read):在对于数据库中的某个数据,一个事务范围内多次查询却返回了不同的数据值,这是由于在查询间隔,被另一个事务修改并提交了。
例如事务T1在读取某一数据,而事务T2立马修改了这个数据并且提交事务给数据库,事务T1再次读取该数据就得到了不同的结果,发生了不可重复读。
不可重复读和脏读的区别是,脏读是某一事务读取了另一个事务未提交的脏数据,而不可重复读则是读取了前一事务提交的数据。
在某些情况下,不可重复读并不是问题,比如我们多次查询某个数据当然以最后查询得到的结果为主。
确保事务可以多次从一个字段中读取相同的值,在这个事务持续期间,禁止其他事务对这个字段进行更新,可以避免脏读和不可重复读.
但是可以插入新行,比如第一次读只有5行,第二次读出现了8行,以为出现了幻觉。
幻读(Phantom Read):幻读是事务非独立执行时发生的一种现象。例如事务T1对一个表中所有的行的某个数据项做了从“1”修改为“2”的操作,这时事务T2又对这个表中插入了一行数据项,而这个数据项的数值还是为“1”并且提交给数据库。而操作事务T1的用户如果再查看刚刚修改的数据,会发现还有一行没有修改,其实这行是从事务T2中添加的,就好像产生幻觉一样,这就是发生了幻读。
幻读和不可重复读都是读取了另一条已经提交的事务(这点就脏读不同),所不同的是不可重复读查询的都是同一个数据项,而幻读针对的是一批数据整体(比如数据的个数)。
隔离级别实现的原理:
读未提交:
事务在读数据时并不加锁,
事务在写数据时加行级共享锁。
读已提交:
事务对当前被读取的数据加行级共享锁(当读到时才加锁),一旦读完,立即释放该行级共享锁;
事务在更新某数据的瞬间,必须先加行级排他锁,直到事务结束才释放。
可重复读:
事务在读取某数据瞬间(开始读取的瞬间),必须先对其加行级共享锁,直到事务结束才释放;
事务在更新某数据瞬间(发生更新的瞬间),必须先对其加行级排他锁,直到事务结束才释放。
在innodb中加了间隙锁可以解决幻读问题。
序列化:
事务在读取数据时,必须先对其加表级共享锁,直到事务结束才释放;
事务在更新数据时,必须先对其加表级排他锁,直到事务结束才释放。