Mysql 事务隔离级别
一、数据库事务正确的四个要素(ACID)[1]
原子性(Atomicity):即事务是不可分割的最小工作单元,事务内的操作要么全做,要么全不做,不能只做一部分;
一致性(Consistency):在事务执行前数据库的数据处于正确的状态,而事务执行完成后数据库的数据还是处于正确的状态,即数据完整性约束没有被破坏;比如我们做银行转账的相关业务,A转账给B,要求A转的钱B一定要收到。如果A转了钱而B没有收到,那
么数据库数据的一致性就得不到保障,在做高并发业务时要注意合理的设计。
隔离性(Isolation):并发事务执行之间无影响,在一个事务内部的操作对其他事务是不产生影响,这需要事务隔离级别来指定隔离性;
持久性(Durability):事务一旦执行成功,它对数据库的数据的改变必须是永久的,不会因各种异常导致数据不一致或丢失。
二、并行事务的四个问题
先理解锁的概念:https://www.cnblogs.com/Jomini/p/13219722.html
2.1、丢失更新
别的事务读到相同的东西,各自写,自己的写被覆盖了。
两个事务同时更新一行数据,最后一个事务的更新会覆盖掉第一个事务的更新,从而导致第一个事务更新的数据丢失,后果比较严重。一般是由于没加锁的原因造成的。
解决方式 :使用数据库锁机制
(详细: https://www.cnblogs.com/doucheyard/p/5662171.html )
2.2、脏读(Dirty reads)
读到别的事务未提交的数据
一个事务A读取到了另一个事务B还没有提交的数据,并在此基础上进行操作。如果B事务rollback,那么A事务所读取到的数据就是不正确的,会带来问题。
2.3、不可重复读(Non-repeatable reads)
两次读之间有别的事务修改
在同一事务范围内读取两次相同的数据,所返回的结果不同。比如事务B第一次读数据后,事务A更新数据并commit,那么事务B第二次读取的数据就与第一次是不一样的。
2.4、 幻读(Phantom reads)
两次读之间有别的事务增删
一个事务A读取到了另一个事务B新提交的数据。比如,事务A对一个表中所有行的数据按照某规则进行修改(整表操作),同时,事务B向表中插入了一行原始数据,那么后面事务A再对表进行操作时,会发现表中居然还有一行数据没有被修改。
2.5、注意:不可重复读和幻读的区别是,不可重复读对应的表的操作是更改(UPDATE),而幻读对应的表的操作是插入(INSERT),两种的应对策略不一样。对于不可重复读,只需要采用行级锁防止该记录被更新即可,而对于幻读必须加个表级锁,防止在表中插入数据。有关锁的问题,下面会讨论。
三、解决方式
SQL定义了下面的4个等级的事务隔离级别:
3.1、未提交读(READ UNCOMMITTED )
最低隔离级别,一个事务能读取到别的事务未提交的更新数据,很不安全,可能出现丢失更新、脏读、不可重复读、幻读;
原理:
(1)事务对当前被读取的数据不加锁;
事务1读取某行记录时,事务2也能对这行记录进行读取、更新;当事务2对该记录进行更新时,事务1再次读取该记录,能读到事务2对该记录的修改版本,即使该修改尚未被提交。
(2)事务在更新某数据的瞬间(就是发生更新的瞬间),必须先对其加 行级共享锁,直到事务结束才释放。
事务1更新某行记录时,事务2不能对这行记录做更新,直到事务1结束。
3.2、提交读(READ COMMITTED)
一个事务能读取到别的事务提交的更新数据,不能看到未提交的更新数据,不会出现 丢失更新、脏读,但可能出现不可重复读、幻读;
原理:
(1)事务对当前被读取的数据加行级共享锁(当读到时才加锁),一旦读完该行,立即释放该行级共享锁;
【注意是:一旦读完该行,立即释放该共享锁(注意是读完立即释放),所以第二次读的数据将会不同,即导致 “不可重复读” 的问题】
事务1读取某行记录时,事务2也能对这行记录进行读取、更新;当事务2对该记录进行更新时,事务1再次读取该记录,读到的只能是事务2对其更新前的版本,要不就是事务2提交后的版本。
(2)事务在更新某数据的瞬间(就是发生更新的瞬间),必须先对其加行级排他锁,直到事务结束才释放。
事务1更新某行记录时,事务2不能对这行记录做更新,直到事务1结束。
3.3、可重复读(REPEATABLE READ)
保证同一事务中先后执行的多次查询将返回同一结果,不受其他事务影响,不可能出现丢失更新、脏读、不可重复读,但可能出现幻读;
原理:
(1) 事务在读取某数据的瞬间(就是开始读取的瞬间),必须先对其加 行级共享锁,直到事务结束才释放;
事务1读取某行记录时,事务2能对该表读取,但不能修改,事务1再次读取该记录,读到的仍然是第一次读取的那个版本。
【注意:和 “提交读” 的读操作不同的是,这里读加的共享锁 是整个事务都加了,而不是读完就解锁;】
(2) 事务在更新某数据的瞬间(就是发生更新的瞬间),必须先对其加 行级排他锁,直到事务结束才释放
事务1更新某行记录时,事务2不能对这行记录做更新,直到事务1结束。
问题:因为事务1读取和修改的的时候只是加了行级别锁, 所以其他事务可以对该表插入数据,会导致幻读
MVCC(多版本并发控制) https://www.cnblogs.com/lmj612/p/10598971.html
InnoDB在每行记录后面保存两个隐藏的列来,分别保存了这个行的创建时间和行的删除时间。
这里存储的并不是实际的时间值,而是系统版本号,当数据被修改时,版本号加1
在读取事务开始时,系统会给当前读事务一个版本号,事务会读取版本号<=当前版本号的数据
此时如果其他写事务修改了这条数据,那么这条数据的版本号就会加1,从而比当前读事务的版本号高,读事务自然而然的就读不到更新后的数据了
3.4、序列化(SERIALIZABLE)
MVCC(多版本并发控制) + next-key locking https://blog.csdn.net/qq_35590091/article/details/107734005
最高隔离级别,不允许事务并发执行,而必须串行化执行,最安全,不可能出现更新、脏读、不可重复读、幻读,但是效率最低。
隔离级别越高,数据库事务并发执行性能越差,能处理的操作越少。所以一般地,推荐使用REPEATABLE READ级别保证数据的读一致性。对于幻读的问题,可以通过加锁来防止。
原理:
(1)事务在读取数据时,必须先对其加 表级共享锁 ,直到事务结束才释放;
事务1正在读取A表中的记录时,则事务2也能读取A表,但不能对A表做更新、新增、删除,直到事务1结束。
(2)事务在更新数据时,必须先对其加 表级排他锁 ,直到事务结束才释放。
事务1正在更新A表中的记录时,则事务2不能读取A表的任意记录,更不可能对A表做更新、新增、删除,直到事务1结束。