MySQL事务
什么是事务
- 事务是一组操作, 这组操作要么就是全部成功,要么就是全部失败,什么都不做;
- 事务是并发控制的基本单位;
- InnoDB 引擎支持事务,MyISAM 引擎是不支持事务。
事务特性
数据库事务包括如下特性:
原子性(Atomicity)
- 事务的所有操作,要么全部做完,要么全部不做,不可能停在中间状态;
- 事务是一个不可分割的整体,就像原子一样不可分割;
一致性(Consistency)
- 指事务将数据库从一种状态转变为另一种状态时, 数据状态要保证前后一致, 不能有不一致的情况;
- 事务开始前后,数据库中数据的完整性约束没有被破坏;
隔离性 (Isolation)
- 当不同的事务同时操纵相同的数据时,每个事务都有各自的完整数据空间, 不会受到其他事务的影响;
- 并发事务间的修改必须相互隔离;
持久性 (Durability)
- 事务一旦提交,则其结果就是永久性的。即使发生宕机的故障,数据库也能将数据恢复;
事务并发安全问题
MySQL中并发操作带来的数据安全问题主要包括:
脏读
:A事务读到B事务未提交
数据;不可重复读
:同一事务中重复读取相同记录的值
不一致;幻读
:同一事务中多次读取记录的数量
不一致;
隔离级别
针对上面出现的几种情况,在标准SQL规范中,定义了4个事务的隔离级别对其进行处理.
读未提交
(RU, Read Uncommitted):事务A能读取到事务B的已修改未提交
的数据;读提交
(RC, Read Committed):事务A读取到的是其他并发事务的已提交
的数据, 并发事务的未提交
数据对其他事务不可见, 实现了未提交数据
的并发隔离;可重复读取
(RR, Repeatable Read):事务A在同一事务中可反复读取同一数据, 该数据的值不会受到其他并发事务的影响;序列化
( Serializable):提供严格的事务隔离。要求失去序列化执行,事务只能一个接一个地执行,不能并发执行。
这些隔离级别的安全等级和操作性能关系如下:
- 不同等级隔离级别采用不同的实现方式;
- 不同等级隔离级别的数据安全等级由上到下一步步提高的;
- 不同等级隔离级别的性能由上到下一步步降低;
- 高等级的隔离级别包含解决了低等级隔离级别的问题;
隔离级别 | 脏读 | 不可重复读 | 幻读 | 实现方式 | 性能 | |
---|---|---|---|---|---|---|
读未提交(RU) | O | O | O | 无 | 最好 | |
读提交(RC) | O | O | 读不加锁, 写入、修改和删除加锁 | Oracle默认隔离级别 | ||
可重复读(RR) | O | MVCC | InnoDB默认隔离级别 | |||
串行化(Serialser) | 读加共享锁,写加排它锁 | 最差 |
MVCC
MVCC简介
- MVCC(Multiversion Concurrency Control,多版本并发控制), 是一种用来解决
读-写冲突
的无锁并发控制技术; - 可用于实现RR等级的事务隔离级别;
- MVCC实现依赖于
隐藏字段
,版本链
和读视图
; - MVVC实现依赖记录行中的隐藏字段包括:
- trx_id: 事务ID, 用来记录当前事务的ID,
- roll_pointer: 回滚指针, 用于;
在MVCC并发控制中,读操作可以分成两类:
- 快照读(snapshot read):
- 读取的是记录的可见版本 (有可能是历史版本),不用加锁;
- 包括简单的select语句;
- 当前读(current read):
- 读取的是记录的最新版本,需上锁;
- 包括特殊的select语句包括(加锁, 插入, 更新, 删除等);
在可重读Repeatable reads事务隔离级别下:
- SELECT时,读取创建版本号<=当前事务版本号,删除版本号为空或>当前事务版本号。
- INSERT时,保存当前事务版本号为行的创建版本号
- DELETE时,保存当前事务版本号为行的删除版本号
- UPDATE时,插入一条新纪录,保存当前事务版本号为行创建版本号,同时保存当前事务版本号到原来删除的行
通过MVCC,虽然每行记录都需要额外的存储空间,更多的行检查工作以及一些额外的维护工作,但可以减少锁的使用,大多数读操作都不用加锁,读数据操作很简单,性能很好,并且也能保证只会读取到符合标准的行,也只锁住必要行。
MVCC在mysql中的实现依赖的是undo log与read view
- undo log :undo log 中记录某行数据的多个版本的数据。
- read view :用来判断当前版本数据的可见性
ReadView(读视图)
- ReadView是MVCC中用来判断当前事务数据视图的窗口;
- ReadView主要通过以下几个结构来判断当前事务的有效数据视图:
- trx_ids: 当前并发的活跃事务id列表;
- up_limit_id: , 当前已经提交的事务号 + 1, 所有<该值id的事务都已提交, 所以对当前事务来说应该都可见;
- low_limit_id: 当前最大的事务号 + 1, 所有>该值id的事务, ;
UndoLog和版本链
- 回滚日志(UndoLog)用于记录数据更新前的旧记录;
- 每次对记录更新(包括insert, update, delete)时, 将会生成一条新undolog日志;
- 每条undolog日志有一个
roll_pointer
字段, 指向上一条的undolog日志, 最终形成链式结构, 称为版本链
; - 每条undolog有2种类型之一:
- insert类型: 插入操作时生成, 事务提交后删除;
- update类型: 更新或删除操作时生成, pruge线程回收;
- undolog存储于
<表名>.ibdata
文件中;
MVCC的实现流程
mvcc的实现的大体流程如下:
- 事务开始时, 生成一个当前事务id(trx_id);
- 建立
ReadView
, ReadView包含了当前活跃的并发事务ID列表及过滤窗口; - 查询数据, 使用ReadView中的版本号对查询数据进行过滤;
- 根据ReadView从
UndoLog
版本链中查询符合要求的数据; - 返回符合要求的数据;
NextKey锁
- MySQL 通过行锁+间隙锁的方式 解决了RR级别下解决了
幻读
的问题;
总结
前面讲的重做日志,回滚日志以及锁技术就是实现事务的基础。
- 原子性(Atomic): 通过 undo log 来实现的;
- 持久性(D): 通过 redo log 来实现的;
- 隔离性(I): 是通过 (读写锁+MVCC)来实现的;
- 一致性C: 是通过原子性,持久性,隔离性来实现的!!!