MySQL事务

MySQL 事务



一、事务是什么?

事务将数据库从一种一致性状态转换为另一种一致性状态。

事务可由一条sql语句组成,也可以由一组复杂的sql语句组成。

二、使用方法

-- 开启事务
START TRANSACTION | BEGIN
-- 提交事务,并使得已对数据库做的所有修改持久化
COMMIT
-- 回滚事务,结束用户的事务,并撤销正在进行的所有未提交的修改
ROLLBACK
-- 创建一个保存点,一个事务可以有多个保存点
SAVEPOINT identifier
-- 删除一个保存点
RELEASE SAVEPOINT identifier
-- 事务回滚到保存点
ROLLBACK TO [SAVEPOINT] identifier

二、事务ACID特性

1.原子性(Atomcity)

事务操作要么都做(commit),要么都不做(rollback);
事务是访问并更新数据库各种数据项的一个程序执行单元,是不可分割的工作单位;
事务通过 undo log 来实现回滚操作,undo log 记录事务每一步操作,当回滚时,回放事务具体操作的逆运算。
undo log记录的是逻辑日志。

2.隔离性(Isolation)

事务的隔离性要求每个读写事务的对象对其他事务的操作对象能相互分离,也就是事务提交前对其他事务都不可见;
事务的隔离性通过MVCC和锁来实现;

MVCC是多版本并发控制,主要解决一致性非锁定读,通过记录和获取行版本,而不是通过锁来限制读操作,从而实现高效并发读。

锁用来处理并发DML操作。数据库中提供粒度锁的策略,针对表(聚集索引B+树)、页(聚集索引B+树叶子节点)、行(叶子节点当中某一段记录行)三种粒度加锁。

3.持久性(Durability)

事务提交后,事务DML操作将会持久化(写入redo log磁盘文件,包括页信息、页偏移值、具体数据)。即使发生宕机等故障,数据库中的数据也能恢复。
redo log记录的是物理日志。

4.一致性(Consistency)

一致性指事务将数据库从一种一致性状态(正确状态)转变为另一种一致性(正确)状态,在事务执行前后,数据库完整性约束没有被破坏。一致性由 A、I、D 共同维护。

一致性放在最后说,是因为不太好理解
什么是数据库的一致(正确)的状态?满足给这个数据库预定义(约束)的一些规则。这些规则有哪些呢,具体到某个表的某个字段,比如你在定义表的时候,给这个字段的类型是number类型,并且它的值不能小于0,那么你在某个 transaction 中给这个字段插入(更改)为一个 String 值或者是负值是不可以的,这不是一个“合法”的transaction,也就是说它不满足我们给数据库定义的一些规则(约束条件)。

事务并发异常

脏读(dirty read)

事务A读到了事务B未提交的数据,此时如果事务B执行回滚操作,那A读到的数据就是脏数据。
在这里插入图片描述

不可重复读(non-repeatable read)

事务A可以读到事务B提交的数据。通常发生在一个事务中,针对row行数据内容,两次读到的数据不一致的情况。

有一种理解是这样的,如下图所示,个人感觉是不对的,下面这种应该是幻读问题,改成update才是不可重复读。
在这里插入图片描述
微软SQL Server上定义的不可重复读,比MySQL官方文档上定义的,感觉更加清晰:
在这里插入图片描述

幻读(phantom read)

  1. 事务中一次读操作不能支撑接下来的业务操作逻辑。通常发生在一个事务中一次读判断,并不能作为接下来操作的依据(接下来的插入操作失败了)。

  2. 微软SQL Server上定义的幻读,比MySQL官方文档上定义的,感觉更加清晰:
    两次读取记录的总行数不一致。即幻读发生在事务A执行select操作,得到的所有rows数据后,另一个事务B执行insert/delete操作,导致第二次事务A查询到的所有rows数据和第一次查询的不一致。
    在这里插入图片描述
    个人感觉需要上面两种结合理解,第一种理解是针对实际应用场景的解释。第二种理解是一个概念解释。

幻读在RR及以下隔离级别中存在。但是在RR隔离级别,可通过读加锁(使用next-key lock)解决幻读问题。
在这里插入图片描述

