
一、什么是事务在MySQL中键入begin命令一组事务就开始了随后可输入多个SQL语句提交后表格就被实际修改了。所以事务现象上可以简单理解成一组SQL语句构成的整体要么全部成功要么全部失败。但实际上一张表格可能有同时多个事务在修改和访问很可能出现并发问题。所以为了让程序员不需要考虑各种各样的潜在错误和并发问题一个完整的事务不是简单的sql集合还需要满足如下四个属性原子性一个事务中的所有操作要么全部完成要么全部不完成不会结束在中间某个环节。事务在执行过程中发生错误会被回滚Rollback到事务开始前的状态就像这个事务从来没有执行过一样。一致性在事务开始之前和事务结束以后数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设规则这包含资料的精确度、串联性以及后续数据库可以自发地完成预定的工作。实际上一致性是另外三种特性得到保证的结果。隔离性数据库允许许多并发事务同时对其数据进行读写和修改的能力隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。持久性事务处理结束后对数据的修改就是永久的即便系统故障也不会丢失。注上面四个属性简称为 ACID二、隔离级别为了保证隔离性事务隔离分为不同级别包括读未提交Read uncommitted、读提交read committed、可重复读repeatable read和串行化Serializable下面分别做介绍读未提交一端在事务中做的修改还未提交就能被另一端可以看到这种读到没提交的数据的行为叫脏读。读提交一端提交事务后才能被另一端看到但这会导致在一个事务内部第一次select看到的是提交前的数据第二次select看到提交后的数据这是不合理的这种现象叫不可重复读。可重复读直到当前事务结束了才能看到其它事务变更后的数据。串行化事务加锁执行即当前库有事务在执行时的同时不能有其它事务执行。注如果可重复读隔离级别下同时写了怎么办上述隔离机制针对的是读写并发的场景如果同时读不存在并发问题同时写当然选择串行执行。三、隔离的实现原理1.事务IDmysql面临同一时间处理多个事务的问题所以要对事务做管理所以事务会被封装成对象然后用特定数据结构进行管理。基于此每个事务都能有单向增长的id,因此可根据id大小判断事务被创建的时间顺序。2.三个隐藏字段我们看到的表显示并不完全MySQL其实还有四个隐藏字段最近修改该条记录的事务IDMySQL中一行信息也称为一行记录回滚指针指向该记录的上一版本地址也就是每次我们在事务中对记录做修改时会创建一分历史数据的拷贝自增的隐藏主键在表没有主键、也没有合适的唯一非空索引时生成注此外还有一个删除标记位用01表示该记录是否被更新3.undo日志前面索引学习中提到过MySQL在内存中开了一块缓冲区Buffer Pool另外还开了一块区域作为undo日志缓冲区。4.多版本并发控制MVCC前置知识大概介绍后下图展示MySQL如何进行记录的版本控制注一条条undo log就叫做版本。上图演示的是读操作如果是删除操作仅需将删除标志位设为1再放到undolog里去逻辑上这条记录已经被删除了如果后续快照读后面讲解其它事务逻辑上判定这条事务的删除自己不可见就会把这条记录重新恢复出来。当没有活跃事务需要用到旧记录时才会真正的从B树中删除该记录。如果是插入操作新插入的记录没有历史版本InnoDB会生成一条Insert undo log并放入undo log中。这条日志里只记录这条新记录的主键信息若该记录需要回滚就会根据这个主键找到B树中的数据并删除。一旦事务成功提交这条undo log会被标记为可丢弃由后台线程清理掉。undo log是否会满?undo log的服务对象是一个事务的commit以后会清空undo log,所以不用担心undo log满溢。这就是为什么commit后下次再打开事务无法回滚。那么事务调用select时看到的是最新版本还是历史版本当前读读取最新版本快照读读取历史版本什么决定了select使用当前读还是快照读呢这就要看具体的SQL操作只读不改-快照读要改数据UPDATE/DELETE/INSERT-当前读先当前读拿最新数据再加锁修改。强制要求看最新数据且加锁SELECT FOR UPDATE- 当前读强制拿最新数据并加锁。下面来看看什么决定了快照读可以读到的内容。5.读视图read viewread view是在执行快照读的时被创建的类对象用于后续决定快照读读的是哪个版本。下面给出该对象的主要字段。m_ids;//一张列表用来维护Read View生成时刻系统正活跃的事务ID up_limit_id;//记录m_ids列表中事务ID最小的ID low_limit_id;//ReadView生成时刻系统尚未分配的下一个事务ID也就是目前已出现过的事务ID的最大值1 creator_trx_id//创建该ReadView的事务ID某个事事务执行快照读时后根据数据版本记录的事务IDDB_TRX_ID来判断这个版本自己是否可见注意版本始终在那里如果想强制读取是没问题的这里只是一种软性约束DB_TRX_ID creator_trx_id那这个版本就是我自己创建的当然可以看到。DB_TRX_ID up_limit_id那这个版本已经是古早事务创建的了至少在我形成快照时它已经退出了所以我看见这个版本是没关系的。DB_TRX_ID low_limit_id这个版本是我形成快照后才启动的事务创建的逻辑上我能读到未来的消息所以不可见。DB_TRX_ID up_limit_idDB_TRX_ID low_limit_id则进行二次判断如果DB_TRX_ID在m_ids中说明他是我进来后正在活跃的事务我不该去窥视他的版本如果DB_TRX_ID不在m_ids说明这个事务也已经退出了可以看见。至此我们已经知道快照读能看见哪些版本可RC读提交和RR可重复读都会执行快照读他们的功能为什么会形成差异呢很简单RC隔离级别下每次快照读都形成新的快照意味着每次都可以读到在此次快照形成时之前的所有版本RR隔离级别下只使用第一次生成的快照。所以无论读几次都和第一次读到的版本一模一样。