前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >记一次mysql事务隔离级别踩坑

记一次mysql事务隔离级别踩坑

原创
作者头像
mariolu
修改2019-08-03 02:11:17
1.6K0
修改2019-08-03 02:11:17
举报
文章被收录于专栏:CDN及云技术分享

一、事务的隔离级别

1.1 隔离级别对应的读到的状态可能性

事务隔离级别有四种,以下图为例,四种隔离级别的读数据状态会是不一样的:

代码语言:javascript
复制
事务 A:  |---0---|---1---|---2---|---3---|---4---|
事务 B:                          |---5---|---6---|---7---|---8---|
查询操作:|读事务开始 <--- 不同的隔离级别在不同的时间点读到不同的状态-->|
  • READ UNCOMMITABLE: 这种读到状态可能是最多的0-8都有可能
  • READ COMMITTABLE : 这种读到0、4、8
  • REPEATEDABLE READ:这种读到0
  • SERAIABLE:查询操作不可访问,并且事务B的操作会被阻塞。效果如下图
代码语言:javascript
复制
事务 A: |---0---|---1---|---2---|---3---|---4---|
事务 B:                                         |---5---|---6---|---7---|---8---|
查询操作: 无论是读事务或者写事务 都会得到排他锁,如果是对同一记录操作,事务A一旦先开始,事务B就不能操作,或者查询也不能操作。

1.2 从锁机制看事务隔离级别

  • READ UNCOMMITABLE: 无任何事务控制,无加任何读锁,写锁
  • READ COMMITABLE,写时候加了排他锁,读了时候使用记录粒度的读锁(共享锁,这个共享锁 不锁事务,锁记录)。事务A查询的时候 读到的是就记录,事务B做了提交,事务A再次查询(也就是不可重复读)。这两次会得到不一样的结果。如果在一个事务里,这个事务里做了两次同样的记录数量查询。两次查询的结果不一样(幻读)。
  • REPEATEDLE READ,可重复读,和READ COMMITABLE不同的时,读锁(共享锁)锁的是整个事务执行的过程。所以在整个事务的执行过程中,任何其他事务尝试更新完这条记录的结果,这个读事务都是用MVCC提交前的版本)。和READ COMMITABLE区别的是,读记录锁的是记录还是事务过程。两者锁的时间不一样。
  • SERAIABLE:RANGE KEY,查询满足这个range范围的这些个数的记录都被锁住。

1.2.1 不可重复读和幻读的区别

不可重复读的重点是修改:

同样的条件 ,   你读取过的数据 ,   再次读取出来发现值不一样了

幻读的重点在于新增或者删除(导致记录数变化)

同样的条件 ,   第 1 次和第 2 次读出来的记录数(强调的是记录数,而不是记录本身,因为读锁的锁粒度是记录自身,而不是整张表)不一样。

1.3 不同隔离级别的错误读取

通过在写的时候加锁,可以解决脏读。

通过在读的时候加锁(或者MVCC提供旧的提交版本),可以解决不可重复读。

通过串行化,可以解决幻读。

隔离级别

脏读

不可重复读

幻读

READ UNCOMMITABLE

1

1

1

READ COMMITABLE

0

1

1

REPEATABLE READ

0

0

1

SERIAL

0

0

0

二、mysql的实现级别

2.1 默认隔离级别

默认是隔离级别是read commitable。这个隔离级别,做个试样:

图1 mysql的隔离级别
图1 mysql的隔离级别

模拟是否可重复读,和幻读:

代码语言:javascript
复制
事务1(窗口1):

mysql> begin
    -> ;
Query OK, 0 rows affected (0.00 sec)

mysql> select count(*) from pem_of_plat_73;//执行一次结果数量集的统计,检验是否有幻读
+----------+
| count(*) |
+----------+
|       48 |
+----------+
1 row in set (0.00 sec)

mysql> select status from pem_of_plat_73 where pem_id=10154;//执行一次记录的修改,检验是否可重复读
+--------+
| status |
+--------+
|      0 |
+--------+
1 row in set (0.00 sec)


