学习目的
- 了解事务的概念和特性
- 掌握Mysql事务的原理及使用
- 掌握解决Mysql事务并发引起的问题
一.事务
- 概念
事务是数据库管理系统执行过程中最小的逻辑单元,一个事务是一个完整的业务逻辑单元,不可再分。事务由一个有限的数据库操作序列构成,是满足 ACID 特性的一组操作。 - 本质
一个事务 = 一条DML语句。一组(多个) SQL语句组成一个业务逻辑,实现一个业务逻辑的多个事务要么同时全部成功,要么同时全部失败;一个业务逻辑的实现不允许出现一条SQL(事务)成功,一条失败。(若所有的业务都能使用1条DML语句完成,不需要事务机制)。 - 原理
开启事务后,对于执行多个DML操作(insert、update、delete),只要没有提交事务,所有的DML操作都是暂时保存到"操作历史"(日志)当中,不会真正的参与到数据文件中。
- 提交事务:将操作历史当中的所有DML操作同步序列化到数据文件当中,并清楚操作历史的DML;
- 回滚事务:直接将操作历史当中的所有DML操作清楚,不与数据文件进行交流。
- 特性
- 原子性Atomic:事务就是执行过程的最小逻辑单元,不可再分;
- 一致性Consistency:事务必须保证多条DML语句同时成功或者同时失败;
- 隔离性Isolation:事务与事务之间的执行互不影响,互不干扰;
- 持久性Durability:事务中执行的最终数据必须持久化到硬盘文件中,事务才算成功的结束。
- 和事务相关的SQL语句
与事务相关的SQL语句只有DML数据操纵语句(insert delete update),只有对表数据的增加、删除、更新才会出现安全问题,只读(select)语句不会发生数据安全问题。 - 事务出现的原因
数据库自身存在约束:数据长度、主键唯一,用户自定义约束等影响了数据库中数据的安全。
1.1 事务控制语言TCL
- 概念
事务控制语言全称Transaction Control Language,是SQL语言当中对事务进行控制的语句,主要的常用语句是commit(提交事务)和rollback(回滚事务)。 - 分类
- commit:提交事务
- rollback:回滚事务
二.事务并发问题
事务使用的经典业务场景:AOP,
2.1 丢失修改
2.2 读脏数据
2.3 不可重复读
- 概念
不可重复读是指 在一个事务内,多次读同一数据(读取到的值不一样)。 - 原理
在当前事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的前后两次读取数据之间,由于第二个事务的修改,第一个事务两次读到的的数据可能是不一样的。这样发生在一个事务内两次读到的 不一样的数据 称为不可重复读;本质是 原始读取的数据不可重复(不相同)。
例如,一个阅读者前后两次读取同一文档,但在前后分开的两次读取之间,作者重写了该文档,当编辑人员第二次读取文档时,文档已更改。如果在作者全部完成编写后编辑人员才可以读取文档,则可以避免该问题。 - 特点
不可重复读的重点是修改,同样的条件,读取过的数据,再次读取出来的值不一样(不可以重复读取到相同的数据)。
2.4 虚读 / 幻读
- 概念
虚读是指 当事务不是独立执行时发生的一种现象。 - 原理
当一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。 同时,另一个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,就会发生第一个事务的用户发现表中 还有没有修改的数据行,就好象发生幻觉一样。
例如,一个编辑人员更改作者提交的文档,但当生产部门将其更改内容合并到该文档的主复本时,发现作者已将未编辑的新材料添加到该文档中。 如果在编辑人员和生产部门完成对原始文档的处理之前,任何人都不能将新材料添加到文档中,则可以避免该问题。 - 特点
幻读的重点在于新增数据或者删除数据,同样的条件,第 1 次和第 2 次读出来的数据个数(记录条数)不一样。
三.事务隔离级别(解决事务并发的方法)
- 概念
事务的隔离级别是由于事务的隔离性特点而存在的,每一个隔离级别表示了事务之间不同的隔离层次,也因此导致事务之间的隔离性能不同。 - 分类
- 第一级别:读未提交(read uncommitted)
- 第二级别:读已提交(read committed)
- 第三级别:可重复读(repeatable read)
- 第四级别:串行化 / 序列化(serializable)
- 默认隔离级别
- oracle默认隔离级别:第二级别 - - 读已提交。
- mysql默认隔离级别:第三级别 - - 可重复读。
3.1 第一级别:读未提交(read uncommitted)
- 描述
对方事务还没有提交,当前事务可以读取到对方未提交的数据。当对方事务对数据进行修改时,这种修改还没有提交到数据库中(仅保留在"操作历史"日志中),另外一个事务同时访问这个数据便产生了"脏数据",并使用了这个数据。 - 存在问题
读未提交存在脏读现象(Dirty Read),表示读到了"脏"(未提交)的数据。 -
原理
读未提交原理.png - 演示
- 开启两个窗口(同时开启两个事务)
- 在同一个数据库下,针对同一个数据表进行操作演示事务
- 设置两个事务的隔离级别都为第一级别--读未提交:set global transaction isolation level read uncommitted;
- 查看事务的隔离级别:select @@global.tx_isolation;
-
手动开启事务:start transaction;
读未提交演示.png
3.2 第二级别:读已提交(read committed)
- 描述
两个事务同时开启后,对方事务更新了数据,但还未提交,此时当前事务读取到的数据(条数)是更新之前的。在对方事务提交后,当前事务再次读取,此时读取到的就是对方事务提交后(更新后)的数据。
在同一事务过程中,同一事物多次读取到的数据前后是不一致的(前面读取的数据总数或数据内容,和后面读取的数据总数或数据内容不相同),因此又叫"不可重复读"。 - 存在问题
读已提交可以解决脏读现象,但读已提交存在不可重复读问题。 - 原理:事务之间隔离不可见
事务2第一次读取id的value为Null,第二次读取id的value为2,两次读取的结果不相同。
事务2第一次读取id的结果只有一个,而第二次读取id的结果有两个,第三次读取id的结果有三个,多次读取的结果(条数)不相同。
读已提交原理.png - 演示
- 开启两个窗口(同时开启两个事务)
- 在同一个数据库下,针对同一个数据表进行操作演示事务
- 设置两个事务的隔离级别都为第一级别--读未提交:set global transaction isolation level read committed;
- 查看事务的隔离级别:select @@global.tx_isolation;
-
手动开启事务:start transaction;
读已提交.png
3.3 第三级别:可重复读(repeatable read)
- 描述
可重复读指的是在不同的时间,多次读取同一个数据库表,表的总记录数不变,前面一次读取多少条记录,后面也读取得到多少条记录。
对于两个同时开启的事务,当前事务第一次读取得到表中的n条数据,对方事务在当前事务第一次读取后便删除表中的所有n条数据,并提交事务;
当前事务再次(第二次)读取时,依然可以读取到和第一次读取时的n条数据。但是该n条理论上已被对方事务删除,当前事务应该读取到0条才对,但实际还是读出了n条。
因此,虽然可以完成可重复读,但是可重复读取出来的是"假的"、"虚幻的"数据,称为"虚读"或"幻读"。 - 存在问题
可重复读解决了"不可重复读"的问题,但是引起了虚读(幻读)的新问题。 -
原理
可重复读.png - 演示
- 开启两个窗口(同时开启两个事务)
- 在同一个数据库下,针对同一个数据表进行操作演示事务
- 设置两个事务的隔离级别都为第一级别--读未提交:set global transaction isolation level repeatable read;
- 查看事务的隔离级别:select @@global.tx_isolation;
-
手动开启事务:start transaction;
可重复读.png
3.4 第四级别:串行化 / 序列化(serializable)
- 描述
事务串行化类似于线程并发同步。对于同时开启的多个事务,当前事务若正在执行中,其他事务必须等待,只有当前事务结束(提交或回滚),其他事务才能开启执行。不能在当前事务执行的期间强行插入,必须排队等候。 - 存在问题
解决第一、第二、第三级别的所有存在问题,但由于需要排队执行,因此效率较低。 -
原理
串行化.png - 演示
- 开启两个窗口(同时开启两个事务)
- 在同一个数据库下,针对同一个数据表进行操作演示事务
- 设置两个事务的隔离级别都为第一级别--读未提交:set global transaction isolation level serializable;
- 查看事务的隔离级别:select @@global.tx_isolation;
- 手动开启事务:start transaction;