Mysql事务隔离级别
事物的个隔离级别是非常重要的概念,Mysql的隔离级别有以下几种
- 读未提交读
在所有事物中可以看到事物没有提交的结果,实际应用中是很少的,他的性能也不比其他隔离级别好很多,读到未提交的结果导致脏读
- 读已提交度
大多数据库的默认隔离级别,但是不是mysql的默认级别,一个事物只能看到已经提交的结果,他也支持不可重复读,在同一个事物的其他实例在该实例中修改的数据,导致两次select的结果可能不一样
- 可重复读
mysql的默认隔离级别,在事务开始的时候,直到事务结束看到的行的数据都是一样,这种隔离级别是会产生幻读,幻读就是在用户读取某一范围的数据时候,其他事物新增了一条数据,用户再次读取的时候,返现多了一行数据(幻读是指读到了其他事务的inset,不可重复读是指读到了其他事物的delete/update)
- 串行化 这种隔离级别就是使事物严格按照顺序执行,就是在每一行数据上加上锁,保证了事物不可冲突,避免了幻读,脏读,不可重复读,但是增加了锁超时和锁竞争
之前我们说过在可重复读级别下,事物T开始的时候会创建一个read-view,之后再事物T期间的其他事物修改了数据,对于事务T也是不可见的,但是在我上一篇说过,一个事物在修改一行数据的时候,发现这行数据已经被行锁锁住了,这个时候只能等待行锁被释放,但是在释放之后,他读取的值有事什么呢
首先,我们看一下例子如下建表语句
mysql> CREATE TABLE `t` (
`id` int(11) NOT NULL,
`k` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
insert into t(id, k) values(1,1),(2,2);
看看上面的例子,我们看看事物A 和事物B查询到的结果是什么,此时你老想一下,(没有特别说明,默认都是autocommit=1)
事物A的结果是1,而事物B的结果是3,不知道是否和你的答案一致呢,
为什么会是这样呢,首先我们得看看数据库的一个概念视图,数据库中有定义了两种视图
- 一个视图view是在一个查询语句生成的一个虚拟表,在调用查询语句并生成结果,创建的语句可用 create view ,查询方式和表的方式一样
- 另一个视图则是innodb中的一致性视图,consistent read view ,用于隔离级别读已提交和可重复读的实现
快照在MVCC中是如何使用的呢
在开启一个事物的时候,就会拍个照,这个快照是对于整个库的,但是我们想象,对于一个100G的数据库,我们不可能把所有的数据全部复制一次,但是为什么开始一个事物还是很快呢,
实际上每一个事物的开启,都会创建一个事物id ,transaction id,他是向数据库申请,严格按照顺序递增的,而每一行数据都是有多个版本的,每一个事物更新数据后都会把此时的事物id,赋值给每一个版本的事物id,即row tx_id,并且保留旧的数据,并且在新的数据版本都可以直接拿到旧版本的数据,正如下图,一行数据多次更新的版本状态
如上图显示,目前最新的版本是k=22,他是的事物id=15,因此他的row trx_id=15,之前我们也提到过,语句的更新会产生undo log,其实上面的三个实线箭头就是undo log,而实际上v1,v2,v3不是真实存在的,而是有当前版本和undo log计算出来的。
在可重复读隔离级别,我们知道在事物启动的时候,只能看到事物启动前提交的数据,之后生成的版本我们是不认的,当然自己修改的数据还是要认的,
在实际应用中,每一个事物都会有一个数组,数组保存的是当前系统活跃的事物id,活跃的是指,启动但未提交的事物id。
- 低水位,是指数组中最小的事物id
- 高水位,是指数组中最大的事物id+1
一致性视图是有视图数组和高水位组成如下图
数据的可见性是根据数据的row trx_id和一致性视图判断的
这样,当一个事物启动的瞬间,row trx_id可能有以下几种情况
- 如果落在绿色部分,表示事物已经提交,对当前事物可见
- 如果落在红色部分,表示事物未启动,对当前事物不可见
- 如果在黄色部分有两种情况
- 如果在视图数组中,表示事务没有提交,可见
- 如果不在视图数组中,表示事务已经提交,不可见
到这里,我们回过来看看开头我们的问题,为什么事务A的k=1,而事物B的k=3
此时假设一下如下
- 事物A开启前,只有一个活跃事务row trx_id=99
- 事物A,B,C的版本分别是100,101,102
- 三个事物开启前此时(1,1)row trx_id=90
因此根据上面的可见性规则判断如下
上图中看到事物c先把(1,1)更新成了(1,2)此时row trx_id=102,然后事物B更新(1,2)为(1,3),此时的row trx_id=101,
此时我们来看看事物A查询的数据如何获取
- 此时事务A的视图数组是[99,100]
- 找到当前版本(1,3)此时的row trx_id=101,落在了红色部分,不可见
- 向上寻找上一个版本(1,2)此时的row trx_id=102,落在红色部分,不可见
- 向上寻找上一个版本(1,1),此时row trx_id=90,落在了绿色部分,可见
- 因此此时的k=1
上面的判断是从代码逻辑进行判断,其实我们可以按照下面规则进行判断
- 版本未提交,不可见
- 版本提交,是视图创建后,不可见
- 版本提交,是视图创建前,可见
我们也验证一下上面的规则如下
- (1,3)版本未提交,不可见
- (1,2)版本提交,但是视图创建后提交,不可见
- (1,1)版本提交,且是视图创建前提交,可见
但是有人会发现事物B 的update语句感觉是不有问题呢,为什么会是在(1,2)基础上进行增加的,事物C的视图不是后面才创建的吗
如果不是按照历史版本更新的话,事物c的更新不是就丢失了吗,导致读到的数据是脏读,那究竟是为什么的,这里我们要加一条规则,uodate的时候,是先读在写的,而这个读必须读取当前值,这种叫做当前读,
除了update语句外,我们的select 如果加锁,也是使用当前读,如果使用下面语句读取到的k=3
select k from t where id =1 lock in share mode
select k from t where id =1 for update
我们再进一步分析事务C如果不是自动提交,而是在下面的事务C1
此时(1,2)已经生产,但是事物没有进行提交,那么事物B的更新语句如何执行,这就要提到了上一节说的行锁,此时id=1被行锁锁住,事物B的当前读,必须等待id=1的行锁释放后,才能使用当前读。
可重复读的核心就是一致性视图,更新的时候只能用当前读,如果当前记录被行锁锁定,必须等待释放,再去更新,
读已提交和不可重复读的逻辑类似,主要有以下区别
- 在可重复读下,事物创建的建立的一致性视图,之后的其他语句通用这一个一致性视图
- 读已提交,是事物中每一个语句都会新建一个一致性视图
我们再来看看读已提交的情况,如下图
事物A在获取查询语句的时候创建视图,(1,3)(1,2)此时生成的时刻是在创建视图之前,因此
- (1,3)此时还没有提交,不可见
- (1,2)此时已经提交,可见
- 因此事物A,k=2
- 显而易见事务B,k=3
- SDP(3):ScalikeJDBC- JDBC-Engine:Fetching
- SDP(2):ScalikeJDBC-Connection Pool Configuration
- 使用外部表关联MySQL数据到Oracle(r6笔记第100天)
- 使用selenium模块模拟浏览器爬去网页,并进行点击定位内容笔记
- python 报错'gbk' codec can't encode character 'ue5d1' in position 0:
- python文件打开方式详解——a、a+、r+、w+区别
- 三封报警邮件的分析(r6笔记第95天)
- HCTF2017 部分 Web 出题思路详解
- 基于springboot+kotlin+gradle构建的框架的坑
- 创建Task的多种方法
- 运行map()后,报:<map object at 0x02629E50>解决方法与原因分析
- Gradle的快速入门
- Python笔记从html中提取字段
- 一条细小的报警短信的处理(r6笔记第96天)
- MySQL 教程
- MySQL 安装
- MySQL 管理与配置
- MySQL PHP 语法
- MySQL 连接
- MySQL 创建数据库
- MySQL 删除数据库
- MySQL 选择数据库
- MySQL 数据类型
- MySQL 创建数据表
- MySQL 删除数据表
- MySQL 插入数据
- MySQL 查询数据
- MySQL where 子句
- MySQL UPDATE 查询
- MySQL DELETE 语句
- MySQL LIKE 子句
- mysql order by
- Mysql Join的使用
- MySQL NULL 值处理
- MySQL 正则表达式
- MySQL 事务
- MySQL ALTER命令
- MySQL 索引
- MySQL 临时表
- MySQL 复制表
- 查看MySQL 元数据
- MySQL 序列 AUTO_INCREMENT
- MySQL 处理重复数据
- MySQL 及 SQL 注入
- MySQL 导出数据
- MySQL 导入数据
- MYSQL 函数大全
- MySQL Group By 实例讲解
- MySQL Max()函数实例讲解
- mysql count函数实例
- MYSQL UNION和UNION ALL实例
- MySQL IN 用法
- MySQL between and 实例讲解
- React-Router 5.0 制作导航栏+页面参数传递
- Vue3.0快速入门(速查)
- 憧憬博客Nginx到Tengine的迁移
- SpringCloud微服务构建浅析
- 宋宝华:Linux设备与驱动的手动解绑与手动绑定
- ELK7.x日志系统搭建 1. elk基础搭建
- 腾讯云直播开发日记 (二)附近直播-直播礼物-直播回放
- 腾讯云直播开发日记(三) 聊天室-直播转码-连麦混流
- c#类(class)
- es 7.2 生产集群 index 无数据写入故障定位
- HashMap都在用,原理你真的了解吗?
- Android必知必会--事件分发机制
- 又被逼着优化代码,这次我干掉了出入参 Log日志
- C#委托进阶,事件和委托,一次就看明白,附源码
- 树莓派爬虫多平台热搜榜轮播展示