MySQL-事务基础
事务概念
- 事务:一组逻辑操作单元,使数据从一种状态变换到另一种状态
- 事务处理的原则:保证所有事务都作为一个工作单元来执行,即使出现了故障,都不能改变这种执行方式。当在一个事务中执行多个操作时,要么所有的事务都被提交(commit),那么这些修改就永久地保存下来;要么数据库管理系统将放弃所作的所有修改,整个事务回滚(rollback)到最初状态
存储引擎
- 只有InnoDB 是支持事务
SHOW ENGINES;
ACID特性
1. 原子性(atomicity)
- 原子性是指事务是一个不可分割的工作单位,要么全部提交,要么全部失败回滚
2. 一致性(consistency)
- 一致性是指事务执行前后,数据从一个 合法性状态 变换到另外一个 合法性状态
- 合法的数据状态:满足 预定的约束 的状态就叫做合法的状态。满足这个状态,数据就是一致的,不满足这个状态,数据就是不一致的!如果事务中的某个操作失败了,系统就会自动撤销当前正在执行的事务,返回到事务操作之前的状态
3. 隔离型(isolation)
- 隔离性是指一个事务的执行 不能被其他事务干扰 ,即一个事务内部的操作及使用的数据对 并发 的其他事务是隔离的,并发执行的各个事务之间不能互相干扰
4. 持久性(durability)
- 持久性是指一个事务一旦被提交,它对数据库中数据的改变就是 永久性的 ,接下来的其他操作和数据库故障不应该对其有任何影响
- 持久性是通过 事务日志 来保证的。日志包括了 重做日志 和 回滚日志
事务的状态
- 活动的(active):事务对应的数据库操作正在执行过程中
- 部分提交的(partially committed):当事务中的最后一个操作执行完成,但由于操作都在内存中执行,所造成的影响并没有刷新到磁盘
- 失败的(failed):当事务处在 活动的 或者 部分提交的 状态时,可能遇到了某些错误(数据库自身的错误、操作系统错误或者直接断电等)而无法继续执行,或者人为的停止当前事务的执行
- 中止的(aborted):如果事务执行了一部分而变为 失败的 状态,那么就需要把已经修改的事务中的操作还原到事务执行前的状态。换句话说,就是要撤销失败事务对当前数据库造成的影响。我们把这个撤销的过程称之为 回滚 。当 回滚 操作执行完毕时,也就是数据库恢复到了执行事务之前的状态
- 提交的(committed)当一个处在 部分提交的 状态的事务将修改过的数据都 同步到磁盘 上
显式事务
1. 显式开启一个事务
BEGIN;
#或者
START TRANSACTION;
2. START TRANSACTION 的修饰符
- READ ONLY :标识当前事务是一个 只读事务 ,也就是属于该事务的数据库操作只能读取数据,而不能修改数据;只可以对临时表进行增、删、改操作
- READ WRITE :标识当前事务是一个 读写事务 ,也就是属于该事务的数据库操作既可以读取数据,也可以修改数据
- WITH CONSISTENT SNAPSHOT :启动一致性读
3. 事务操作完成后
- 提交事务:commit
- 中止事务:rollback
4. 保存点的使用
- 在事务中创建保存点,方便后续针对保存点进行回滚
- 一个事务中可以存在多个保存点
- 此时rollback事务并没有处于中止状态。还需要使用rollback或者commit
#创建保存点
SAVEPOINT 保存点名称;#删除某个保存点
RELEASE SAVEPOINT 保存点名称;# 回滚到保存点
ROLLBACK TO [SAVEPOINT]
隐式事务
1. 关闭自动提交功能
- 显式的开启一个事务。这样在本次事务提交或者回滚前会暂时关闭掉自动提交的功能
- 把系统变量 autocommit 的值设置为 OFF
- Oracle默认不自动提交,需要手写COMMIT命令,而MySQL默认自动提交
SET autocommit = OFF;
#或
SET autocommit = 0;#查看
SHOW VARIABLES LIKE 'autocommit';
隐式提交数据的情况
1. 数据定义语言(DDL)
- 数据库对象,指的就是数据库、表、视图、存储过程等结构
- 当我们使用CREATE、ALTER、DROP等语句去修改数据库对象时,就会隐式的提交前边语句所属于的事务
2. 隐式使用或修改mysql数据库中的表
- 当使用ALTER USER、CREATE USER、DROP USER、GRANT、RENAME USER、REVOKE、SET PASSWORD等语句时也会隐式的提交前边语句所属于的事务
3. 事务控制或关于锁定的语句
- 当我们在一个事务还没提交或者回滚时就又使用 START TRANSACTION 或者 BEGIN 语句开启了另一个事务时,会 隐式的提交 上一个事务
- 当前的 autocommit 系统变量的值为 OFF ,我们手动把它调为 ON 时,也会 隐式的提交 前边语句所属的事务
- 使用 LOCK TABLES 、 UNLOCK TABLES 等关于锁定的语句也会 隐式的提交 前边语句所属的事务
4. 加载数据的语句
- 使用LOADDATA语句来批量往数据库中导入数据时,也会隐式的提交前边语句所属的事务
5. 关于MySQL复制的一些语句
- 使用START SLAVE、STOP SLAVE、RESET SLAVE、CHANGE MASTER TO等语句时会隐式的提交前边语句所属的事务
6. 其它的一些语句
- 使用ANALYZE TABLE、CACHE INDEX、CHECK TABLE、FLUSH、LOAD INDEX INTO CACHE、OPTIMIZE TABLE、REPAIR TABLE、RESET等语句也会隐式的提交前边语句所属的事务
7. completion_type 参数
- completion=0,这是默认情况。当执行COMMIT的时候会提交事务,在执行下一个事务时,还需要使用START TRANSACTION或者BEGIN来开启
- completion=1,这种情况下,当提交事务后,相当于执行了COMMIT AND CHAIN,也就是开启一个链式事务,即当我们提交事务之后会开启一个相同隔离级别的事务。
- completion=2,这种情况下COMMIT = COMMIT ANDRELEASE,也就是当我们提交后,会自动与服务器断开连接
隔离级别
1. 数据并发问题
- 脏写( Dirty Write ):对于两个事务 Session A、Session B,如果事务Session A 修改了 另一个 未提交 事务Session B 修改过 的数据,那就意味着发生了 脏写
- 脏读( Dirty Read ):对于两个事务 Session A、Session B,Session A 读取 了已经被 Session B 更新 但还 没有被提交 的字段。之后若 Session B 回滚 ,Session A 读取 的内容就是 临时且无效 的
- 不可重复读( Non-Repeatable Read ):对于两个事务Session A、Session B,Session A 读取 了一个字段,然后 Session B 更新 了该字段。 之后Session A 再次读取 同一个字段, 值就不同 了
- 幻读( Phantom ):对于两个事务Session A、Session B, Session A 从一个表中 读取 了一个字段, 然后 Session B 在该表中 插入 了一些新的行。 之后, 如果 Session A 再次读取 同一个表, 就会多出几行。幻读只是重点强调了读取到之前读取没有获取到的记录。读取的数据变少了不属于幻读
2. SQL隔离级别
- READ UNCOMMITTED :读未提交,在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。不能避免脏读、不可重复读、幻读
- READ COMMITTED :读已提交,它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。可以避免脏读,但不可重复读、幻读问题仍然存在
- REPEATABLE READ :可重复读,事务A在读到一条数据之后,此时事务B对该数据进行了修改并提交,那么事务A再读该数据,读到的还是原来的内容。可以避免脏读、不可重复读,但幻读问题仍然存在。这是MySQL的默认隔离级别
- SERIALIZABLE :可串行化,确保事务可以从一个表中读取相同的行。在这个事务持续期间,禁止其他事务对该表执行插入、更新和删除操作。所有的并发问题都可以避免,但性能十分低下。能避免脏读、不可重复读和幻读
3. MySQL查看隔离级别
# 查看隔离级别,MySQL 5.7.20的版本之前:
SHOW VARIABLES LIKE 'tx_isolation';# 查看隔离级别,MySQL 5.7.20的版本及之后:
SHOW VARIABLES LIKE 'transaction_isolation';#或者不同MySQL版本中都可以使用的:
SELECT @@transaction_isolation;
4. 设置事务的隔离级别
- READ UNCOMMITTED
- READ COMMITTED
- REPEATABLE READ
- SERIALIZABLE
- 使用GLOBAL:
- 当前已经存在的会话无效,只对执行完该语句之后产生的会话起作用
- 数据库重启之后,恢复默认隔离级别
- 使用 SESSION:
- 对当前会话的所有后续的事务有效
- 如果在事务之间执行,则对后续的事务有效
- 该语句可以在已经开启的事务中间执行,但不会影响当前正在执行的事务
SET [GLOBAL|SESSION] TRANSACTION ISOLATION LEVEL 隔离级别;# 隔离级别中的空格用-代替
SET [GLOBAL|SESSION] TRANSACTION_ISOLATION = '隔离级别'
事务分类
1. 扁平事务(Flat Transactions)
- 由BEGIN WORK开始,由COMMIT WORK或ROLLBACK WORK结束,其间的操作是原子的,要么都执行,要么都回滚
- 扁平事务的主要限制是不能提交或者回滚事务的某一部分,或分几个步骤提交
- 有三种不同的结果:
- 事务成功完成
- 应用程序要求停止事务。比如应用程序在捕获到异常时会回滚事务
- 外界因素强制终止事务。如连接超时或连接断开
2. 带有保存点的扁平事务(Flat Transactions with Savepoints)
- 支持扁平事务支持的操作外,还允许在事务执行过程中回滚到同一事务中较早的一个状态
- 保存点(Savepoint)用来通知事务系统应该记住事务当前的状态,以便当之后发生错误时,事务能回到保存点当时的状态
3. 链事务(Chained Transactions)
- 链事务是指一个事务由多个子事务链式组成,它可以被视为保存点模式的一个变种。带有保存点的扁平事务,当发生系统崩溃时,所有的保存点都将消失,这意味着当进行恢复时,事务需要从开始处重新执行,而不能从最近的一个保存点继续执行
- 链事务的思想是:在提交一个事务时,释放不需要的数据对象,将必要的处理上下文隐式地传给下一个要开始的事务,前一个子事务的提交操作和下一个子事务的开始操作合并成一个原子操作。在提交子事务时就可以释放不需要的数据对象,而不必等到整个事务完成后才释放
4. 嵌套事务(Nested Transactions)
- 嵌套事务是一个层次结构框架,由一个顶层事务(Top-Level Transaction)控制着各个层次的事务,顶层事务之下嵌套的事务被称为子事务(Subtransaction),其控制着每一个局部的变换,子事务本身也可以是嵌套事务。
- 嵌套事务的层次结构可以看成是一棵树
5. 分布式事务(Distributed Transactions)
- 分布式事务通常是在一个分布式环境下运行的扁平事务,因此,需要根据数据所在位置访问网络中不同节点的数据库资源
- 例如,一个银行用户从招商银行的账户向工商银行的账户转账1000元,这里需要用到分布式事务,因为不能仅调用某一家银行的数据库就完成任务