Mysql的事务的隔离级别详解

悲观锁(Pessimistic Lock), 顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。

乐观锁(Optimistic Lock), 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库如果提供类似于write_condition机制的其实都是提供的乐观锁。

两种锁各有优缺点,不可认为一种好于另一种,像乐观锁适用于写比较少的情况下,即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。但如果经常产生冲突,上层应用会不断的进行retry,这样反倒是降低了性能,所以这种情况下用悲观锁就比较合适。

事务隔离级别共有4种:

事务隔离级别 脏读 不可重复度 幻读
1.未提交读
2.提交读(不可重复读)
3.可重复读
4.串行化

mysql的默认事务隔离级别为可重复读,而Oracle、Sqlserver的默认事务隔离级别都为提交读(不可重复读)
1.InnoDB 支持表锁和行锁,使用索引作为检索条件修改数据时采用行锁,否则采用表锁。锁表时在其它的事务中执行会发生等待的现象,时间长的话会有报错提示。

2 InnoDB 自动给修改操作加锁,给查询操作不自动加锁

3.行锁相对于表锁来说,优势在于高并发场景下表现更突出,毕竟锁的粒度小。

4. 当表的大部分数据需要被修改,或者是多表复杂关联查询时,建议使用表锁优于行锁。

5.为了保证数据的一致完整性,任何一个数据库都存在锁定机制。锁定机制的优劣直接影响到一个数据库的并发处理能力和性能。

举例说明:不可重复读

1.事务隔离级别为不可重复读

2.在两个事务同时开启时

3.事务B中,在事务A操作增删改之前查询一次数据

4.事务A做了更新或删除的操作,并提交

5.事务B中,在事务A操作增删改之后查询一次数据。两次查询结果不一致,可以看见事务A中已经提交的操作

脏读:事务中的修改,即使没有提交,对其他事务也都是可见的。事务可以读取未提交的数据,这也称为脏读。

幻读:指的是当某个事务在读取某个范围内的记录时,另外一个事务又在该范围内插入了新记录,当之前的事务再次读取该范围内的记录时,会产生幻行。InnoDB和XtraDB存储引擎通过多版本并发控制(MVVC)解决了幻读的问题。

事务隔离级别为可重复读的情况下:

序列化和可重复读的实验测试结果:基于MySQL版本5.7.22

可重复读 事务一 事务二 事务一结果 事务二结果
步骤一 begin begin    
步骤二 select select 结果(1) 结果(2)
步骤三 update id=9      
步骤四 select select 出现update的结果(3) 与(2)一致(4)
步骤五   update id=9   此时阻塞状态(可能超时)
步骤六 select select 与(3)结果一致(5) 与(2)结果一致(6)
步骤七 commit      
步骤八 select select 与(3)一致(7) 与(6)一致(8)----------此时体现了可重复读的特点,无论事务一如何修改,是否提交,事务二查询的结果是一致的。
步骤九   update id=9  这里有人说mysql没有彻底解决幻行的问题,我认为那是概念的混淆,串行化是可以避免这个问题的。 事务一提交后执行成功,此时结果按照事务二的结果为准,更新时所以用id为更新条件,否则可能更新失败,应为这个条数据的内容已经被事务一更改
步骤十 select select 与(3)一致(9) 出现事务二update的结果(10)
步骤十一    commit    
步骤十二 select select 与(12)一致(11) 与(10)一致(12)
可重复读 事务一 事务二 事务一结果 事务二结果
步骤一 begin begin    
步骤二 select select 结果(1) 结果(2)
步骤三 insert      
步骤四 select select 出现i事务一nsert的结果(3) 与(2)一致(4)
步骤五   insert    
步骤六 select select 与(3)结果一致(5) 出现事务二insert的结果(6)但是如果id是自增的情况,id会在事务一之后,因为事务先执行的操作,即使没有提交。
步骤七 commit      
步骤八 select select 与(3)一致(7) 与(6)一致(8)----------按照幻读的概念此时应该出现幻行,但是没有出现事务一增加的数据,应为mysql已经解决的幻行的问题。
步骤九   update /delete  id=事务一新增的id   虽然看不见数据,但是可以对数据进行更新和删除修改的操作,因为数据已经实际存在了,查询的时候是从视图中得到的数据。而更新不是,并且更新完就可以看见这条数据
步骤十 select select 与(3)一致(9) 出现事务二update/delete的结果(10)
步骤十一    commit    
步骤十二 select select 与(12)一致(11) 与(10)一致(12)

 共享锁和排他锁:也叫读锁和写锁。

读锁是共享的,或者说是相互不阻塞的。即多个客户在同一时刻可以同时读取同一个资源,互不干扰。

写锁是排他的,也就是说一个写锁会阻塞其他的写锁和读锁。只有这样,才能确保在给定的时间里,只有一个用户能执行写入,并防止其他用户读取正在写入的同一资源。

我的实验结果总结:

可重复读:update、insert加的是读锁,因为其他的事务里面是可以读取数据的,但是不能做修改的操作。

串行化:select 是读锁,因为在select的时候,其他事务是只能select。

update、insert加的是排他锁,因为在update、insert的时候,其他事务是不能select、insert、delete.

简单来说,SERIALIZABLE会在读取的每一行数据上都加锁。

手动实现加锁

共享锁:select * from tableName where ... + lock in share more

排他锁:select * from tableName where ... + for update

查询事务的隔离级别:select  @@tx_isolation;

设置事务的隔离级别:SET session TRANSACTION ISOLATION LEVEL Serializable;(参数可以为:Read uncommitted,Read committed,repeatable read,Serializable)

开启事务:start transaction;或者begin;

提交事务:commit;

原文地址:https://www.cnblogs.com/fengxiaoyuan/p/10259581.html