前言还是像我剖析线程池的时候所说我感觉我确实走的太快了不过学的早的好处就是可以随时调整那不如我们现在朝花夕拾一下后面几个博客估计也都是MySQL正文并发问题隔离级别是因为并发问题而生的那到底MySQL会有哪些并发问题误区这里大家可能会进入一个误区我发现别人更新的数据不是很正常吗我就是在获取最新的这个数据但是大家要注意这里是一个事务事务具有隔离性这里破坏的就是隔离性从表现上来看。这里我一个事务具有原子性两次操作查询的目标应当一致所以不可重复读和幻读我觉得也是破坏原子性的一个体现这个不是标准只是帮助理解这个话不要记因为这里本身在一个线程里面这里的两个操作看起来就是无缝执行的所以其实符合原子性脏读这个就是️线程A正在修改这个SQL数据这个时候事务没有提交这个时候线程B来查询这个数据但是这个时候获取到了线程A还未修改的数据不可重复读这个指的就是线程A有两次查询操作第一次查询比如是100但是中途线程B来修改数据把这里的100修改成了1000然后A第二次执行查询操作发现这里是1000同一个事务里面两次读取不一致幻读SQL经典定义这个指的就是比如我线程A还是查询两次第一次查询当前有5行数据线程B再插入一条对空白线程A 不操作的地方做操作第二次线程A查询发现有6条数据就像出现了幻觉这个就叫幻读MySQL RR级别 InnoDB独有现象这里指的就是比如我线程A读一下我这个数据里面只有1 3 5然后线程B插入一个4然后我这个A的第二个操作恰好也是插入一个4发现冲突线程A再读发现只有1 3 5没有4。这个就像幻觉一样这个也是幻读隔离级别这里企业大多使用RC或者RR大部分都是RC因为这里越往下并发性能也就越差特别是这里的串行化堪比单线程的想看这里的实现原理这里必须得看看下面的MVCC读未提交read uncommited意思就是如果这个时候你没有提交我也可以看到你的数据读已提交read committed RC这里的意思就是我只能看到你提交后的数据**这里的实现是引入了快照读**可重复读repeatable read RR意思就是这里你开始我看到你什么样执行过程中也看你什么样**这里的解决方案是一个事务只有一个快照**串行化serializable这个就是多个事务之间发生读写冲突的时候得排队一个事务一个事务来堪比单线程这个就是不同级别可能出现的问题但是MySQL的RR可以很大程度避免幻读但是无法完全避免幻读MVCC这个东西就是数据库来实现上面四种隔离级别的利器和基础了当前读和快照读快照读只有普通的select语句是快照读这里会生成一个快照里面只有已提交的事务当前读比如select … for update或者insert或者update或者delete都是当前读这里就会提交这里的数据。现在看是不是感觉没区别等一会MVCC到RR的时候你们就会发现明显区别**其实也很明显只有快照读可以访问版本链而当前读直接读最新数据来**而RR级别为什么不可以完全解决幻读也是因为这里的当前读和快照读的冲突数据库隐藏字段首先是数据库隐藏字段DB_TRX_ID这个是最近修改事务的IDDB_ROLL_PTR这个是回滚指针指向上一次事务的修改也就是指向上一个版本DB_ROW_ID这个是隐藏主键如果没有主键字段就是用这个做主键undolog版本链概念undolog是事务原子性的基础当我们执行一条非查询的语句比如insert这里的undolog就会生成一个对应的delete用于回滚而这里的日志更新时机就是在执行语句真正执行前这里就更新了日志防止你断个电啥的造成损失即使宕机也可以回滚而undolog的第二个作用就是来帮我生成一个版本链如图所示这里就像链表这里的头节点是最新的记录每次使用头插法来插入新版本快照读的相关属性这里直接贴一张来自黑马程序员的图我们一会讲案例的时候回来查数据链访问规则快照读注意这里RR和RC的区别就是在于这里RR每个事务只有一张快照但是RC每次查询都会有快照这个时候就会导致一个问题就是每次RC其实查询到的就是最新的已提交的数据这个时候就触发了不可重复读和幻读但是不用判断这里的数据链访问规则所以说这里的数据链访问规则就是给RR准备的那我们看看这里是如何访问数据链的这里首先你要先去上面看ReadView的属性在这个数据链访问规则还是很重要的比如这里我们的m_ids就是135但是要注意快照读访问的就是已提交的事务这里相当于一个黑名单关于这里的第一条规则没什么说的我创建的我还不可以访问了关于第二条规则这里就是不在黑名单里面你就相当于提交了所以这里可以访问如果大于max就说明你不在我这个快照里面无法读第四个就说这个如果不在黑名单就可以访问临键锁 next-key锁当前读在RR隔离级别里面对于当前读的防范就在这里这里就是临键锁行锁间隙锁行锁可以防止不可重复读的问题间隙锁可以避免大部分幻读问题比如访问13这里就会(-∞,1], (1,3], (3,∞)锁住防止别的线程插入数据但是相应的这里的并发性能就会下降因为其他的线程阻塞了我们来一个例子理解一下我们就看一下这个InnoDB在RR对临键锁的优化吧比如我们现在有一组数据1020203040对应id12456对于第一种情况我们找一个id3吧就临键锁锁住这里的2-4然后发现3不在把临键锁退化为间隙锁防止这里另一个进程插入一个3导致幻读对于第二种情况这里我们建立一个索引吧然后通过这个索引来找这里20的值这里(1020]加临键锁(20,20]临键锁,(20-30]加临键锁。如果这里没有20的话这里就会锁1030并且退化为间隙锁第一种情况- 易困惑点这里其实只需要锁一下这两条20以及这里的间隙就够了。但是这里其实多锁了一个30因为这里数据命中了这里无法从临键锁退化到间隙锁对于第三种情况这里我们查询id4的值吧这里(-∞,1]临键锁(12]加临键锁,24加间隙锁- 目的防止对这里id4的值做改动无法解决的冲突RR看似这里我们有undolog版本链来防范快照读的幻读有临键锁来防范当前读的幻读那还有什么幻读是我们干不了的这里能有什么问题。那其实小林coding就给出了两个场景那也就是我们定义里面说的两个定义场景一这里我们线程A来查询一下数据5表中没有所以查询不出来这里线程B插入了一个数据5现在A再来查询快照读当然查不出来这个时候A对数据5做一个更新操作当前读当然可以更新这个时候再去读这个时候发现这个操作id是自己就可以读到了场景二这里我们线程A查询一下100的数据发现有3条然后这个时候B插入一条200这个时候A使用for update查询发现有4条就是我们说的SQL的经典幻读定义这里的解决方案就是这里一旦开启事务就使用for update也就是统一使用当前读其实总结来看就是一个事务使用了当前读和快照读导致两种读法出现冲突尾声其实从企业的方面来看他们不在乎不可重复读和幻读只要不出现脏读就好所以这里其实大家都为了这个并发性能大多业务都是RC而这里的RR只有涉及一些重要业务的时候使用包括一些别的数据库默认都是RC所以说我觉得只需要重点关注一下RR和RC就好
MySQL—隔离级别和MVCC
前言还是像我剖析线程池的时候所说我感觉我确实走的太快了不过学的早的好处就是可以随时调整那不如我们现在朝花夕拾一下后面几个博客估计也都是MySQL正文并发问题隔离级别是因为并发问题而生的那到底MySQL会有哪些并发问题误区这里大家可能会进入一个误区我发现别人更新的数据不是很正常吗我就是在获取最新的这个数据但是大家要注意这里是一个事务事务具有隔离性这里破坏的就是隔离性从表现上来看。这里我一个事务具有原子性两次操作查询的目标应当一致所以不可重复读和幻读我觉得也是破坏原子性的一个体现这个不是标准只是帮助理解这个话不要记因为这里本身在一个线程里面这里的两个操作看起来就是无缝执行的所以其实符合原子性脏读这个就是️线程A正在修改这个SQL数据这个时候事务没有提交这个时候线程B来查询这个数据但是这个时候获取到了线程A还未修改的数据不可重复读这个指的就是线程A有两次查询操作第一次查询比如是100但是中途线程B来修改数据把这里的100修改成了1000然后A第二次执行查询操作发现这里是1000同一个事务里面两次读取不一致幻读SQL经典定义这个指的就是比如我线程A还是查询两次第一次查询当前有5行数据线程B再插入一条对空白线程A 不操作的地方做操作第二次线程A查询发现有6条数据就像出现了幻觉这个就叫幻读MySQL RR级别 InnoDB独有现象这里指的就是比如我线程A读一下我这个数据里面只有1 3 5然后线程B插入一个4然后我这个A的第二个操作恰好也是插入一个4发现冲突线程A再读发现只有1 3 5没有4。这个就像幻觉一样这个也是幻读隔离级别这里企业大多使用RC或者RR大部分都是RC因为这里越往下并发性能也就越差特别是这里的串行化堪比单线程的想看这里的实现原理这里必须得看看下面的MVCC读未提交read uncommited意思就是如果这个时候你没有提交我也可以看到你的数据读已提交read committed RC这里的意思就是我只能看到你提交后的数据**这里的实现是引入了快照读**可重复读repeatable read RR意思就是这里你开始我看到你什么样执行过程中也看你什么样**这里的解决方案是一个事务只有一个快照**串行化serializable这个就是多个事务之间发生读写冲突的时候得排队一个事务一个事务来堪比单线程这个就是不同级别可能出现的问题但是MySQL的RR可以很大程度避免幻读但是无法完全避免幻读MVCC这个东西就是数据库来实现上面四种隔离级别的利器和基础了当前读和快照读快照读只有普通的select语句是快照读这里会生成一个快照里面只有已提交的事务当前读比如select … for update或者insert或者update或者delete都是当前读这里就会提交这里的数据。现在看是不是感觉没区别等一会MVCC到RR的时候你们就会发现明显区别**其实也很明显只有快照读可以访问版本链而当前读直接读最新数据来**而RR级别为什么不可以完全解决幻读也是因为这里的当前读和快照读的冲突数据库隐藏字段首先是数据库隐藏字段DB_TRX_ID这个是最近修改事务的IDDB_ROLL_PTR这个是回滚指针指向上一次事务的修改也就是指向上一个版本DB_ROW_ID这个是隐藏主键如果没有主键字段就是用这个做主键undolog版本链概念undolog是事务原子性的基础当我们执行一条非查询的语句比如insert这里的undolog就会生成一个对应的delete用于回滚而这里的日志更新时机就是在执行语句真正执行前这里就更新了日志防止你断个电啥的造成损失即使宕机也可以回滚而undolog的第二个作用就是来帮我生成一个版本链如图所示这里就像链表这里的头节点是最新的记录每次使用头插法来插入新版本快照读的相关属性这里直接贴一张来自黑马程序员的图我们一会讲案例的时候回来查数据链访问规则快照读注意这里RR和RC的区别就是在于这里RR每个事务只有一张快照但是RC每次查询都会有快照这个时候就会导致一个问题就是每次RC其实查询到的就是最新的已提交的数据这个时候就触发了不可重复读和幻读但是不用判断这里的数据链访问规则所以说这里的数据链访问规则就是给RR准备的那我们看看这里是如何访问数据链的这里首先你要先去上面看ReadView的属性在这个数据链访问规则还是很重要的比如这里我们的m_ids就是135但是要注意快照读访问的就是已提交的事务这里相当于一个黑名单关于这里的第一条规则没什么说的我创建的我还不可以访问了关于第二条规则这里就是不在黑名单里面你就相当于提交了所以这里可以访问如果大于max就说明你不在我这个快照里面无法读第四个就说这个如果不在黑名单就可以访问临键锁 next-key锁当前读在RR隔离级别里面对于当前读的防范就在这里这里就是临键锁行锁间隙锁行锁可以防止不可重复读的问题间隙锁可以避免大部分幻读问题比如访问13这里就会(-∞,1], (1,3], (3,∞)锁住防止别的线程插入数据但是相应的这里的并发性能就会下降因为其他的线程阻塞了我们来一个例子理解一下我们就看一下这个InnoDB在RR对临键锁的优化吧比如我们现在有一组数据1020203040对应id12456对于第一种情况我们找一个id3吧就临键锁锁住这里的2-4然后发现3不在把临键锁退化为间隙锁防止这里另一个进程插入一个3导致幻读对于第二种情况这里我们建立一个索引吧然后通过这个索引来找这里20的值这里(1020]加临键锁(20,20]临键锁,(20-30]加临键锁。如果这里没有20的话这里就会锁1030并且退化为间隙锁第一种情况- 易困惑点这里其实只需要锁一下这两条20以及这里的间隙就够了。但是这里其实多锁了一个30因为这里数据命中了这里无法从临键锁退化到间隙锁对于第三种情况这里我们查询id4的值吧这里(-∞,1]临键锁(12]加临键锁,24加间隙锁- 目的防止对这里id4的值做改动无法解决的冲突RR看似这里我们有undolog版本链来防范快照读的幻读有临键锁来防范当前读的幻读那还有什么幻读是我们干不了的这里能有什么问题。那其实小林coding就给出了两个场景那也就是我们定义里面说的两个定义场景一这里我们线程A来查询一下数据5表中没有所以查询不出来这里线程B插入了一个数据5现在A再来查询快照读当然查不出来这个时候A对数据5做一个更新操作当前读当然可以更新这个时候再去读这个时候发现这个操作id是自己就可以读到了场景二这里我们线程A查询一下100的数据发现有3条然后这个时候B插入一条200这个时候A使用for update查询发现有4条就是我们说的SQL的经典幻读定义这里的解决方案就是这里一旦开启事务就使用for update也就是统一使用当前读其实总结来看就是一个事务使用了当前读和快照读导致两种读法出现冲突尾声其实从企业的方面来看他们不在乎不可重复读和幻读只要不出现脏读就好所以这里其实大家都为了这个并发性能大多业务都是RC而这里的RR只有涉及一些重要业务的时候使用包括一些别的数据库默认都是RC所以说我觉得只需要重点关注一下RR和RC就好