MySQL——事务和隔离级别

MySQL——事务和隔离级别
什么是事务事务就是一组原子性的SQL查询要么都执行要么都不执行或者说是一个独立的工作单元。为什么需要事务银行应用是解释事务必要性的一个经典例子。假设一个银行的数据库有两张表支票表和储蓄表。现在要从用户Jane的支票账户转移200美元到他的储蓄账户那么至少需要几个步骤以上六个步骤必须打包到一个事务中任何一个步骤失败都必须回滚所有步骤否则万在执行第三步之后服务器忽然掉电了从支票表的账户扣了200万但是钱并没有到储蓄表的账户因此要解决这种问题就要保证转账业务里的所有数据库的操作是不可分割的要么全部执行成功要么全部失败不允许出现中间状态的数据。数据库中事务就能达到这样的效果我们在转账前先开启事务等所有数据操作执行完成后才提交事务对已经提交的事务来说该事物对数据库所做的修改将永久生效如果中途发生中断或错误那么改事务期间对数据库所做的修改将会被回滚到没执行该事物之前的状态事务特性有什么ACID事务是由MySQL的引擎来实现的我们常见的InnoDB引擎是支持事务的。原子性Atomicity:一个事务中的所有操作要么全部完成要么全部不完成不会结束在中间某个环节而且事务在执行过程中发生错误会被回滚到事务开始前的状态就像这个事务从来没有执行过一样就好比买一件商品购买成功时则给商家付了钱商品到手购买失败时则商品在商家手中消费者的钱也没有花出去。一致性Consistency是指事务操作前和操作后数据满足完整性约束数据库保持一致性状态。比如用户A和用户B在银行分别有800元和600元总共1400元用户A给用户B转账200分别为两个步骤从A的账户扣除200元和对B的账户增加200元。一致性就是要求上述操作步骤后最后的结果是用户A还有600元用户B还有800元总计1400元而不会出现用户A扣除了200元但是用户B未增加的情况。隔离性lsolation:数据库允许多个并发事务同时对其数据进行读写和修改的能力隔离性可以防止多个事务并发执行时由于交叉执行而导致数据不一致因为多个事务同时使用相同的数据时不会相互干扰每个事务都有一个完整的数据空间对其他并发事务是隔离的也就是说消费者购买商品这个事务是不影响其他消费者购买的。持久性Durability事务处理结束后对数据的修改就是永久的即使系统故障也不会丢失。InnoDB引擎通过什么技术来保证事务这四个特性的呢持久性是通过redo log重做日志来保证的原子性是通过undo log回滚日志来保证的隔离性是通过MVCC多版本并发控制或锁机制来保证的一致性是通过持久性原子性隔离性来保证的为什么事务要有隔离性我们就要先知道并发事务时会引发什么问题在同时处理多个事务的时候就可能出现脏读、不可重复读、幻读的问题脏读一个事务读到了另一个未提交事务修改过的数据就意味着发生了【脏读】现象假设一个银行转账的场景时间事务A转账事务B查账余额T1开始事务1000T2将余额改为800未提交800脏数据T3读取余额 → 读到800T4发生异常回滚事务1000恢复T5基于 800 做业务决策错误不可重复读在一个事务内多次读取到同一个数据如果出现前后两次读到的数据不一样的情况就意味着发生了【不可重复读】现象时间事务A统计事务B修改订单订单金额T1开始事务100T2读取订单金额 →100T3开始事务T4将金额改为200T5提交事务200T6再次读取订单金额 →200注意记录的数量和记录集合没有变但是同一条记录的值不一致幻读在一个事务内多次查询某个符合条件的记录数量如果出现前后两次查询到的记录数量不一样的情况就意味着发生了【幻读】现象时间事务A事务B表中的行T1开始事务1, 2T2查询 id BETWEEN 1 AND 3 → 返回 (1,2)T3开始事务T4插入id3 并提交1, 2, 3T5再次查询 id BETWEEN 1 AND 3 → 返回(1,2,3)注意幻读记录的数量或记录集合变了事务的隔离级别有哪些脏读读到其他事务未提交的数据不可重复读前后读取的数据不一致幻读前后读取的记录数量不一致SQL标准提出四种隔离级别来规避这些现象隔离级别越高性能效率就越低这四个隔离级别如下读未提交read uncomittedRU指一个事务还没提交时它做的变更就能被其他事务看到允许脏读、不可重复读、幻读读时不加锁直接读最新数据性能最高但是几乎不用数据可能完全错误读提交read comittedRC指一个事务提交之后它做的变更才能被其他事务看到可重复读repeatable readRR指一个事务执行过程中看到的数据一直跟这个事务启动时看到的数据是一致的MySQL InnoDB引擎的默认隔离级别串行化(serializableSL)会对记录加上读写锁在多个事务对这条记录进行读写操作时如果发生了读写冲突的时候访问的事务必须等前一个事务执行完成才能继续执行MySQL在【可重复读】隔离级别下可以很大程度上避免幻读现象的发生所以MySQL并不会使用【串行化】隔离级别因为使用【串行化】隔离级别会影响性能分析一下每个阶段看到的是余额是多少在【读未提交】隔离级别下事务B修改余额后虽然没有提交事务但是此时的余额已经可以被事务A看到了于是事务A中余额V1是200V2是200余额V3是200在【读提交】隔离级别下事务B修改余额后只有在事务B提交事务后才能被事务A看到于是事务A中余额V1是100V2是200余额V3是200在【可重复读】隔离级别下事务A只能看到启动事务时的数据在提交事务之后才可以看到新数据所以在初始和提交状态之间状态都是一致的余额V1是100V2是100余额V3是200在【串行化】隔离级别下因为事务A在读操作事务B无法进行写操作在事务A提交之后才可以进行事务B的写操作余额V1是100V2是100余额V3是200四种隔离级别具体如何实现的呢对于【读未提交】隔离级别的事务来说因为可以读到未提交事务修改的数据所以直接读取最新的数据即可对于【读提交】和【可重复读】隔离级别的事务来说它们是通过Read View来实现的它们的区别在于创建Read view的时机不同大家可以把Read View理解成一个数据快照就像相机拍照那样定格某一个时刻的风景。【读提交】隔离级别是在每个语句执行前都会生成一个Read view,而【可重复读】隔离级别是启动事务时生成一个Read View,然后整个事务期间都在用这个Read view对于【串行化】隔离级别的事务来说通过加读写锁的方式来避免并行访问为什么隔离级别是读未提交读已提交可重复读串行化不是其它的什么