事务2:

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> update pem_of_plat_73 set  status=2  where pem_id=10154;//完成记录修改
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> insert into pem_of_plat_73 (plat_id, pem_id, pem_addon_type, task_id, mtime, status, prev_status) values (73, 10155, 0, 22616, now(), 0, -1);
Query OK, 1 row affected (0.00 sec)

mysql> commit;
Query OK, 0 rows affected (0.00 sec)


事务1:

mysql> select count(*) from pem_of_plat_73; //查询记录数量无变多
+----------+
| count(*) |
+----------+
|       48 |
+----------+
1 row in set (0.00 sec)

mysql> select count(*) from pem_of_plat_73 where pem_id=10154; //查询记录无修改。
+----------+
| count(*) |
+----------+
|        1 |
+----------+
1 row in set (0.00 sec)


这跟我们预想的有一点点不一样,如果REAPEATABLE READ应该是有幻读啊。可是为什么记录数一直是48呢。理论上应该有幻读啊。

2.2 mysql(MVCC机制)的幻读

InnoDB使用了MVCC版本控制。 MVCC记录了每一次的增删改查。另一个事务的增删改记录的版本号要高于本事务。所以这些另一个增删改记录是没影响的。

图2 MVCC的多版本机制
图2 MVCC的多版本机制

MVCC并发控制机制 -多版本并发控制(Multiversion Concurrency Control),每一个写操作都会创建一个新版本的数据,读操作会从有限多个版本的数据中挑选一个最合适的结果直接返回;在这时,读写操作之间的冲突就不再需要被关注,而管理和快速挑选数据的版本就成了 MVCC 需要解决的主要问题。

2.3 怎么复现幻读

代码语言:javascript
复制
create table ab(a int primary key, b int);

Tx1:
begin; 
select * from ab; // empty set

Tx2: 
begin; 
insert into ab values(1,1);
commit; 
Tx1: 
select * from ab; // empty set, expected phantom read missing. 
update ab set b = 2 where a = 1; // 1 row affected. //最主要是这一步,通过在本事务更新记录使得MVCC机制select出这条记录
select * from ab; // 1 row. phantom read here!!!! 

2.4、mysql怎么解决幻读

第二个问题是如果想升级成SERIABLE,比如说两个事务都在增加一个表记录。每条新增记录需要记录之前的数量,以这个数量作为记录的版本号。那么第一次的事务增加记录提交,另一个事务需要及时知道这个数量更新。

其实Mysql提供了这么一种机制,读查询也会得到一个排他锁。语法是select ...for update。MySQL会对查询结果集中每行数据都添加排他锁,其他线程对该记录的更新与删除操作都会阻塞。

如果事务B insert了一个记录,那么事务A的select统计数量操作会被阻塞。

如果事务A先select一个记录。事务B想insert/update这个记录,也会被阻塞。

图3 事务1先insert/update但是还没commit,事务2select被阻塞
图3 事务1先insert/update但是还没commit,事务2select被阻塞
图4 可能lock超时
图4 可能lock超时
图5 死锁(怎么出现)
图5 死锁(怎么出现)

三、分布式的事务隔离级别

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、事务的隔离级别
    • 1.1 隔离级别对应的读到的状态可能性
      • 1.2 从锁机制看事务隔离级别
        • 1.2.1 不可重复读和幻读的区别
      • 1.3 不同隔离级别的错误读取
      • 二、mysql的实现级别
        • 2.1 默认隔离级别
          • 2.2 mysql(MVCC机制)的幻读
            • 2.3 怎么复现幻读
              • 2.4、mysql怎么解决幻读
              • 三、分布式的事务隔离级别
              相关产品与服务
              云数据库 SQL Server
              腾讯云数据库 SQL Server (TencentDB for SQL Server)是业界最常用的商用数据库之一,对基于 Windows 架构的应用程序具有完美的支持。TencentDB for SQL Server 拥有微软正版授权,可持续为用户提供最新的功能,避免未授权使用软件的风险。具有即开即用、稳定可靠、安全运行、弹性扩缩等特点。
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档