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 ?
 
简单来说,当前读就是带S锁读或者带X锁读,或者dml操作,快照读:读不加锁,读写不冲突。
 
快照读要从MVCC说起
 
MVCC增加了一种可以不带锁的读取数据方式,但是读取的时版本快照,并不是最新内容
快照读读取的就是MVCC中的版本快照
MVCC只支持Mysql的InnoDB引擎中的已提交读(READ COMMITTD)和可重复读(REPEATABLE READ)这两种隔离级别下使用

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的修改  
 
 
 
 
 
 
 
 
 
 
 
###我的意思是其他在执行完DML后就会对表格进行修改,也即会更改行记录的trx_id,但是在commit之后才会被发现,因为在commit之前,执行dml之后该记录带X锁,会与其他想这条记录的事务冲突,所以在这个期间不会被其他事务看到修改。
 
2. 关于2pl

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隔离级别 与 锁的更多相关文章

  1. mysql 隔离级别与锁

    1.什么是事务 事务是一条或多条数据库操作语句的组合,具备ACID,4个特点. 原子性:要不全部成功,要不全部撤销 隔离性:事务之间相互独立,互不干扰 一致性:数据库正确地改变状态后,数据库的一致性约 ...

  2. Mysql隔离级别,锁与MVCC

    关键词:事务,ACID,隔离级别,MVCC,共享锁,排它锁 阅读本文前请先阅读http://hedengcheng.com/?p=771 http://www.hollischuang.com/arc ...

  3. mysql隔离级别与锁,接口并发响应速度的关系(2)

    innoDB默认隔离级别 mysql> SELECT @@tx_isolation; +-----------------+ | @@tx_isolation | +-------------- ...

  4. mysql隔离级别与锁,接口并发响应速度的关系(1)

    默认隔离级别:可重复读 原始数据 | id | name | addr | | nick | NULL | 事务1 事务2 start transaction start transaction ; ...

  5. MySQL数据库引擎、事务隔离级别、锁

    MySQL数据库引擎.事务隔离级别.锁 数据库引擎InnoDB和MyISAM有什么区别 大体区别为: MyISAM类型不支持事务处理等高级处理,而InnoDB类型支持.MyISAM类型的表强调的是性能 ...

  6. 重新学习MySQL数据库9:Innodb中的事务隔离级别和锁的关系

    重新学习MySQL数据库9:Innodb中的事务隔离级别和锁的关系 Innodb中的事务隔离级别和锁的关系 前言: 我们都知道事务的几种性质,数据库为了维护这些性质,尤其是一致性和隔离性,一般使用加锁 ...

  7. MySQL事务隔离级别,锁(转)

    add by zhj: 本文针对的是MySQL的InnoDB存储引擎,不适用于MySQL的其它存储引擎和其它数据库 原文:MySQL数据库事务隔离级别(Transaction Isolation Le ...

  8. Mysql数据库事务的隔离级别和锁的实现原理分析

    Mysql数据库事务的隔离级别和锁的实现原理分析 找到大神了:http://blog.csdn.net/tangkund3218/article/details/51753243 InnoDB使用MV ...

  9. MySql各事务隔离级别及锁问题

    聊事务隔离级别和锁问题之前首先得理解事务的隔离级别和共享锁及独占锁的概念: 事务的隔离级别:   脏读 不可重复读 幻读 Read uncommitted √ √ √ Read committed × ...

随机推荐

  1. GO gRPC教程-环境安装(一)

    前言 gRPC 是一个高性能.开源和通用的 RPC 框架,面向移动和 HTTP/2 设计,带来诸如双向流.流控.头部压缩.单 TCP 连接上的多复用请求等特.这些特性使得其在移动设备上表现更好,更省电 ...

  2. 剑指Offer系列之题1~题5

    目录 1.二维数组的查找 2.替换空格 3.从尾到头打印链表 4.链表中环的入口节点 5.重建二叉树 写在前面:本随笔中包含五道题:题目描述,题目思路以及对应解法. 1.二维数组的查找 在一个二维数组 ...

  3. Python起步学习

    Python起步学习 案例1:程序输入输出 案例2:判断合法用户 安全3:编写判断成绩的程序 1 案例1:程序输入输出 1.1 问题 编写login.py脚本,实现以下目标: 提示用户输入用户名 将用 ...

  4. C语言数据结构无向图

    #include<stdio.h>#include<stdlib.h>#define num 8struct nearnode{ int order; nearnode* pn ...

  5. Struts2-学习笔记系列(7)-PreResultListener

    在action处理完成之后,系统转入实际的物理试图之间被回调. Action,拦截器都可以添加该监听器.拦截器添加了该监听器后,该监听器会对该拦截器所有拦截的action其作用 public Stri ...

  6. 运行jmeter.bat时 提示 not able to find java executable or version

    安装过好几次,这是第一次遇到运行jmeter.bat时 提示 not able to find java executable or version Please check your Java in ...

  7. 搞懂 XML 解析,徒手造 WEB 框架

    恕我斗胆直言,对开源的 WEB 框架了解多少,有没有尝试写过框架呢?XML 的解析方式有哪些?能答出来吗?! 心中没有答案也没关系,因为通过今天的分享,能让你轻松 get 如下几点,绝对收获满满. a ...

  8. Supermarket POJ - 1456(贪心)

    题目大意:n个物品,每个物品有一定的保质期d和一定的利润p,一天只能出售一个物品,问最大利润是多少? 题解:这是一个贪心的题目,有两种做法. 1 首先排序,从大到小排,然后每个物品,按保质期从后往前找 ...

  9. Python 3.9 性能优化:更快的 list()、dict() 和 range() 等内置类型

    Python 的 3.9.0 版本正在开发中,计划在 2020-10-05 发布 final 版本. 官方在 changelog 中披露了很多细节,其中有一项"vectorcall" ...

  10. 关于JS垃圾回收机制

    一.垃圾回收机制的必要性 由于字符串.对象和数组没有固定大小,所以当它们的大小已知时,才能对它们进行动态的存储分配.JavaScript程序每次创建字符串.数组或对象时,解释器都必须分配内存来存储那个 ...