解决幻读问题:解决两次当前读不一致的问题。通过加锁解决。
在这里插入图片描述

当前读和快照读的详细说明和实现方式,可以参考一下这篇文章

隔离级别

ISO和ANIS SQL标准制定了四种事务隔离级别的标准,各数据库厂商在正确性和性能之间做了妥
协,并没有严格遵循这些标准;MySQL innodb默认支持的隔离级别是 REPEATABLE READ;

READ UNCOMMITTED

读未提交:允许所有事务读取其他事务还未提交的数据。
该级别下读不加锁,写加排他锁,写锁在事务提交或回滚后释放锁。

READ COMMITTED

读已提交:所有事务只能读取其他事务已提交的数据。其他事务仍可以更新、新增数据。
该级别支持 MVCC (多版本并发控制),也就是提供一致性非锁定读;此时读取操作读取最新版本的历史快照数据(快照读)

REPEATABLE READ

可重复读:所有事务前后多次读取的数据是一致的。事务执行前,其他事务不能更新该数据,但可以新增数据。
该级别下也支持 MVCC,此时读取操作读取事务开始时的版本数据

SERIALIZABLE

可串行化:所有事务串行化执行。
该级别下给读加了共享锁;此时隔离级别最严苛,性能低;


不同隔离级别下并发异常

在这里插入图片描述

undo log

回滚日志
保证事务的原子性
实现多版本技术,
delete undo log:用于回滚,提交即清理(后续不支持读了,所以可以清理)
insert undo log:用于回滚,同时实现快照读,不能随便删除(MVCC需要读历史版本的数据,所以不能随便删除)。

  1. undo log如何清理?
    依据系统活跃的最小活跃事务ID判断,如果小于最小活跃事务ID,则可以清理。

  2. 为什么InnoDB不记录表的总行数?为什么InnoDB count(*)很慢,不像MyISAM能记录总数?
    因为不同的事务隔离级别,读到的总行数不一样。

如下图所示,每条记录中,都保存了DB_TRX_ID事务ID、DB_ROLL_PTR回滚指针(指向上一个记录的地址);
第一条记录是最新记录,undo log保存的是历史版本记录
在这里插入图片描述

redo log

  • 记录修改
  • 用于异常恢复
  • 循环写redo log文件
    redo log实际上记录数据页的变更,而这种变更记录是没必要全部保存,因此redo log实现上采用了大小固定(可配置),循环写入的方式,当写到结尾时,会回到开头循环写日志。如下图:
    write pos:redo log当前记录的LSN(Log Sequence Number,日志序列号)位置,即当前的redo log写入位置;
    check point:数据页更改记录刷盘后,对应的redo log的LSN位置,可以简单理解为刷盘位置;
    check point到write pos之间是待刷盘的数据页更改记录;
    write pos到check point的数据是可以写redo log的空间,如果write pos追上check point了,会等待check point释放空间;
    在这里插入图片描述
    在innodb中,既有redo log需要刷盘,还有数据页也需要刷盘,redo log存在的意义主要就是降低对数据页刷盘的要求,还有就是宕机恢复(持久性)。
    启动innodb的时候,不管上次是正常关闭还是异常关闭,总是会进行恢复操作。因为redo log记录的是数据页的物理变化,并且是顺序存储的,因此恢复速度快。
    重启innodb时,首先会检查磁盘中数据页的LSN,如果数据页的LSN小于日志中的LSN,则会从checkpoint开始恢复。
  • 如下图所示,redo log的写入流程:
  1. client发送一条update语句,交给server层;
  2. server层解析sql,交给innoDB存储引擎处理;
  3. InnoDB写undo log;
  4. InnoDB更新内存数据;
  5. 记录页的修改,写入redo log,状态改为prepare;
  6. 更新完成,返回给Server层;
  7. Server层提交;
  8. 事务提交,将事务记录为commit状态。
    在这里插入图片描述

redo log的刷盘时机
如下图所示,redo log包含三种刷盘时机:

在这里插入图片描述
其中Log buffer和OS buffer的区别:当程序崩溃时,Log buffer会丢失,但OS buffer不会。
在这里插入图片描述

码字不易,下次继续写一下锁相关的内容。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值