本文正在参加「技术专题19期 漫谈数据库技术」活动
学习数据库事务之后,想要加深印象,更好的理解数据库事务中的隔离级别。故记下学习笔记。 验证前提-SQL
# 查看事务隔离级别
1. select @@transaction_isolation;
2. show variables like 'transaction_isolation';
# 修改事务隔离级别
set global transaction isolation level "隔离级别的英文写法"
# 显示的开启一个事务
start transaction;
......
commit;
# 自动提交:如果执行一个select语句,事务就开始了,并且会帮你自动提交
# 建议是设置成自动提交(那么下面的测试就需要自己显示的启动的一个事务)
show variables like 'autocommit';
# 关闭自动提交
set autocommit=0;
@[TOC]
什么是数据库事务
简单来说,事务就是要保证一组数据库操作,要么全部成功,要么全部失败。MySQL 事务支持是在引擎层实现的。因此说 Innode 支持事务,MyISAM 不支持事务。
事务的四大特性
原子性(Aotomicity) 隔离性(Isolation) 一致性(Consistency) 持久性(Durability) 本文主要记录隔离性的学习笔记
隔离级别
当数据库上有多个事务同时执行的时候,就可能出现**脏读(dirty read)、不可重复读(non-repeatable read)、幻读(phantom read)**的问题。为了解决这些问题,引入了隔离级别的概念。
- 脏读:一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。
- 不可重复读:是指在一个事务内,多次读同一数据,在这个事务还没结束时,另外一个事务也访问该数据(修改),那么这个事务在当前事务中,读取到的数据不一致。「即,一个事务两次读取同一个数的值是不一样的」
- 幻读:在同一个事务中,前一个读取记录数和后一个读取记录数不一致的情况(不可重复读是针对同一行记录)。
- 一个例子:在 RR 隔离级别下,当一个事务查询一次,未命中,那么需要插入此条记录,但是另一个事务此时插入了。那么第一个事务插入的操作就会失败(比如主键冲突啥的),而且第一个事务往下查询,都是为空(RR 级别的特性)
SQL 标准的事务隔离级别包括:读未提交(read uncommitted)、读已提交(read committed)、可重复读(repeatable read)、串行化(serializable)。
- 读未提交:一个事务还没提交时,它做的变更就能被别的事务看到。
- 读已提交:一个事务提交完之后,它做的变更才会被其他事务看到。
- 可重复读:一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。
- 串行化:对于同一条记录,“写”会“加锁”,“读”会加“锁”。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。
本地验证
现在在本地验证上述 MySQL 隔离级别的解释。 登录本地MySQL很简单的,也以自行百度:
mysql -hlocalhost -uroot -p -P3306 #连接数据信息
show databases; #查看你得数据库
use database_name;# 进入具体的数据库
读未提交(出现脏读)
进入到我们准备的数据库里面:(需要用到两个窗口「两个会话」)
A 窗口:
B 窗口:
查看当前的隔离级别:是读未提交;并且在开启一个事务进行更新操作;并且不提交。
此时回到 A 窗口再次查询 ,数据已然改变。
此时,我们直接退出 B 窗口:数据又变回来了,说明更新并没有真正执行。「自己也可验证」。
读已提交(解决脏读)
同样准备两个窗口:
A 窗口:
B 窗口:
再回到 A 窗口:没有发生变化
所以读已提交解决了脏读的问题。
可重复读(解决不可重复读)
注意下面验证的先后顺序。不然你可能会认为出现‘错误‘哦
打开一个 A 窗口,开启一个事务,先查询一次:
这里我们在查询的时候就显示的打开了一个事务,这是必须的。因为数据库事务默认是自动提交的,你直接运行一个查询语句,那么会直接帮你提交事务,那么测试结果就会出现问题
在 B 窗口下进行一次完整的更新事务操作:
回到 A 窗口继续查询:B 事务已经提交的情况下,A 事务中仍然还是刚开始的时候的状态。
这就是可重复读的定义:一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。
当提交 A 窗口的事务之后,再次进行查询结果:
串行化(解决幻读)
此种方法隔离性是最高的,设置数据库的隔离级别之后就生效。自己测试的话,可以使用可重复读和串行化两种隔离级别对比测试。
--- 2021-07 --- 时隔一年,重新看此文章的时候,发现当时可能偷懒,然后没给出案例,特此再次记录。
- 首先查看事务类型,并保证为 RR 型
- 打开两个窗口,为了开启两个事务
开启事务1:先查询表 student ,假设符号某个逻辑,需要插入一条数据,对应 step1。此时 事务2 插入了一条数据, 对应 step2 。
此时step2已经完成,step3进行插入,报错(主键冲突)。再次select,返现只有一条数据(RR 型事务保证一个事务内,所有查询看到的东西和事务开始的时候是一样的)。
开启事务2:插入一条数据,并且提交。
所以,这就出现问题了,我事务一中,查看完全没问题,但是插入就会报主键冲突。对于事务一就发生幻读。事务一不管查询多少篇,都是”满足业务“的,但是进行后续操作就失败了。
解决:使用串行化(这里主要是讨论事务-隔离性的几个特性)。具体就是事务2在插入的时候会挂起,在事务一提交后才会执行。如果主键一致,那么事务2会报错。事务1的整个逻辑就成功了。「串行化就是给事务默认加了一个锁」。
总结
先来一张事务中可能出现的问题吗,以及对应的解决方法。
可重复读:针对的是一行记录,改变某个字段的值,我们可以在一个事务内,对其查询结果保持不变。
串行化:针对的比较大范围,比如表的记录数,我先查询一个总数,另外一个事务新增或者删除行数据之后再次进行读取,如果不是串行化(相当于对表加锁),那么就会出现前后数据不一致的情况。
总的来说,这样下来,学习一遍,自己在总结一遍,感觉理解的更多了。