在MySQL中,事务有4种隔离级别,分别是:
- Read Uncommitted 未提交读
- Read Committed 已提交读
- Repeatable Read 可重复读
- Serializable 可串行化
在理解四种隔离级别之前,我们需要先了解另外三个名词:
- 脏读
a事务会读取到b事务还未提交的数据,但是b事务由于某种原因进行回滚操作,这样,a事务读取的数据是不可用的,进而会造成一些异常结果。
- 不可重复读
a事务周期内对某一数据多次查询,同时这些数据在b事务中进行了update或delete操作。那么a事务每次查询出来的结果可能都不一样。
- 幻读
幻读的结果其实和不可重复读是一样的表现,差异就在于不可重复读主要是针对其他事务进行了编辑(update)和删除(delete)操作。而幻读主要是针对插入(insert)操作。也就是在一个事务生命周期内,会查询到另外一个事务新插入的数据。
Read uncommitted 未提交读
未提交读,这种情况下,一个事务a可以看到另一个事务b未提交的数据,如果此时事务b发生回滚,那么事务a拿到的就是脏数据,这也就是脏读的含义。
此隔离级别在MySQL InnoDB一般不推荐使用。
Read Committed 已提交读
已提交读,一个事务从开始直到提交之前,所做的任何修改对其他事务都是不可见的。解决了脏读问题,但是存在幻读现象。
Repeatable Read 可重复读
可重复读,该级别保证在同一事务中多次读取同样记录的结果是一致的,在InnoDB存储引擎中同时解决了幻读和不可重复读问题。
InnoDB引擎通过使用Next-Key Lock解决了幻读的问题。Next-Key Lock是行锁和间隙锁的组合,当InnoDB扫描索引记录的时候,会首先对索引记录加上行锁(Record Lock),再对索引记录两边的间隙加上间隙锁(Gap Lock)。加上间隙锁之后,其他事务就不能在这个间隙修改或者插入记录。
Serializable 可串行化
Serializable 是最高的隔离级别,它通过强制事务串行执行,避免了幻读的问题,但是 Serializable 会在读取的每一行数据上都加锁,所以可能导致大量的超时和锁争用的问题,因此并发度急剧下降,在MySQL InnoDB同样不被建议使用。
开启事务
- BEGIN、BEGIN WORK、START TRANSACTION
执行BEGIN命令不会真正在引擎层开启新事务,仅仅是为当前线程设定标记,表示为显式开启的事务。
- START TRANSACTION READ ONLY
开启只读事务,当MySQL Server接收到任何数据更改的SQL时,都会直接拒绝修改并返回错误,此错我不会进入引擎层。
- START TRANSACTION READ WRITE
允许super用户在当前线程只读状态为true的情况下启动读写事务。
- START TRANSACTION WITH CONSISTENT SNAPSHOT
开启事务会进入引擎层,并开启一个readview。只有在RR隔离级别下,这种操作才有效,否则会报错。
Undo log
在数据进行修改时会记录相应的undo日志,如果事务失败或者回滚,可以借助记录的undo日志进行回滚。Undo log是逻辑日志,记录更改前的数据镜像。在修改时如果同时需要读取当前数据的时候,它可以根据版本信息分析出该行记录以前版本的数据。另外Undo log也会产生重做日志,因为Undo log也要进行持久化保护。
事务提交
- 使用全局事务ID产生器生成事务NO,将当前连接的事务指针(trx_t)添加到全局提交事务链表(trx_serial_list)中
- 标记undo,如果这个事务只使用了一个UndoPage且使用量小于3/4个Page,则把这个Page标记为TRX_UNDO_CACHED,如果不满足且是insert undo则标记为TRX_UNDO_TO_FREE,否则undo为update undo则标记为TRX_UNDO_TO_PURGE。标记为TRX_UNDO_CACHED的undo会被引擎回收。
- 把update undo放入所在undo segment的history list,并递增rseg_history_len(全局)。同时更新Page上的TRX_UNDO_TRX_NO, 如果删除了数据,则重置delete_mark
- 把undate undo从update_undo_list中删除,如果被标记为TRX_UNDO_CACHED,则加入到update_undo_cached队列中
- mtr_commit(日志undo/redo写入公共缓冲区),至此,在文件层次事务提交。这个时候即使crash,重启后依然能保证事务是被提交的。接下来要做的是内存数据状态的更新(trx_commit_in_memory)
- 只读事务只需要把readview从全局readview链表中移除,然后重置trx_t结构体里面的信息即可。读写事务首先需要是设置事务状态为TRX_STATE_COMMITTED_IN_MEMORY,释放所有行锁并且将trx_t从rw_trx_list中移除,readview从全局readview链表中移除。如果有insert undo则在这里移除,如果有update undo则唤醒Purge线程进行垃圾清理,最后重置trx_t里的信息,便于下一个事务使用
回滚
- 如果是只读事务,则直接返回
- 判断当前是回滚整个事务还是部分事务,如果是部分事务,则记录下需要保留多少个Undo log,多余的全进行回滚
- 从update undo和insert undo中找出最后一条undo,从这条undo开始回滚
- 如果是update undo则将标记为删除的记录清理标记,更新过的数据回滚到最老的版本。如果是insert undo则直接删除聚集索引和二级索引
- 如果所有undo都已经被回滚或者回滚到了指定的undo则停止,把Undo log删除
索引
InnoDB引擎使用B+树作为索引结构,主键索引的叶子节点data域保存了完整的字段数据,非主键索引的叶子节点保存了指向主键的值数据。
上图是 InnoDB 主索引(同时也是数据文件)的示意图,可以看到叶节点包含了完整的数据记录,这种索引叫做聚集索引。因为 InnoDB 的数据文件本身要按主键聚集,所以 InnoDB 要求表必须有主键,如果没有显式指定,则 MySQL 系统会自动选择一个可以唯一标识数据记录的列作为主键,如果不存在这种列,则 MySQL 自动为 InnoDB 表生成一个隐含字段作为主键,这个字段长度为6个字节,类型为长整形。
InnoDB 的辅助索引 data 域存储相应记录主键的值而不是地址。换句话说,InnoDB 的所有辅助索引都引用主键作为 data 域。聚集索引这种实现方式使得按主键的搜索十分高效,但是辅助索引搜索需要检索两遍索引:首先检索辅助索引获得主键,然后用主键到主索引中检索获得记录。
结尾
对于MySQL InnoDB的诸多特性,本文只介绍了很小的一部分,感兴趣的同学可阅读 《MySQL技术内幕:InnoDB存储引擎》了解更多相关知识。
好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对的支持。
更多相关Mysql内容来自木庄网络博客
标签:Mysql
相关阅读 >>
drivermanager连接mysql数据库在实际开发中使用
更多相关阅读请进入《mysql》频道 >>

数据库系统概念 第6版
本书主要讲述了数据模型、基于对象的数据库和XML、数据存储和查询、事务管理、体系结构等方面的内容。