目录
一.事务
1.什么是事务
我们一般所讲的事务,是MySQL中InnoDB存储引擎所支持的事务。而所谓的事件简单的来讲几就是一组特定SQL语句的集合。
2.事务所具有的四个特性
- 原子性:一个事务是一个不可分割的工作单位,事务中包括的操作要么都做(某个语句的集合要么全部执行成功),要么都不做(某个语句的集合要么全部执行失败)。如果某一步执行失败了,事务就会回滚到语句执行前的状态。
- 一致性:完整性约束,数据完整性约束指的是为了防止不符合规范的数据进入数据库,在用户对数据进行插入、修改、删除等操作时,DBMS自动按照一定的约束条件对数据进行监测,使不符合规范的数据不能进入数据库,以确保数据库中存储的数据正确、有效、相容。
- 隔离性:一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
- 持久性:事务执行的结果在磁盘上永久的保存,指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。
这四个属性通常称为ACID特性。
3.原子性
日志系统中有两个日志,分别是
- 重做日志(redo log)
- 未做日志(undo log)
首先事务在提交以后,重做日志先记录这个事务将要执行的操作,接着进行日志先行,就是将现在重做日志中存储的信息刷新到磁盘上,接着才开始执行事务。假如事务A执行到某一步骤时,系统断电了,但恢复电力后可以通过重做日志来查看事务A一共进行了哪些操作以及执行到了哪一个步骤。即重做日志保证了事务全部执行成功。
未做日志用来记录了每一个修改点的状态(版本),通俗的理解为每一条数据被修改前和修改后的值。这样的话,就可以进行事物的回滚了(回到数据一开始的状态)。即未做日志保证了事务全部执行失败。
通过这两个日志就保证了事务的原子性。
4.一致性
与原子性密切相关,但是一致性是一个很难理解的概念,如有兴趣大家可以参考一下这篇博客:
https://blog.csdn.net/qq_26295547/article/details/79828966
以及知乎的相关回答:
https://www.zhihu.com/question/31346392
5.隔离性
如果没有隔离性,事务执行过程中会出现什么问题?
出现脏读,例如现在在数据库的某张表中有这样一条数据
name | salary |
zhangsan | 5000 |
这条数据的意思是zhangsan的工资为5000。现在有一个事务A开始执行,首先事务A进行以下操作
/*A---Begin*/
select *
from table_name
where salary = 5000;
updata table_name
set salary = 8000
where name = zhangsan;
当执行到这里时,事务A已经将zhangsan的工资修改为了8000,但此时因为时间轮片法的规则,给事务A分配的时间到了(此时事务A的执行并没有结束),而此时系统开始执行另一个事务B,事务B进行了下面操作
/*B-----Begin*/
select *
from table_name
where salary = 8000;
此时事务B查找到的结果是 zhangsan的工资为8000,接着事务B又对这一条数据进行了其他一系列操作,因为时间轮片法的规则,给事务B分配的时间到了(此时事务B的执行并没有结束),此时系统又开始执行事务A,若此时事务A又对这条数据进行了其他操作,但某一步执行失败了,根据事务的原子性,事务A就要回滚到语句执行前的状态。回滚后 zhangsan的工资又变成了5000,而此时另一个事务B所有的操作都是建立在 zhangsan 的工资为8000的基础上,这是事务B所读到的数据都是脏数据,而基于脏数据所进行的操作都是错误的,这种情况就是脏读。
/*用户自己执行回滚*/
roolback;
二.脏读
1.什么是脏读
脏读:事务A修改了一个数据,但未提交,事务B读到了事务A未提交的更新结果,如果事务A提交失败,事务B读到的就是脏数据。
2.导致脏读发生的情况
事务执行过程中获取到其他事务执行过程中的结果。
三.不可重复读
1.不可重复读
还是同样的数据
name | salary |
zhangsan | 5000 |
同样事务A执行以下操作
/*A---Begin*/
select *
from table_name
where salary = 5000;
updata table_name
set salary = 8000
where name = zhangsan;
接着事务B开始执行(此时事务A并没有执行结束),事务B进行了以下操作
/*B-----Begin*/
select salary
from table_name
where name = zhangsan;
此时事务B得到的结果是 zhangsan的工资为5000,接者系统又开始执行事务A,事务B暂时被挂起,而此时事务A什么操作都没有进行,接这事务A就结束了。
/*A------End*/
而系统又开始执行事务B,若现在事务B有查询了一次 zhangsan 的工资
select salary
from table_name
where name = zhangsan;
而此时,zhangsan的工资已经被事务修改为了8000,所以事务B的这次查询操作得到的结果就是zhangsan的工资为8000,我们发现B事务什么事情都没有做,只是对同一条数据进行了两趟查询操作,却拿到了两条不同的数据。这种问题就被称为不可重复读(在同一个事务中,对于同一份数据读取到的结果不一致。)
2.不可重复读问题发生的情况
事务在执行过程中获取到了其他不同阶段的结果,是修改(updata)操作导致的。
3.解决不可重复读问题的方法
不可重复读出现的原因就是事务并发修改记录,要避免这种情况,最简单的方法就是对要修改的记录加锁,这回导致锁竞争加剧,影响性能。另一种方法是通过MVCC可以在无锁的情况下,避免不可重复读。
四.幻读
/*A-----Begin*/
insert into table_name
values(lisi,5000);
/*B-----Begin*/
select COUNT(name)
from table_name
where salary = 5000;
/*A-----End*/
select COUNT(name)
from table_name
where salary = 5000;
/*B-----End*/
我们看到,首先执行事务A,事务A向这张表里插入了一条数据,接着事务A被挂起,事务B开始执行,事务B查询了工资为5000的员工的个数,的到的结果是1,接着事务B被挂起,事务A有开始执行,此时事务A没有进行其他操作,而是事务A执行完毕并关闭,接着事务B又开始执行,此时事务B又查询了工资为5000的员工的个数,但得到的结果是2,。
1.什么是幻读
在同一个事务中,同一个查询多次返回的结果不一致。
2.幻读发生的情况
事务执行过程中获取到了其他事务不同阶段的结果,是由插入(insert)和删除(delete)导致的。
3.解决方法
间隙锁,幻读是由于并发事务增加记录导致的,这个不能像不可重复读通过记录加锁解决,因为对于新增的记录根本无法加锁。需要将事务串行化,才能避免幻读。
五.持久性
通过日志来保证事务的持久性。
六.隔离级别
1.隔离级别的分类
- 未提交读(READ-UNCOMMITTED),最低的隔离级别,在这个隔离级别下会出现脏读,不可重复读,幻读的问题。
- 已提交读(READ-COMMITTED),SQL Server默认的隔离级别就是已提交隔离级别。在这个隔离级别下会出现 不可重复读,幻读的问题,可以解决脏读的问题。
- 可重复读(REPEATABLE-READ),InnoDB存储引擎默认的隔离级别就是可重复读隔离级别。在这个隔离级别下会出现幻读的问题,可以解决脏读吧,不可重复读问题。
- 可序列化,隔离级别最高,不会出现上面所述的任何问题,但是效率太低,因为在这种隔离级别下事务之间的关系是串型的,牺牲了系统的并发性。
可以通过下面命名查看当前系统的隔离级别
select @@tx_isolation;
将隔离级别设置为 未提交读 隔离级别
set tx_isolation = "READ-UNCOMMITED";
2.隔离级别的划分
- 全局隔离级别:默认为REPEATABLE-READ,存放在服务器端。
- 会话隔离级别: 会话隔离级别是复制的服务器端的隔离级别。而客户端看到的都是会话隔离级别。所以就算用户将某一会话的隔离级别改变了,其他会话的隔离级别不会改变。