MYSQL隔离级别 与 锁
1.四种隔离级别下数据不一致的情况
|
脏读
|
不可重复读
|
幻读
|
---|---|---|---|
RU | 是 | 是 | 是 |
RC(快照读) | 否 | 是 | 是 |
RC(当前读) | 否 | 否 | 是 |
RR(快照读) | 否 | 否 | 是 |
RR(当前读) | 否 | 否 | 否 |
Serializable(串行化) | 否 | 否 | 否 |
## 关于RR快照读时会不会造成幻读,我举一个例子,RR隔离级别,id主键
## 我也不知道这算不算幻读,
事务A | 事务B |
begin; | begin; |
select count(*) from test; 结果:1 |
|
insert into test values(4,'asd') | |
update test set name='zxcs'; |
|
阻塞 | |
commit | |
select count(*) from test; 结果:2 |
那什么是当前读,什么是快照读呢?
当前读:select * from table where ? lock in share mode;
select * from table where ?
for
update;
insert into table values (…);
update table set ? where ?;
delete from table where ?;
快照读: select * from table where ?
MVCC的实现原理:----UNDO LOG + 隐藏字段trx_id 和roll_pointer
## trx_id : 对该记录最新修改的事务id
## roll_pointer:老版本号 --保存在undo log中
## 如果一个表有两个字段id和name ,实际上是这样的,会有两个是隐藏字段,其实应该是3个隐藏字段,还有一个跟MVCC无关。
id
|
name
|
trx_id
|
roll_pointer
|
---|---|---|---|
1 | lxl | 40 | 上一个版本记录的地址 |
举个栗子,update table set name= 'lxlxlxl' where id=1;(id是主键),假如当先这条dml的事务号为41
id | name | trx_id | roll_pointer |
1 | lxl | 40 | 上一个版本记录的地址 |
### 这条记录会被放到undo log 中
### 然后
id |
name | trx_id | roll_pointer |
1 | lxlxlxl | 41 | trx_id=40的地址 |
###这条记录会放到表中
那快照读的时候是怎么判断版本号的呢?
ReadView中主要就是有个列表来存储我们系统中当前活跃着的读写事务,也就是begin了还未提交的事务。通过这个列表来判断记录的某个版本是否对当前事务可见。假设当前列表里的事务id为[80,100]。 如果你要访问的记录版本的事务id为50,比当前列表最小的id80小,那说明这个事务在之前就提交了,所以对当前活动的事务来说是可访问的。 如果你要访问的记录版本的事务id为85,发现此事务在列表id最大值和最小值之间,那就再判断一下是否在列表内,如果在那就说明此事务还未提交,所以版本不能被访问。 如果不在那说明事务已经提交,所以版本可以被访问。如果你要访问的记录版本的事务id为110,那比事务列表最大id100都大,那说明这个版本是在ReadView生成之后才发生的,所以不能被访问。
这些记录都是去版本链里面找的,先找最近记录,如果最近这一条记录事务id不符合条件,不可见的话,再去找上一个版本再比较当前事务的id和这个版本事务id看能不能访问,以此类推直到返回可见的版本或者结束。
版本链就是每条记录的隐藏字段roll_pointer组成的链表
那么我有一个问题,当一个事务执行一个dml,是在commit后对表,还是在执行完dml后对表进行修改?
我们可以来对比下RU 和 RC 隔离级别
RU 是为什么会造成脏读的现象?就是为什么会读到未提交的数据?
RC 为什么不会造成脏读?
我们假设执行完dml 就会对表进行修改,而不是commit之后修改
RU :id是主键
事务A | 事务B |
begin; | begin |
update table set name= 'lxlxlxl' where id=1; | |
select * from table | |
能读到事务B修改的数据 |
在RC,RR,RS中事务B需要commit 才能被其他事务看到
可以说明
执行完dml 就会对表进行修改,而不是commit之后修改
再来看RC:id是主键
事务A | 事务B |
begin; | begin; |
update table set name= 'lxlxlxl' where id=1; | |
select * from table where id=1 lock in share mode | |
此时事务A阻塞,S锁和 事务B的X锁冲突 此时事务B对表格已经修改,也成立 但是因为锁冲突的原理不会被其他当前读的事务所看到 如果这里采用快照读不会阻塞,也不会读到更改,因为只会读到上一个版本的记录 |
|
commit; | |
执行,并看到事务B的修改 |
2-PL :Two-phase Locking ,锁操作分为两个阶段,加锁阶段和解锁阶段,并且保证加锁阶段与解锁阶段不相交,
2PL就是将加锁/解锁分为两个完全不相交的阶段。加锁阶段:只加锁,不放锁。解锁阶段:只放锁,不加锁。
关于2-pl 有以下变种:
C2PL : 在事物开始时对所有需要访问的数据获取锁。不存在死锁问题,要么事务等待不能开始,要么就已经得到了全部所需的锁
S2PL : 严格2PL,事务持有的写锁必须提交后再释放,读锁在阶段二时释放
SS2PL: 强严格2PL,事务持有的所有锁必须在事务提交(完成)后释放;
3. 分析一下 一个简单的update在不同隔离级别下的效果
update test set name='lxl' where id=5 |
||||
id为主键 | id为二级唯一索引 | id为二级普通索引 | id不是索引 | |
---|---|---|---|---|
RU |
在主键上id=5的记录加上X锁 操作完成后释放所有X锁 |
先在二级唯一索引上进行带锁的当前读(for update), 找到id=5的记录后加上X锁, 然后通过主键值回到主键索引(聚集索引)中把对应的记录加上X锁, 操作完成后释放所有X锁 |
现在二级普通索引上进行待锁的当前读(for update), 找到所有id=5的记录后加上X锁, 然后通过主键值回到主键索引(聚集索引)中把对应的记录加上X锁, 操作完成后释放所有X锁, 跟唯一索引的区别是:唯一索引只有一条记录 |
使用半一致性读 SQL走聚簇索引的全扫描进行过滤把每条记录都加上X锁, 对于不满足where id=5的记录释放掉锁, 最终只有符合条件的记录带上X锁, |
RC |
在主键上id=5的记录加上X锁 操作完成后释放所有X锁 |
先在二级唯一索引上进行带锁的当前读(for update), 找到id=5的记录后加上X锁, 然后通过主键值回到主键索引(聚集索引)中把对应的记录加上X锁, 操作完成后释放所有X锁 |
现在二级普通索引上进行待锁的当前读(for update), 找到所有id=5的记录后加上X锁, 然后通过主键值回到主键索引(聚集索引)中把对应的记录加上X锁, 操作完成后释放所有X锁, (有人会说半一致性读,确实,半一致性读,能肯定的是,没执行update之前没有放锁,可以测试,但不能肯定他不合条件的是在获取所有锁->释放不合条件记录的X锁 -> 执行 还是获取所有锁-> 执行->释放不合条件记录的X锁 ,下面拿例子说一下这个不一致性读) 跟唯一索引的区别是:唯一索引只有一条记录 |
使用半一致性读
SQL走聚簇索引的全扫描进行过滤把每条记录都加上X锁, 对于不满足where id=5的记录释放掉锁, 最终只有符合条件的记录带上X锁, |
RR |
在主键上id=5的记录加上X锁 操作完成后释放所有X锁 |
先在二级唯一索引上进行带锁的当前读(for update), 找到id=5的记录后加上X锁, 然后通过主键值回到主键索引(聚集索引)中把对应的记录加上X锁, 操作完成后释放所有X锁 |
例如 id 的二级索引上有值【1,2,5,7】 现在二级普通索引上进行待锁的当前读(for update), 找到所有id=5的记录后加上X锁,以及把【2-5】【5-5】【5-7】之间加上间隙锁, 然后通过主键值回到主键索引(聚集索引)中把对应的记录加上X锁 操作完成后释放所有X锁和gap锁,跟唯一索引的区别是唯一索引只有一条记录 |
在聚集索引上扫表,
并对每条记录加上X锁, 但不会像RC那样把不符合条件的释放掉, 直到事务结束, 同时对每个间隙加上gap锁, 例如有主键[1,2,3,4], 在1-2 , 2-3, 3-4,4-~,所有间隙加上gap锁 操作结束,把所有X锁和gap锁释放掉 |
RS |
在主键上id=5的记录加上X锁 操作完成后释放所有X锁 |
先在二级唯一索引上进行带锁的当前读(for update), 找到id=5的记录后加上X锁, 然后通过主键值回到主键索引(聚集索引)中把对应的记录加上X锁, 操作完成后释放所有X锁 |
例如 id 的二级索引上有值【1,2,5,7】 现在二级普通索引上进行待锁的当前读(for update), 找到所有id=5的记录后加上X锁,以及把【2-5】【5-5】【5-7】之间加上间隙锁, 然后通过主键值回到主键索引(聚集索引)中把对应的记录加上X锁 操作完成后释放所有X锁和gap锁,跟唯一索引的区别是唯一索引只有一条记录 |
在聚集索引上扫表, 并对每条记录加上X锁, 但不会像RC那样把不符合条件的释放掉, 直到事务结束, 同时对每个间隙加上gap锁, 例如有主键[1,2,3,4], 在1-2 , 2-3, 3-4,4-~,所有间隙加上gap锁 操作结束,把所有X锁和gap锁释放掉 |
结论:update test set name='lxl' where id=5
(1)在id为主键的情况下,在RU,RC,RR,S隔离级别下加锁和释放锁的过程都是一样的
(2)在id为二级唯一索引时,在RU,RC,RR,S隔离级别下加锁和释放锁的过程也是一样的
(3)在id为二级普通索引时,在RU,RC下过程一样,在RR,S隔离级别下多了gap锁防止幻读
(4)在id不是索引的时,RU隔离级别下:;
RC隔离级别下,SQL走聚簇索引的全扫描进行过滤把每条记录都加上X锁,对于不满足where id=5的记录释放掉锁,
RR隔离级别下,SQL走聚簇索引的全扫描对每条记录加上X锁,但不会像RC那样把不符合条件的释放掉,直到事务结束,符合2PL原则。同时 对每个间隙加上gap锁
S隔离级别下,与RR级别一样,只不过强制把事务进行排序,不允许并发操作
4.分析select ,update,delete 在RC级别下不命中索引的操作
RC隔离级别,age不是索引
|
---|
select * from test where age=5; |
## 分析:最后只有主键上age=5的记录带S锁 ## 因为 S2PL的原因,读操作在读完可以把部分读锁释放不必等到commit再释放,而写锁必须事务提交才能释放 |
update viptest.test set name='lxl' where age=5; |
## 分析:最后只有主键上age=5的记录带X锁 ## 因为半一致性读的原因,当读到有锁冲突的记录的时候,mysql会判断,如果不是update需要的数据,如果不是则跳过该记录。 ## 如果读到没加锁的记录话,加锁和释放锁的操作不会被省略 |
delete from viptest.test where age=5; |
## 分析,假如主键上有age 【1,10】范围的数据,因为没走索引,【1,10】每个条记录加上X锁。 ## 半一致性读只对update有效 |
MYSQL隔离级别 与 锁的更多相关文章
- mysql 隔离级别与锁
1.什么是事务 事务是一条或多条数据库操作语句的组合,具备ACID,4个特点. 原子性:要不全部成功,要不全部撤销 隔离性:事务之间相互独立,互不干扰 一致性:数据库正确地改变状态后,数据库的一致性约 ...
- Mysql隔离级别,锁与MVCC
关键词:事务,ACID,隔离级别,MVCC,共享锁,排它锁 阅读本文前请先阅读http://hedengcheng.com/?p=771 http://www.hollischuang.com/arc ...
- mysql隔离级别与锁,接口并发响应速度的关系(2)
innoDB默认隔离级别 mysql> SELECT @@tx_isolation; +-----------------+ | @@tx_isolation | +-------------- ...
- mysql隔离级别与锁,接口并发响应速度的关系(1)
默认隔离级别:可重复读 原始数据 | id | name | addr | | nick | NULL | 事务1 事务2 start transaction start transaction ; ...
- MySQL数据库引擎、事务隔离级别、锁
MySQL数据库引擎.事务隔离级别.锁 数据库引擎InnoDB和MyISAM有什么区别 大体区别为: MyISAM类型不支持事务处理等高级处理,而InnoDB类型支持.MyISAM类型的表强调的是性能 ...
- 重新学习MySQL数据库9:Innodb中的事务隔离级别和锁的关系
重新学习MySQL数据库9:Innodb中的事务隔离级别和锁的关系 Innodb中的事务隔离级别和锁的关系 前言: 我们都知道事务的几种性质,数据库为了维护这些性质,尤其是一致性和隔离性,一般使用加锁 ...
- MySQL事务隔离级别,锁(转)
add by zhj: 本文针对的是MySQL的InnoDB存储引擎,不适用于MySQL的其它存储引擎和其它数据库 原文:MySQL数据库事务隔离级别(Transaction Isolation Le ...
- Mysql数据库事务的隔离级别和锁的实现原理分析
Mysql数据库事务的隔离级别和锁的实现原理分析 找到大神了:http://blog.csdn.net/tangkund3218/article/details/51753243 InnoDB使用MV ...
- MySql各事务隔离级别及锁问题
聊事务隔离级别和锁问题之前首先得理解事务的隔离级别和共享锁及独占锁的概念: 事务的隔离级别: 脏读 不可重复读 幻读 Read uncommitted √ √ √ Read committed × ...
随机推荐
- CyclicBarrier是如何成为一个"栅栏"的
CyclicBarrier是一种类似于栅栏的存在,意思就是在栅栏开放之前你都只能被挡在栅栏的一侧,当栅栏移除之后,之前被挡在一侧的多个对象则同时开始动起来. 1. 如何使用CyclicBarrier ...
- block 的内存结构衍生出来的面试题
今天在群里看到大佬们在讨论一个面试题,问如下代码在 32bit 和 64bit 系统上分别报什么错误: #import <Foundation/Foundation.h> int main ...
- Vue引用阿里图标库
首先进入官网http://www.iconfont.cn/ 转载:https://blog.csdn.net/qq_34802010/article/details/81451278 选择图标库 在里 ...
- Nginx知多少系列之(三)配置文件详解
目录 1.前言 2.安装 3.配置文件详解 4.Linux下托管.NET Core项目 5.Linux下.NET Core项目负载均衡 6.Linux下.NET Core项目Nginx+Keepali ...
- ASP.NET Core中的Controller
ASP.NET CORE出现之前我们实现的Controller,MVC都继承自Controller基类,WebApi的话继承自ApiController.现在ASP.NET CORE把MVC跟WebA ...
- Debian Bug report logs - #724721 zsh: Tab completion error with vi
Hi Ludovic, Ludovic Lebègue wrote: > While using zsh shell trying to autocomplete with tab key di ...
- javascript入门 之 zTree (一)
1.安装: 我用的bower工具,所以执行: bower install ztree 2.详细功能与配制,请考官方文档: http://www.treejs.cn/v3/main.php#_zTree ...
- 基础类封装-pymysql库操作mysql封装
import pymysql from lib.logger import logger from warnings import filterwarnings filterwarnings(&quo ...
- Pytest系列(14)- 配置文件pytest.ini的详细使用
如果你还想从头学起Pytest,可以看看这个系列的文章哦! https://www.cnblogs.com/poloyy/category/1690628.html 前言 pytest配置文件可以改变 ...
- [Python] 字符串加密解密
1. 最简单的方法是用base64: import base64 s1 = base64.encodestring('hello world') s2 = base64.decodestring(s1 ...