-
2022-02-17 11:08:15
目录
一、 4种隔离级别
InnoDB默认是可重复读的(REPEATABLE READ)。
1、读未提交(Read Uncommitted)
一个事务还没提交时,它做的变更就能被别的事务看到
允许脏读,也就是可能读取到其他会话中未提交事务修改的数据
2、读提交(Read Committed)
一个事务提交之后,它做的变更才会被其他事务看到
只能读取到已经提交的数据。Oracle等多数数据库默认都是该级别 (不重复读)
3、可重复读(Repeated Read)
一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。当然在可重复读隔离级别下,未提交变更对其他事务也是不可见的。
可重复读。在同一个事务内的查询都是事务开始时刻一致的,InnoDB默认级别。在SQL标准中,该隔离级别消除了不可重复读,但是还存在幻象读
4、串行读(Serializable)
串行化,顾名思义是对于同一行记录,“写”会加“写锁”,“读”会加“读锁”。当出现读写锁冲突的时候,后面访问的事务必须等前一个事务执行完成,才能继续执行。
完全串行化的读,每次读都需要获得表级共享锁,读写相互都会阻塞
隔离级别
脏读(Dirty Read)
不可重复读(NonRepeatable Read)
幻读(Phantom Read)
读未提交(Read uncommitted)
可能
可能
可能
读提交(Read committed)
不可能
可能
可能
可重复读(Repeatable read)
不可能
不可能
可能
串行化(Serializable )
不可能
不可能
不可能
二、不同隔离级别的示例
mysql> create table T(c int) engine=InnoDB; insert into T(c) values(1);
我们来看看在不同的隔离级别下,事务 A 会有哪些不同的返回结果,也就是图里面 V1、V2、V3 的返回值分别是什么:
1)若隔离级别是“读未提交”, 则 V1 的值就是 2。这时候事务 B 虽然还没有提交,但是结果已经被 A 看到了。因此,V2、V3 也都是 2。
2)若隔离级别是“读提交”,则 V1 是 1,V2 的值是 2。事务 B 的更新在提交后才能被 A看到。所以, V3 的值也是 2。
3)若隔离级别是“可重复读”,则 V1、V2 是 1,V3 是 2。之所以 V2 还是 1,遵循的就是这个要求:事务在执行期间看到的数据前后必须是一致的。
4)若隔离级别是“串行化”,则在事务 B 执行“将 1 改成 2”的时候,会被锁住。直到事务 A 提交后,事务 B 才可以继续执行。所以从 A 的角度看, V1、V2 值是 1,V3 的值是 2。
在实现上,数据库里面会创建一个视图,访问的时候以视图的逻辑结果为准。
1、在“可重复读”隔离级别下,这个视图是在事务启动时创建的,整个事务存在期间都用这个视图。 2、在“读提交”隔离级别下,这个视图是在每个 SQL 语句开始执行的时候创建的。 3、这里需要注意的是,“读未提交”隔离级别下直接返回记录上的最新值,没有视图概念; 4、而“串行化”隔离级别下直接用加锁的方式来避免并行访问。
总结来说,存在即合理,哪个隔离级别都有它自己的使用场景,你要根据自己的业务情况来定。我想你可能会问那什么时候需要“可重复读”的场景呢?我们来看一个数据校对逻辑的案例。
假设你在管理一个个人银行账户表。一个表存了每个月月底的余额,一个表存了账单明细。这时候你要做数据校对,也就是判断上个月的余额和当前余额的差额,是否与本月的账单明细一致。你一定希望在校对过程中,即使有用户发生了一笔新的交易,也不影响你的校对结果。这时候使用“可重复读”隔离级别就很方便。事务启动时的视图可以认为是静态的,不受其他事务更新的影响。
三、隔离级别的设置与查看
1、设置隔离级别
1)在my.ini中设置级别
vim /etc/my.ini transaction-isolation = {READ-UNCOMMITTED | READ-COMMITTED | REPEATABLE-READ | SERIALIZABLE}
2)使用set transaction 设置单个会话或者所有新进连接的隔离级别
SET [SESSION | GLOBAL] \ TRANSACTION ISOLATION LEVEL \ {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE}
(1)global,意思是此语句将应用于之后的所有session,而当前已经存在的session不受影响
(2)选择session,将应用于当前session内此点之后的所有事务(包括已开始但未提交的)。
(3)什么都不写,将应用于当前session内的下一个还未开始的事务。
2、查看当前隔离级别
SELECT @@global.tx_isolation; SELECT @@session.tx_isolation; SELECT @@tx_isolation; show variables like 'transaction_isolation'; +-----------------------+----------------+ | Variable_name | Value | +-----------------------+----------------+ | transaction_isolation | READ-COMMITTED | +-----------------------+----------------+
四、事务隔离的实现原理
1、隔离级别实现的原理
在 MySQL 中,实际上每条记录在更新的时候都会同时记录一条回滚操作。记录上一次的最新值,通过回滚操作,都可以得到前一个状态的值。假设一个值从 1 被按顺序改成了 2、3、4,在回滚日志里面就会有类似下面的记录:
当前值是 4,但是在查询这条记录的时候,不同时刻启动的事务会有不同的 read-view。
如图中看到的,在视图 A、B、C 里面,这一个记录的值分别是 1、2、4,同一条记录在系统中可以存在多个版本,就是数据库的多版本并发控制(MVCC)。
对于 read-viewA,要得到 1,就必须将当前值依次执行图中所有的回滚操作得到。同时你会发现,即使现在有另外一个事务正在将 4 改成 5,这个事务跟 read-view A、B、C 对应的事务是不会冲突的。
你一定会问,回滚日志总不能一直保留吧,什么时候删除呢?答案是,在不需要的时候才删除。也就是说,系统会判断,当没有事务再需要用到这些回滚日志时,回滚日志会被删除。
什么时候才不需要了呢?就是当系统里没有比这个回滚日志更早的 read-view 的时候。
2、为什么建议尽量不要使用长事务?
长事务意味着系统里面会存在很老的事务视图。由于这些事务随时可能访问数据库里面的任何数据,所以这个事务提交之前,数据库里面它可能用到的回滚记录都必须保留,这就会导致大量占用存储空间。
在 MySQL 5.5 及以前的版本,回滚日志是跟数据字典一起放在 ibdata 文件里的,即使长事务最终提交,回滚段被清理,文件也不会变小。我见过数据只有 20GB,而回滚段有200GB 的库。最终只好为了清理回滚段,重建整个库。除了对回滚段的影响,长事务还占用锁资源,也可能拖垮整个库,这个我们会在后面讲锁的时候展开。
3、如何避免长事务对业务的影响?
1)从应用开发端来看:
(1)确认是否使用了 set autocommit=0。这个确认工作可以在测试环境中开展,把MySQL 的 general_log 开起来,然后随便跑一个业务逻辑,通过 general_log 的日志来确认。一般框架如果会设置这个值,也就会提供参数来控制行为,你的目标就是把它改成 1,否则事务一直等着被提交占用资源。
(2)确认是否有不必要的只读事务。有些框架会习惯不管什么语句先用 begin/commit 框起来。我见过有些是业务并没有这个需要,但是也把好几个 select 语句放到了事务中。这种只读事务可以去掉。
(3)业务连接数据库的时候,根据业务本身的预估,通过 SET MAX_EXECUTION_TIME 命令,来控制每个语句执行的最长时间,避免单个语句意外执行太长时间。
2)其次,从数据库端来看:
(1)监控 information_schema.Innodb_trx 表,设置长事务阈值,超过就报警 / 或者 kill;
(2)Percona 的 pt-kill 这个工具不错,推荐使用;
(3)在业务功能测试阶段要求输出所有的 general_log,分析日志行为提前发现问题;
(4)如果使用的是 MySQL 5.6 或者更新版本,把 innodb_undo_tablespaces 设置成2(或更大的值)。如果真的出现大事务导致回滚段过大,这样设置后清理起来更方便。
五、隔离级别引起的问题
1、脏读
脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据(出现在读未提交隔离级别下)
############## session 1 ############## mysql> select @@session.tx_isolation; +-----------------------+ | @@session.tx_isolation | +-----------------------+ | REPEATABLE-READ | +-----------------------+ 1 row in set (0.00 sec) mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> insert into ttd values(1); Query OK, 1 row affected (0.05 sec) mysql> select * from ttd; +------+ | id | +------+ | 1 | +------+ 1 row in set (0.00 sec) ############session 2(会话2):############## # 在REPEATABLE-READ 级别下 不会出现脏读 mysql> select * from ttd; Empty set (0.00 sec) #设置级别为读未提交 mysql> set session transaction isolation level read uncommitted; Query OK, 0 rows affected (0.00 sec) mysql> select @@session.tx_isolation; +------------------------+ | @@session.tx_isolation | +------------------------+ | READ-UNCOMMITTED | +------------------------+ 1 row in set (0.00 sec) #读到了session 1未提交,但修改的数据 mysql> select * from ttd; +------+ | id | +------+ | 1 | +------+ 1 row in set (0.00 sec)
结论:session 2 在READ-UNCOMMITTED 下读取到session 1 中未提交事务修改的数据.
2、不可重复读
是指在一个事务内(前提是在事务中,如果一个session是事务,另一个session不是事务则不影响),多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。
这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。(发生在读提交隔离级别下)
############session 1(会话1):############## #设置隔离级别为 读提交 mysql> select @@session.tx_isolation; +------------------------+ | @@session.tx_isolation | +------------------------+ | READ-COMMITTED | +------------------------+ 1 row in set (0.00 sec) mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> select * from ttd; +------+ | id | +------+ | 1 | +------+ 1 row in set (0.00 sec) ############session 2(会话2):############## mysql> insert into ttd values(2); Query OK, 1 row affected (0.00 sec) mysql> select * from ttd; +------+ | id | +------+ | 1 | | 2 | +------+ 2 rows in set (0.00 sec) mysql> commit; Query OK, 0 rows affected (0.02 sec) ############session 1(会话1):############## #和第一次的结果不一样,READ-COMMITTED 级别出现了不重复读 mysql> select * from ttd; +------+ | id | +------+ | 1 | | 2 | +------+ 2 rows in set (0.00 sec)
原因是因为session1 是 read committed (读提交)级别,在没有commit的情况下读到的两次数据不一致
可重复读级别就不会出现上述下现象
############session 1(会话1):############## mysql> select @@session.tx_isolation; +------------------------+ | @@session.tx_isolation | +------------------------+ | REPEATABLE-READ | +------------------------+ 1 row in set (0.00 sec) mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> select * from ttd; +------+ | id | +------+ | 1 | | 2 | +------+ 2 rows in set (0.00 sec) ############session 2(会话2):############## mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> insert into ttd values(3); Query OK, 1 row affected (0.00 sec) mysql> commit; Query OK, 0 rows affected (0.03 sec) ############session 1(会话1):############## #和第一次的结果一样,REPEATABLE-READ级别出现了重复读,并没有读到3,直到session1 commit之后才能读到3 mysql> select * from ttd; +------+ | id | +------+ | 1 | -------- | 2 | +------+ 2 rows in set (0.00 sec)
3、幻读
第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。
mysql>CREATE TABLE `demo` ( `id` bigint(20) NOT NULL default '0', `value` varchar(32) default NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB mysql> select @@global.tx_isolation, @@tx_isolation; +-----------------------+-----------------+ | @@global.tx_isolation | @@tx_isolation | +-----------------------+-----------------+ | REPEATABLE-READ | REPEATABLE-READ | +-----------------------+-----------------+
实验一:
Session A
Session B
START TRANSACTION;
START TRANSACTION;
SELECT * FROM demo;
empty set
INSERT INTO demo VALUES (1, 'a');
SELECT * FROM demo;
empty set
COMMIT;
SELECT * FROM demo;
empty set
INSERT INTO demo VALUES (1, 'a');
ERROR 1062 (23000):
Duplicate entry '1' for key 1
刚刚明明告诉我没有这条记录的,在REPEATABLE-READ(可重复读)级别下出现了幻读
实验二:
Session A
Session B
START TRANSACTION;
START TRANSACTION;
SELECT * FROM demo;
+------+-------+
| | id | value |
| +------+-------+
| | 1 | a |
| +------+-------+
INSERT INTO demo VALUES (2, 'b');
| SELECT * FROM demo;
| +------+-------+
| | id | value |
| +------+-------+
| | 1 | a |
| +------+-------+
COMMIT;
| SELECT * FROM demo;
| +------+-------+
| | id | value |
| +------+-------+
| | 1 | a |
| +------+-------+
| UPDATE t_bitfly SET value='z';
| Rows matched: 2 Changed: 2 Warnings: 0
| SELECT * FROM demo;
| +------+-------+
| | id | value |
| +------+-------+
| | 1 | z |
| | 2 | z |
| +------+-------+
| (怎么多出来一行)
本事务中第一次读取出一行,做了一次更新后,另一个事务里提交的数据就出现了。也可以看做是一种幻读。
当隔离级别是可重复读,且禁用innodb_locks_unsafe_for_binlog的情况下,在搜索和扫描index的时候使用的next-key locks可以避免幻读。
4、在可重复读级别出现前后数据不一致现象
Session A
Session B
START TRANSACTION;
START TRANSACTION;
| SELECT * FROM demo;
| +----+-------+
| | id | value |
| +----+-------+
| | 1 | a |
| +----+-------+
INSERT INTO demo VALUES (2, 'b');
COMMIT;
| SELECT * FROM demo;
| +----+-------+
| | id | value |
| +----+-------+
| | 1 | a |
| +----+-------+
| SELECT * FROM demo LOCK IN SHARE MODE;
| +----+-------+
| | id | value |
| +----+-------+
| | 1 | a |
| | 2 | b |
| +----+-------+
| SELECT * FROM demo FOR UPDATE;
| +----+-------+
| | id | value |
| +----+-------+
| | 1 | a |
| | 2 | b |
| +----+-------+
| SELECT * FROM demo;
| +----+-------+
| | id | value |
| +----+-------+
| | 1 | a |
| +----+-------+
如果使用普通的读,会得到一致性的结果,如果使用了加锁的读,就会读到“最新的”“提交”读的结果。
本身,可重复读和提交读是矛盾的。在同一个事务里,如果保证了可重复读,就会看不到其他事务的提交,违背了提交读;如果保证了提交读,就会导致前后两次读到的结果不一致,违背了可重复读。
六、总结
InnoDB提供了这样的机制,在默认的可重复读的隔离级别里,可以使用加锁读去查询最新的数据(提交读)。MySQL InnoDB的可重复读并不保证避免幻读,需要应用使用加锁读来保证。而这个加锁读使用到的机制就是next-key locks。
四个级别逐渐增强,每个级别解决一个问题。事务级别越高,性能越差,大多数环境read committed 可以用
更多相关内容 -
MySQL事务隔离级别
2019-03-21 01:54:28NULL 博文链接:https://cuishuangjia.iteye.com/blog/964885 -
Mysql事务隔离级别原理实例解析
2020-12-14 09:57:08再加上很多书都说可重复读解决了幻读问题,比如《mysql技术内幕–innodb存储引擎》等,不一一列举了,因此网上关于事务隔离级别的文章大多是有问题的,所以再开一文说明! 本文所讲大部分内容,皆有官网作为佐证,... -
MySQL数据库事务隔离级别详解
2020-12-16 04:53:45数据库事务隔离级别 数据库事务的隔离级别有4个,由低到高依次为 Read uncommitted:允许脏读。 Read committed: 防止脏读,最常用的隔离级别,并且是大多数数据库的默认隔离级别。 Repeatable read:可以防止脏... -
mysql事务隔离级别
2021-08-13 00:28:31mysql事务的隔离级别:定义了事务与事务之间的隔离程度; mysql有四种事务的隔离级别: 3. 由隔离性引发的问题 脏读、不可重复度、幻读 4. 演示四种隔离级别 注意:我的mysql是5.7.19,不同版本的命令会有些...1. 事务
2. 事务的隔离级别
- mysql事务的隔离级别:定义了事务与事务之间的隔离程度;
- mysql有四种事务的隔离级别:
3. 由隔离性引发的问题
- 脏读、不可重复度、幻读
4. 演示四种隔离级别
-
注意:我的mysql是5.7.19,不同版本的命令会有些微调
-
首先需要开启两个会话,才能演示不同事务之间的隔离性
-
分别查看两个会话中事务的隔离级别
select @@tx_isolation;
一般默认的都是【可重复读】隔离级别
-
把一个会话的事务隔离级别改变,依次演示各种问题
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED/*读未提交---隔离性低*/ READ COMMITTED/*读已提交---隔离性中*/ REPEATABLE READ/*可重复读---隔离性高*/ SERIALIZABLE/*可串行化---隔离性最高*/
-
现在:左边是【可重复读级别】、右边是【读未提交级别】
-
两个会话都开启事务,因为隔离性就是和事务有关的,离开事务不谈隔离级别
-
建立一张表
account(id,name,balance)
我们可以看到,在【读未提交】会话中建立一张表,在【可重复读】会话中是可以感知到这张表的创建的,隔离的仅仅是对表的dml操作(delete update insert),对表的创建是不隔离的; -
在左边插入数据,并且还没有提交事务,右边就可以查询到,我么就说右边的是【脏读】
-
我们可以做适当推理,在左边事务未提交时,右边都可以感知到,那么一旦左边操作提交,那么右边就一定是感应得到的,无论是删除、修改还是插入;这就说明,【脏读】是非常严重的问题,出现脏读就一定会出现【不可重复读】【幻读】,咱们来试试:
-
由此证明了【read uncommitted】 隔离级别会有脏读、不可重复读、幻读
-
接下来,把右边设置成【read committed】隔离级别,继续试验:
提醒: 隔离性的体现,一定要在事务中,因此start transaction;
是必不可少的;- 上述证明,【read committed】隔离级别没有脏读,但依然有不可重复读、幻读
-
接下来提交事务,双边均提交事务,把右边设置成【可重复读级别】:
上图证明了没有【脏读】的现象;
上图证明了没有【幻读】、【不可重复读】现象
-
需要强调的是,事务一旦提交,那么不同会话之间的数据便一致了,因为隔离性针对的是事务:
-
下面演示【可串行化】隔离级别
-
它与【可重复读】的主要区别是,可串行化会给表加锁,一旦一个事务加入可串行化序列,那么它可操作的表必须是空闲的表,若某个表正在被其他事务操作,那么可串行化事务便会一直等待,直到那个事务结束,或者查询超时:
可以观察到,右边会话的查询操作等待了很久,直到左边commit后,才有结果,串行化效率比较低,但适用于多个会话之间需要数据同步互通的系统,虽然需要等待一些时间,但实现了所有会话数据的一致性;
-
可串行化事务会出现两种情况:
1.A会话开启,事务开启并设置为可串行化,先操作某个表,事务仍在继续;此时另一个事务B,不管是什么隔离级别,再操作此表时,就只能selelct,不能进行dml操作
2.A会话开启,事务开启并设置为可串行化,但是事务B抢先操作了那张表,结果A就不能操作这张表了,连select语句都不行!这是不一样的两种情况,注意区分;
5. 隔离级别查询设置命令
-- 查询当前会话隔离级别 SELECT @@tx_isolation; -- 查询系统隔离级别(用户登陆时默认的隔离级别) SELECT @@global.tx_isolation; -- 设置当前会话隔离级别 SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED/*读未提交---隔离性低*/ READ COMMITTED/*读已提交---隔离性中*/ REPEATABLE READ/*可重复读---隔离性高*/ SERIALIZABLE/*可串行化---隔离性最高*/ -- 设置系统当前系统隔离级别 SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED/*读未提交---隔离性低*/ READ COMMITTED/*读已提交---隔离性中*/ REPEATABLE READ/*可重复读---隔离性高*/ SERIALIZABLE/*可串行化---隔离性最高*/
-
MySQL 事务隔离级别
2019-08-20 16:28:12隔离性 隔离性要求一个事务对数据库中数据的修改...在未提交读这种隔离级别中,对数据的修改,即使还未提交,对其他的事务也是可见的。事务可以读取未提交的数据,也称之为“脏读”,读到的数据被称之为“脏数据”。...隔离性
隔离性要求一个事务对数据库中数据的修改,在未提交完成前对于其他事务是不可见的。
SQL标准中定义了四种隔离级别:- 未提交读
- 已提交读
- 可重复读
- 可串行化
下面来详细说明一下这四种隔离级别。
未提交读 (READ UNCOMMITED)
在未提交读这种隔离级别中,对数据的修改,即使还未提交,对其他的事务也是可见的。事务可以读取未提交的数据,也称之为“脏读”,读到的数据被称之为“脏数据”。未提交之前,数据是不准确的,可能会被回滚,也可能还有后续操作的变化。
之前在使用SQLserver时,经常会建议,查询的时候在from子句加上WITH(NOLOCK)。这个关键字的作用就是进行脏读。这是由于在老版本的SQLserver中,是不支持非锁定读的。现在在新版本的SQLserver中可以进行非锁定读了。允许脏读可能产生较多的并发操作,但其代价是读取以后会被其他事务回滚的数据修改。举个栗子:老板给员工发工资,实际发6千,一手抖按了9千,这时员工账户的数据已经发生了更改,但是事务还没提交。员工一查,自己多了3千块钱,以为是涨工资了,过去谢谢老板。老板一看不对啊,我按错了,回滚事务,重新给发6千。小伙纸欲哭无泪。
已提交读(READ COMMITED)
这个隔离级别是大多数操作系统的默认隔离级别,也是平时用的最多的隔离级别。常见的Oracle、SQLserver等,但是MySQL是个例外,MySQL的默认隔离级别是可重复读。已提交读满足前面隔离性的简单定义。一个事务对数据所作的修改,只有到提交之后才能看到。
举个栗子:老板给员工发工资,老板说,我已经给你们发工资了啊,你们查查。然后一众人员一查账户余额都是0 。当转账的事务没有走完的时候,查询出来是没有发生变化的数据。过一会转账事务完成,员工再一查工资,果然到账了。
可重复读(REPEATABLE READ)
该级别保证了在同一个事务中多次读取同样的记录的结果是一致的。相比与可重复读这个隔离级别,已提交读又称为不可重复读。这两个隔离级别有什么区别呢,下面就通过一个例子展示一下。
首先我们建立两个数据库连接
首先切换数据库为test,在这个库中我建立了一个简单的表 t,里面就这些数字。
我们来看一下这个表的隔离级别是什么,使用下面这个命令。可以看到,当前的隔离级别是可重复读show variables like '%iso%';
我们开启两个事务,在其中一个事务中进行查询,另一个事务进行插入操作。
事务1
事务2
这里两个事务都未提交,首先在事务1中进行查询操作,查询小于7的记录,可以查到1、3、5三条记录。之后再事务2中做了insert操作,插入了记录 2 ,然后再去事务1中查询,显示结果还是1、3、5。然后我们提交一下事务2,可以看到事务1查询出来还是1、3、5。这个事务级别就是可重复读,即使其他事务已经提交,但这个事务中读到的数据还是原先的数据。
如果是已提交读,那么在事务2提交之后,事务1进行查询就会是最新的数据。可串行化(SERIALIZABLE)
串行化是最高的隔离级别。串行化会在读取的每一行上都加锁,所以可能会导致大量的锁超时和锁争用问题。在实际业务中我们很少使用这个隔离级别。除非是严格要求数据一致性,并且可以接受在没有并发的前提下,我们才会考虑使用这种隔离级别。
总结
这四种隔离级别隔离性从低到高分别是:未提交读、已提交读、可重复读、可串行化。
并发性跟隔离性恰好相反,从低到高是:可串行化、可重复读、已提交读、未提交读。
这也非常好理解,隔离性越高,说明锁的粒度越细。并发性自然就会降低。好比家里有大门、里屋门、柜子门。如果全都加锁,我取完东西在把钥匙给下一个人。这样隔离级别高,但是操作效率肯定慢。反过来,如果都不加锁,大家不用等待钥匙,就可以同时操作一个柜子。并发性高,但是取到的东西就会有些乱。
MySQL的默认隔离级别是可重复读,不是读已提交。 -
MySQL查看和修改事务隔离级别的实例讲解
2020-12-14 09:55:14查看事务隔离级别 在 MySQL 中,可以通过show variables like ‘%tx_isolation%’或select @@tx_isolation;语句来查看当前事务隔离级别。 查看当前事务隔离级别的 SQL 语句和运行结果如下: mysql> show variables ... -
五分钟搞清楚MySQL事务隔离级别
2021-01-06 22:44:40作者:伞U ...好久没碰数据库了,只是想起自己当时在搞数据库的...为了说明问题,我们打开两个控制台分别进行登录来模拟两个用户(暂且成为用户 A 和用户 B 吧),并设置当前 MySQL 会话的事务隔离级别。 一. read unco -
MySQL事务及Spring隔离级别实现原理详解
2020-09-08 21:15:52主要介绍了MySQL事务及Spring隔离级别实现原理详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 -
MySQL的Innodb中的事务隔离级别和锁的关系
2020-12-14 20:45:52我们都知道事务的几种性质,数据库为了维护这些性质,尤其是一致性和隔离性,一般使用加锁这种方式。同时数据库又是个高并发的应用,同一时间会有大量的并发访问,如果加锁过度,会极大的降低并发处理能力。所以... -
Mysql事务隔离级别测试
2022-01-28 16:24:04以下几个概念是事务隔离级别要实际解决的问题,所以需要搞清楚都是什么意思。 脏读 脏读指的是读到了其他事务未提交的数据,未提交意味着这些数据可能会回滚,也就是可能最终不会存到数据库中,也就是不存在的数据...一、概念说明
以下几个概念是事务隔离级别要实际解决的问题,所以需要搞清楚都是什么意思。
脏读
脏读指的是读到了其他事务未提交的数据,未提交意味着这些数据可能会回滚,也就是可能最终不会存到数据库中,也就是不存在的数据。读到了并一定最终存在的数据,这就是脏读。
可重复读
可重复读指的是在一个事务内,最开始读到的数据和事务结束前的任意时刻读到的同一批数据都是一致的。通常针对数据更新(UPDATE)操作。
不可重复读
对比可重复读,不可重复读指的是在同一事务内,不同的时刻读到的同一批数据可能是不一样的,可能会受到其他事务的影响,比如其他事务改了这批数据并提交了。通常针对数据更新(UPDATE)操作。
幻读
幻读是针对数据插入(INSERT)操作来说的。假设事务A对某些行的内容作了更改,但是还未提交,此时事务B插入了与事务A更改前的记录相同的记录行,并且在事务A提交之前先提交了,而这时,在事务A中查询,会发现好像刚刚的更改对于某些数据未起作用,但其实是事务B刚插入进来的,让用户感觉很魔幻,感觉出现了幻觉,这就叫幻读。
事务隔离级别
SQL 标准定义了四种隔离级别,MySQL 全都支持。这四种隔离级别分别是:
- 读未提交(READ UNCOMMITTED)
- 读提交 (READ COMMITTED)
- 可重复读 (REPEATABLE READ)
- 串行化 (SERIALIZABLE)
可重复读是 MySQL 的默认级别。
二、开始测试
前提准备
查看隔离级别
show variables like '%tx_isolation%'
数据库表
DROP TABLE IF EXISTS `goods`; CREATE TABLE `goods` ( `goods_id` int(8) NOT NULL AUTO_INCREMENT, `goods_name` varchar(255) DEFAULT NULL, PRIMARY KEY (`goods_id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 34 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
默认表中有一条数据
读未提交
首先设置全局隔离级别为读未提交。
set global transaction isolation level read uncommitted;
两事务中操作的执行顺序如下:
A事务开启线程查询
A事务修改
B事务开启线程查询,拿到的数据是banana。
A事务此时rollback,表中存储数据是apple
上面B产生了脏读,要想解决脏读这个问题可以采用读已提交的隔离级别
读已提交
首先设置全局隔离级别为读已提交。
set global transaction isolation level read committed;
两事务中操作的执行顺序如下:
A开启线程查询
B开启线程查询,查询与A一致,都是表中数据;
A线程修改数据
B线程查询,A事务未提交,B中查询仍是apple
A线程提交
B线程查询得到提交后数据
读提交解决了脏读的问题,但是无法做到可重复读
可重复读
首先设置全局隔离级别为可重复读。
set global transaction isolation level repeatable read;
(下图过程测试实现可重复读)
A开启线程查询
B开启线程查询,查询与A一致,都是表中数据;
A线程修改数据并提交
B线程在同一线程内查询,得到结果未变。与可重复读的概念理解一致,就是同一事务里不同时间点查询,不会受到其他事务修改数据的影响。
此时C新开启线程查询,读到就是修改后的数据
(下图过程测试产生否幻读的问题)
A开启线程查询
B开启线程插入数据并提交
A线程再次查询,没有查到3 cherry
A线程插入3 cherry,报主键=3已经存在。
此时就产生了幻读,A事务本身查询看不到主键=3的数据,但插入时却仍报主键冲突。
串行化
A开启线程查询
B开启线程插入数据,
总结:
读未提交会产生脏读问题;
为了解决脏读,使用读已提交,会产生可不重复读问题;
为了解决不重复读,使用可重复读;
MySQL 已经在可重复读隔离级别下解决了幻读的问题,通过行锁和间隙锁的组合 Next-Key 锁实现。
最简单粗暴的方式是串行,影响性能最大,故很少使用;
-
Mysql事务隔离级别之读提交详解
2020-12-15 21:31:27查看mysql 事务隔离级别 mysql> show variables like '%isolation%'; +---------------+----------------+ | Variable_name | Value | +---------------+----------------+ | tx_isolation | READ-COMMITTED | +---... -
MySQL事务隔离级别和实现原理 - 米扑博客
2021-02-01 20:11:54开发中经常提到数据库的事务,那你知道数据库还有事务隔离的说法吗,事务隔离还有隔离级别,那什么是事务隔离,隔离级别又是什么呢?MySQL 事务本文所说的 MySQL 事务都是指在 InnoDB 引擎下,MyISAM 引擎是不支持... -
mysql事务隔离级别与spring事务隔离级别的区别
2021-01-27 03:19:42mysql事务隔离级别与spring事务隔离级别的区别:脏读:为什么会出现脏读,因为你对数据库的任何修改都会是立即生效的,至于别人能不能看到主要取决与你 是否加锁了,数据库的执行与事务没有关系,事务只是保证对... -
MySQL事务隔离级别的实验
2022-03-14 18:59:22MySQL的事务隔离级别一共有四个,分别是读未提交、读已提交、可重复读以及可串行化。 默认是什么? 它是可重复读。 在两个终端里,打开两个mysql客户端。数据初始时,id为1, age为11 终端1 终端2 结论 start... -
深入理解mysql事务隔离级别和锁机制
2022-03-13 10:30:52其实本质上,出现出现这些问题的本质就是数据库的多事务并发问题,针对这些问题,数据库设计了事务隔离机制、锁机制、MVCC多版本并发控制机制等来解决并发问题。接下来,我会详细的介绍这些机制,让大家深入理解... -
MySQL数据库事务隔离级别
2021-02-26 04:08:52数据库隔离级别有四种,应用《高性能mysql》一书中的说明:然后说说修改事务隔离级别的方法:1.全局修改,修改mysql.ini配置文件,在最后加上#可选参数有:READ-UNCOMMITTED,READ-COMMITTED,REPEATABLE-READ,... -
MySQL 事务隔离级别和实现原理
2021-10-08 15:48:51一、MySQL 事务 本文所说的 MySQL 事务都是指在 InnoDB 引擎下,MyISAM 引擎是不支持事务的。 数据库事务指的是一组数据操作,事务内的操作要么全部成功,要么全部失败。什么都不做,不一定是真的什么都没... -
mysql事务隔离级别——可重复读
2021-11-24 00:07:37设置事务隔离级别为读取已提交| 事务A 事务B 开启事务,设置事务隔离级别为读取已提交 查到5条记录 开启事务,插入一条记录id=6 事务提交 继续查询,依然查到6条记录 这就出现了一个问题,... -
查询mysql事务隔离级别
2019-10-11 16:37:593、事务隔离级别越高,越能保证数据的完整性,但是对并发性能影响大,就比如隔离级别为串行化时,读取数据会锁住整张表。 4、不可重复读和幻读很相似,两者整体上都是两次读的数据不相同,但是不可重复读一般时更新... -
深入理解Mysql事务隔离级别与锁机制
2022-04-12 14:34:03一:联合索引的第一个字段用范围不会走索引 EXPLAIN SELECT * FROM employees WHERE name > 'LiLei' AND age = 22 AND position ='manager'; -
mysql事务隔离级别——读已提交
2021-11-24 01:29:18开启事务,设置事务隔离级别为读未提交 查到5条记录 开启事务,插入一条记录id=6 ,事务并未提交 继续查询,查到6条记录 事务回滚 继续查询,查到5条记录 这样在事务A中就出现了脏读数据 事务脏... -
mysql事务隔离级别及实现原理
2019-09-04 22:22:33近来需要学习的东西实在太多了,看了容易忘记。准备写个blog记录下,方便以后复习。本文主要是讲解mysql的事务隔离级别和不同级别所出现的问题。