参考书籍《mysql是怎样运行的》
以及极客时间《mysql实战45讲》
本文总结事务相关的概念,事务相关的命令等,具体持久性,原子性,隔离性如何实现,会在后续的学习中进行补充
一丶事务#
上一篇关于buffer pool的学习中,我们学习Buffer pool相关知识,innodb再对某个页面进行读写访问时,会将这个页面读到buffer pool 中,之后如果修改了某个页面,也不会立即刷新到磁盘,而只是将缓冲页面的控制块添加到flush 链表中,之后某个时间点(后台定时任务线程每隔一定时间将flush 链表中的脏页刷新到磁盘,或者新的页面需要缓冲,将LRU链表中一些脏页刷盘)。那么如果mysql服务突然崩溃,或者服务器突然断电,岂不是已经提交的事务
将丢失?
事务是指是程序中一系列严密的逻辑操作,而且所有操作必须全部成功完成,否则在每个操作中所作的所有更改都会被撤消
这里保证已经提交事务一定可以持久化到磁盘的就是redo log。如果事务执行到一半需要回滚如何实现昵,这就是undo log实现的版本控制链的作用。
二丶事务特性#
1.原子性#
要么多个操作都执行,要么多个操作都不执行,不可存在中间状态。但是现实世界中一个业务存在多个步骤,这些步骤抽象为数据库操作又存在多个,一个数据库操作又可分为多步(比如先修改缓冲页,再刷新磁盘)任何一个时间点都可能发生意想不到的错误,使操作无法执行下去,所以需要保证:如果再执行过程中发生了错误
,就把已经执行的操作恢复为执行前的样子。
2.隔离性#
对于现实世界中状态转换对应的某些数据库操作来说,不仅仅需要保证原子性,还需要保证其他数据库操作造成状态转换不会影响到本次状态转换。这便是隔离性
3.一致性#
只有符合约束的数据才是有效(年龄再符合规定的范围,房价不能为负数)如果数据中的数据全部符合现实世界的约束,我们就说这些数据具备一致性
。如何实现一致性
- 数据库本身可以保证一定的一致性(主键,唯一索引保证不可重复,申明某些列not null拒绝null值的插入)甚至可以定义一些触发器进行限定
- 更多一致性操作,需要crud程序员进行限制
如果数据库操作不符合原子性(转钱扣了转账者,但是收款的人钱数没有增加)或者不符合隔离性(两个操作并发,第二个操作读到第一个操作没有提交的数据,然后第二个操作后写回,发生了脏写)这也是不符合一致性要求——数据库中原子性和隔离性都是保证一致性的一种手段
4.持久性#
当现实世界中的一个状态转换完成后,这个转换结果需要永久保留,这称为持久性
,现实世界中状态转换映射到数据中时,持久性意味着转换对应的数据需要持久化到磁盘中,无论发生什么事故,本次转换操作的影响都不应该丢失(损坏磁盘除外doge)
三丶事务的状态#
- 活动的:事务对应的数据库操作正在执行过程中时
- 部分提交的:当事务中最后一个操作执行完成,但是由于操作都是在内存中执行,造成了影响没用刷新到磁盘时
- 失败的:事务处于活动状态或者部分提交时,约到某些错误而无法继续执行,或者任务终止事务的执行,事务就称为失败的
- 中止的:事务执行到一半,需要撤销对数据库造成的影响进行撤销,
回滚
,当回滚操作执行完毕,称为终止的 - 提交的:处于
部分状态的事务
对应操作刷新到磁盘,称为提交的
四丶mysql中的事务语法#
1.开启事务#
-
Begin
-
Start Transaction
该命令可以跟随如下修饰符,多个修饰符使用逗号分隔,如
Start Transaction read only,with consistent snapshot
-
read only
标识当前事务是一个只读事务,属于该事务的数据库操作只能进行读操作(但是可以改当前会话中的临时表)(为什么只读还需要事务,因为需要获得一致性视图,不同的隔离级别会读到不同的数据,比如你想统计流水表中本月每一个顾客的消费信息,先统计了总流水Sum,然后依次统计每一个用户的流水,如果没用事务,第后续的对每一个顾客的统计之和可能读到最新的数据(用户新增了消费),而导致总流水和每一个用户流水之和不等的情况)
-
read write
标记当前事务是读写事务
-
with consistent snapshot
启动一致性读
-
begin/start transaction 命令并不是一个事务的起点,在执行到它们之后的第一个操作
InnoDB 表的语句,事务才真正启动。(一致性视图是在第执行第一个快照读语句时创建的)
如果你想要马上启动一个事务,可以使用 start transaction with consistent snapshot 这个命令。(一致性视图是在执行 start transaction with consistent snapshot 时创建的)
2.提交事务#
-
commit
-
commit work and chain
提交当前事务并开启下一个事务,如果执行 commit work and chain,则是提交事务并自动启动下一个事务,这样省去了再次执行 begin 语句的开销
3.手动中止事务#
roll back
,程序员可以手段roll back进行回滚事务,如果事务在执行的过程中遇到一些错误,比如发生了死锁,会回滚整个事务
4.自动提交#
mysql 有一个系统变量autocommit
来控制是否自动提交事务,如果不显示的使用开启事务的命令,那么每一条语句就视为一个独立的事务。关闭自动提交的方式有:手动开启事务,设置自动提交为OFF,这时候需要手动提交或者进行回滚。
6.隐式提交#
mysql中如果输入了一些特定的语句,即便不是commit 命令也会提交事务。如
-
执行ddl,定义数据库对象的命令,比如建立表,修改表,以及视图,存储过程等
-
隐式的使用或者修改mysql数据库中的表
使用
alter user
,create user
,drop user
等语句的时候会提交前面语句属于的事务 -
事务控制or关于锁定的语句
如果一个事务还没有提交,或者回滚的时候,又开启一个新事务,或者修改
autocommit
的值从OFF 到ON的时候也会提交事务。使用lock tables,unlock tables等关于锁定的语句也会提交事务 -
加载数据的语句
比如使用load data等语句也会提交事务
-
关于mysql复制的语句
比如
start slave
,stop slave
也会提交事务 -
其他语句
比如
optimize table
等
7.保存点#
savepoint 保存点名称
,可以开启一个保存点,使用rollback to 保存点名称
可以回滚到指定保存点,使用release savepoint 保存点
可以释放保存点,保存点如同游戏的存档,避免我们从头开始,心态炸裂
五丶小心长事务#
长事务意味着系统里面会存在很老的事务视图。由于这些事务随时可能访问数据库里面的任何数据,所以这个事务提交之前,数据库里面它可能用到的回滚记录都必须保留,这就会导致大量占用存储空间(记录回滚信息的undo log,需要一致存储,占用空间)可以在information_schema
库的innodb_trx
这个表中查询长事务的信息