MySqL中的事物
1、什么是事物
- 事物是一组操作的集合,或者说是一组SQL语句的集合
- 主要用于处理操作量大的,复杂度高的数据
2、为什么要使用事物
- 事物要保证成批的SQL语句全部执行,要么全都不执行,这样可以保证数据库的完整性
- 事物可以用来管理增删改这些改变数据的语句
3、事物的特性(ACID)
- 原子性:事物是操作的最小单位,事物中的操作要么都执行要么都不执行,不可拆分
- 一致性:事物的操作数据库中的数据,只会使数据库从一个一致的状态到另一个一致的状态
- 隔离性:事物的执行之间互不影响(与隔离级别有关)
- 持久性:事物一旦执行完毕后,对数据库是永久性的改变,不可恢复。
事物的操作
事物默认是开启的可以通过show variables like 'autocommit’SQL语句查看
- 事物的创建
- 隐式事物:事物没有开始和结束的标记,比如一条insert,update,delete。也就是说一条SQL语句默认就有隐式事物。
- 显式事务:事物有开始和结束的标记。比如在一组sql语句的最前面和最后面添加开始和结束的标记
#首先关闭自动开启的事物
set autocommit = 0;
#开始事物
start transaction; #可以不写
#编写多条SQL语句
...
# 关闭事物提交和回滚都会关闭事物根据情况写一条
# 提交事物
commit; # 或者rollback;
事物中的问题
我们可以试想一下,当有多个事物同时对一个数据进行操作的时候,这些事物的操作有些提交,有些没有提交,这就会导致其他事物操作的数据出现问题。
事物的并发问题
- 脏读:假设有两个事物,一个事物对一条数据进行了更新操作,没有提交,这时另一个事物又来读取了这条数据,这时候该事物读到的数据就是无效的。
举个例子:小明的老婆每个月都会给小明100块零花钱,但是有一次小明的老婆操作失误给小明打了200块零花钱,小明查看银行卡余额发现多了100。这时候小明读到的数据就是脏数据。(小明的老婆在之后会要回这100块钱)
- 不可重复读:假设有两事物T1、T2,T1事物读取了一条数据,这时候T2事物修改了该条数据,T1在来读取该条数据就会发现值不相同了。
- 幻读:假设有两个事物T1、T2,T1从一个表中读取了该表中数据的条数,T2在该表中插入几条数据,这时候T1在来读该表,机会发现数据的条数多了几行。
事物中的并发问题有些向多线程中出现的问题,我们在解决多线程中出现的问题时用到了锁,我们在解决事物并发问题时用到了隔离级别。
事物的隔离级别
MySQL中事物有四种隔离级别
- read uncommitted:读未提交数据,允许事物读取未提交的数据,三种并发问题都会出现
- read committed:读已提交数据,只允许事物读取已经被提交的数据,解决脏读
- repeatable read:可重复读,事物可以多次从一个字段中读取相同的值,在这期间禁止任何其他事物的操作。解决脏读和不可重复读(默认)
- serializable:串行化,事物可以从一个表中读取相同的行,在这期间禁止插入、更新、删除操作,解决所有问题。但是效率低下
四种隔离级别的演示
首先要知道几种常见的事物操作
- 查看隔离级别
# mysql版本8.0用
select @@transaction_isolation;
# mysql版本5.0用
select @@tx_isolation;
- 设置隔离级别
# 设置当前隔离级别
set session transaction isolation level 隔离级别;
# 设置全局隔离级别
set global transaction isolation level 隔离级别;
开启两个命令行窗口,都登陆MySQL,然后进行隔离级别的演示。使用user表,User表结构如下
- read uncommitted
# 步骤一修改隔离级别为read uncommitted
set session transaction isolation level read uncommitted;
# 步骤二开启事物,并更新name字段,单不结束事物
set autocommit = 0;
update user set name = 'lucy' where id = 1;
# 步骤三打开另外一个命令行窗口也设置隔离级别为read uncommitted ,查询表中的数据
set session transaction isolation level read uncommitted;
select * from user;
-
read committed
验证步骤和上个隔离级别相似,也是设置隔离级别后更新数但不提交数据,在另一个窗口也设置隔离级别后查询同一张表,发现name没有更新。解决了脏读。
但是不可重复读的问题还是会出现。我们在其中的一个窗口开启事物查询user表发现字段是mike,然后在另一个窗口中修改name的值为john,并提交,时候在在另一个窗口中查询发现name的值在一个事物中不一样了。
-
repeatable read
设置隔离级别为repeatable read,先在一个命令行窗口中开启一个事物并查询user表但不结束事物,在另一个表中更改name并提交事物。返回查询user表的窗口再次查询发现name没有改变。
-
serializable
更改隔离级别为serializable,现在一个命令行窗口中开启一个事物,然后查询user表但不提交事物。这时候在另外一张表中开启事物后向user表中插入新的一条数据,发现user表中的数据插入不进去。一直等待另一个窗口中的事物提交。
事物中的保存点
语法:savepoint [保存点名] ;
注意:savepoint关键字需要搭配rollback关键字使用
例子:
事物中需要注意的小细节
1.在事物中使用truncate清空表中的数据,使用rollback回滚是无效的
set autocommit = 0;
start transaction;
truncate table user;
rollback;