mySQL--深入理解事务隔离级别

<section class="xmteditor" style="display:none;" data-tools="新媒体管家" data-label="powered by xmt.cn"></section><p class="a X2"><span class="a X2"></span></p><section class="xmt-style-block" data-style-type="1" data-tools="新媒体排版" data-id="13006"><section class="KolEditor" style="border-width: 0px;border-style: none;border-color: initial;padding: 0px;margin-top: 20px;"><section style="border-width: 0px;border-style: none;border-color: initial;"><section style="border-width: 0px;border-style: initial;border-color: initial;text-align: center;"><section style="display: inline-block;color: inherit;"><section class="active brush" style="margin-top: 0.2em;padding-right: 0.5em;padding-bottom: 5px;padding-left: 0.5em;color: rgb(255,140,0);font-size: 1.8em;line-height: 1;border-bottom-width: 1px;border-bottom-style: solid;border-color: rgb(255,140,0);"><p style="font-size:1em;color: rgb(255,140,0);">深入理解数据库事务隔离</p></section><section style="margin: 5px 1em;font-size: 1em;line-height: 1;color: rgb(33, 33, 34);border-color: rgb(33, 33, 34);"><p class="active brush" style="color: rgb(121,121,121);font-family: 'Microsoft YaHei', Arial, Helvetica, sans-serif;font-size: 15px;line-height: 24px;text-align: center;">脏读、幻读、不可重复读实战演练</p></section></section></section><section style="width: 0px;height: 0px;clear: both;"></section></section><p style="white-space: normal;">
</p></section></section><p class="a X2"><span style="color: rgb(0, 0, 0);"></span></p><p style="margin: 10px auto;font-family: Verdana, Arial, Helvetica, sans-serif;font-size: 14px;white-space: normal;background-color: rgb(255, 255, 255);"><span style="color: rgb(0, 0, 0);"><strong><span style="color: rgb(0, 0, 0);font-size: 16px;">一、事务的基本要素(ACID)</span></strong></span></p><p style="margin: 10px auto;font-family: Verdana, Arial, Helvetica, sans-serif;font-size: 14px;white-space: normal;background-color: rgb(255, 255, 255);"><span style="color: rgb(255, 0, 0);"><strong><span style="font-size: 16px;">  </span></strong><strong><span style="color: rgb(0, 0, 0);font-size: 14px;">1、原子性(Atomicity):事务开始后所有操作,要么全部做完,要么全部不做,不可能停滞在中间环节。事务执行过程中出错,会回滚到事务开始前的状态,所有的操作就像没有发生一样。也就是说事务是一个不可分割的整体,就像化学中学过的原子,是物质构成的基本单位。</span></strong></span></p><p style="margin: 10px auto;font-family: Verdana, Arial, Helvetica, sans-serif;font-size: 14px;white-space: normal;background-color: rgb(255, 255, 255);"><span style="color: rgb(255, 0, 0);"><strong><span style="color: rgb(0, 0, 0);font-size: 14px;">   </span></strong><strong><span style="color: rgb(0, 0, 0);font-size: 14px;">2、一致性(Consistency):事务开始前和结束后,数据库的完整性约束没有被破坏 。比如A向B转账,不可能A扣了钱,B却没收到。
</span></strong></span></p><p style="margin: 10px auto;font-family: Verdana, Arial, Helvetica, sans-serif;font-size: 14px;white-space: normal;background-color: rgb(255, 255, 255);"><span style="color: rgb(255, 0, 0);"><strong><span style="color: rgb(0, 0, 0);font-size: 14px;">   3、隔离性(Isolation):同一时间,只允许一个事务请求同一数据,不同的事务之间彼此没有任何干扰。比如A正在从一张银行卡中取钱,在A取钱的过程结束前,B不能向这张卡转账。</span></strong></span></p><p style="margin: 10px auto;font-family: Verdana, Arial, Helvetica, sans-serif;font-size: 14px;white-space: normal;background-color: rgb(255, 255, 255);"><span style="color: rgb(255, 0, 0);"><strong><span style="color: rgb(0, 0, 0);font-size: 14px;">   4、持久性(Durability):事务完成后,事务对数据库的所有更新将被保存到数据库,不能回滚。</span></strong></span></p><p style="margin: 10px auto;font-family: Verdana, Arial, Helvetica, sans-serif;font-size: 14px;white-space: normal;background-color: rgb(255, 255, 255);"> </p><p style="margin: 10px auto;font-family: Verdana, Arial, Helvetica, sans-serif;font-size: 14px;white-space: normal;background-color: rgb(255, 255, 255);"><span style="font-size: 16px;color: rgb(0, 0, 0);"><strong>二、事务的并发问题</strong></span></p><p style="margin: 10px auto;font-family: Verdana, Arial, Helvetica, sans-serif;font-size: 14px;white-space: normal;background-color: rgb(255, 255, 255);"><span style="color: rgb(255, 0, 0);"><strong><span style="color: rgb(0, 0, 0);font-size: 14px;">  1、脏读:事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据</span></strong></span></p><p style="margin: 10px auto;font-family: Verdana, Arial, Helvetica, sans-serif;font-size: 14px;white-space: normal;background-color: rgb(255, 255, 255);"><span style="color: rgb(255, 0, 0);"><strong><span style="color: rgb(0, 0, 0);font-size: 14px;">  2、不可重复读:事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果 不一致。</span></strong></span></p><p style="margin: 10px auto;font-family: Verdana, Arial, Helvetica, sans-serif;font-size: 14px;white-space: normal;background-color: rgb(255, 255, 255);"><span style="color: rgb(255, 0, 0);"><strong><span style="color: rgb(0, 0, 0);font-size: 14px;">  3、幻读:系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读。</span></strong></span></p><p style="margin: 10px auto;font-family: Verdana, Arial, Helvetica, sans-serif;font-size: 14px;white-space: normal;background-color: rgb(255, 255, 255);"><span style="color: rgb(0, 0, 0);"><strong><strong style="color: rgb(255, 0, 0);font-family: Verdana, Arial, Helvetica, sans-serif;font-size: 14px;white-space: normal;background-color: rgb(255, 255, 255);"><span style="font-size: 14px;">小结:不可重复读的和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除。解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表</span></strong></strong></span></p><p style="margin: 10px auto;font-family: Verdana, Arial, Helvetica, sans-serif;font-size: 14px;white-space: normal;background-color: rgb(255, 255, 255);"> </p><p style="margin: 10px auto;font-family: Verdana, Arial, Helvetica, sans-serif;font-size: 14px;white-space: normal;background-color: rgb(255, 255, 255);"><strong><span style="font-size: 16px;color: rgb(0, 0, 0);">三、MySQL事务隔离级别</span></strong></p><table width="615" height="92"><tbody><tr><td style="padding: 3px;border-color: silver;border-collapse: collapse;">事务隔离级别</td><td style="padding: 3px;border-color: silver;border-collapse: collapse;">脏读</td><td style="padding: 3px;border-color: silver;border-collapse: collapse;">不可重复读</td><td style="padding: 3px;border-color: silver;border-collapse: collapse;">幻读</td></tr><tr><td style="padding: 3px;border-color: silver;border-collapse: collapse;">读未提交(read-uncommitted)</td><td style="padding: 3px;border-color: silver;border-collapse: collapse;">是</td><td style="padding: 3px;border-color: silver;border-collapse: collapse;">是</td><td style="padding: 3px;border-color: silver;border-collapse: collapse;">是</td></tr><tr><td style="padding: 3px;border-color: silver;border-collapse: collapse;">不可重复读(read-committed)</td><td style="padding: 3px;border-color: silver;border-collapse: collapse;">否</td><td style="padding: 3px;border-color: silver;border-collapse: collapse;">是</td><td style="padding: 3px;border-color: silver;border-collapse: collapse;">是</td></tr><tr><td style="padding: 3px;border-color: silver;border-collapse: collapse;">可重复读(repeatable-read)</td><td style="padding: 3px;border-color: silver;border-collapse: collapse;">否</td><td style="padding: 3px;border-color: silver;border-collapse: collapse;">否</td><td style="padding: 3px;border-color: silver;border-collapse: collapse;">是</td></tr><tr><td style="padding: 3px;border-color: silver;border-collapse: collapse;">串行化(serializable)</td><td style="padding: 3px;border-color: silver;border-collapse: collapse;">否</td><td style="padding: 3px;border-color: silver;border-collapse: collapse;">否</td><td style="padding: 3px;border-color: silver;border-collapse: collapse;">否</td></tr></tbody></table><p style="margin: 10px auto;font-family: Verdana, Arial, Helvetica, sans-serif;font-size: 14px;white-space: normal;background-color: rgb(255, 255, 255);">
</p><p>查看mysql默认事务隔离级别:</p><p><img class="" data-ratio="0.3564356435643564" src="http://upload-images.jianshu.io/upload_images/10597162-5e7846b1d38da7ca" data-type="png" data-w="303" height="81.0pt" width="227.25pt"></p><p class="a a5"><span class="a a5">1、 读未提交例子</span></p><p>(1)打开客户端A,设置事务模式为read uncommitted,查询表</p><p><img class="" data-ratio="0.45076923076923076" src="http://upload-images.jianshu.io/upload_images/10597162-2703a38472750b83" data-type="png" data-w="650" height="187.2pt" width="415.3pt"></p><p class="a a5"><span class="a a5">(2) 在A提交事务之前,打开客户端B,更新表</span></p><p><img class="" data-ratio="0.1527777777777778" src="http://upload-images.jianshu.io/upload_images/10597162-e44a9895ac66422d" data-type="png" data-w="648" height="63.45pt" width="415.3pt"></p><p><img class="" data-ratio="0.3865979381443299" src="http://upload-images.jianshu.io/upload_images/10597162-7e16947b34a2f0f4" data-type="png" data-w="582" height="160.55pt" width="415.3pt"></p><p>(3)此时,B的事务没提交,但是A已经可以查到B更新的数据</p><p><img class="" data-ratio="0.45187165775401067" src="http://upload-images.jianshu.io/upload_images/10597162-272001ae98acaccc" data-type="png" data-w="374" height="126.75pt" width="280.5pt"></p><p>(4)一旦B的事务因某原因回滚,则A查到的数据就是脏数据。</p><p><img class="" data-ratio="0.5717821782178217" src="http://upload-images.jianshu.io/upload_images/10597162-286156e2fd78519f" data-type="png" data-w="404" height="173.25pt" width="303.0pt"></p><p>(5)在A执行更新语句,在不知道有其他事务回滚时,会发现结果好奇怪。要解决这个问题可以采用读已提交的事务隔离级别。</p><p><img class="" data-ratio="0.7269681742043551" src="http://upload-images.jianshu.io/upload_images/10597162-412b9b9121479885" data-type="png" data-w="597" height="301.9pt" width="415.3pt"></p><p class="a a5"><span class="a a5">2、 读已提交</span></p><p class="a a5"><span class="a a5">(1) 在客户端A设置事务模式为read committed;</span></p><p><img class="" data-ratio="0.4588607594936709" src="http://upload-images.jianshu.io/upload_images/10597162-6f2f4e53f271c2e8" data-type="png" data-w="632" height="190.55pt" width="415.3pt"></p><p class="a a5"><span class="a a5">(2) 在客户端A事务提交之前,打开客户端B,起事务更新表</span></p><p><img class="" data-ratio="0.5569422776911076" src="http://upload-images.jianshu.io/upload_images/10597162-8fa8a1c71b2ad908" data-type="png" data-w="641" height="231.3pt" width="415.3pt"></p><p class="a a5"><span class="a a5">(3) B的事务还未提交,A不能查到已经更新的数据,解决了脏读问题:</span></p><p><img class="" data-ratio="0.9102244389027432" src="http://upload-images.jianshu.io/upload_images/10597162-f8447947d270110c" data-type="png" data-w="401" height="273.75pt" width="300.75pt"></p><p class="a a5"><span class="a a5">(4) 此时提交客户端B的事务</span></p><p><img class="" data-ratio="0.5282051282051282" src="http://upload-images.jianshu.io/upload_images/10597162-10cee51839b6cb9a" data-type="png" data-w="390" height="154.5pt" width="292.5pt"></p><p class="a a5"><span class="a a5">(5) A执行与上一步相同的查询,结果发现与上一步不同,这就是不可重复读的问题:</span></p><p><img class="" data-ratio="0.997275204359673" src="http://upload-images.jianshu.io/upload_images/10597162-b2b70ed9e3da8bd6" data-type="png" data-w="367" height="274.5pt" width="275.25pt"></p><p class="a a5"><span class="a a5">3、 可重复读</span></p><p class="a a5"><span class="a a5">(1) 打开客户端A,设置事务模式为repeatable read。</span></p><p><img class="" data-ratio="0.4452887537993921" src="http://upload-images.jianshu.io/upload_images/10597162-91eeca9c15e51b01" data-type="png" data-w="658" height="184.95pt" width="415.3pt"></p><p class="a a5"><span class="a a5">(2) 在A事务提交之前,打开客户端B,更新表account并提交:</span></p><p><img class="" data-ratio="0.6629746835443038" src="http://upload-images.jianshu.io/upload_images/10597162-74b3a7e4d1a9335b" data-type="png" data-w="632" height="275.35pt" width="415.3pt"></p><p class="a a5"><span class="a a5">(3) 在客户端A执行步骤1的查询,zhangsan的balance依然是450与步骤(1)查询结果一致,没有出现不可重复读的问题;接着执行update balance = balance – 50 where id=4;balance没有变成450-50=400;zhangsan的balance值用的是步骤(2)中的400来算的,所以是350。数据的一致性没有被破坏。</span></p><p><img class="" data-ratio="0.7166123778501629" src="http://upload-images.jianshu.io/upload_images/10597162-4e88c0ca5c4a5f09" data-type="png" data-w="614" height="297.6pt" width="415.3pt"></p><p>(4)在客户端A提交事务</p><p><img class="" data-ratio="0.6063829787234043" src="http://upload-images.jianshu.io/upload_images/10597162-8b713a8d708ddc1a" data-type="png" data-w="376" height="171.0pt" width="282.0pt"></p><p class="a a5"><span class="a a5">(5) 在客户端A开启事务,随后在客户端B开启事务,新增一条数据。提交</span></p><p><img class="" data-ratio="0.21896792189679218" src="http://upload-images.jianshu.io/upload_images/10597162-3688326190dec0b8" data-type="png" data-w="717" height="90.95pt" width="415.3pt"></p><p class="a a5"><span class="a a5">(6) 在A计算balance之和,值为350+16000+2400=18750,没有把客户端B新增的数据算进去,客户端A提交后再计算balance之和,居然变成了19350,这时因为把客户端B的600算进去了。站在客户的角度,客户是看不到客户端B的,他会觉得天上掉馅饼了,多了600块,这就是幻读,站在开发者的角度,数据的一致性没有破坏。但是在应用程序中,我们的代码可能会把18750提交给用户了,如果一定要避免这种小概率状况的发生,那么就要采取“串行化”的事务隔离级别了。</span></p><p><img class="" data-ratio="0.7237113402061855" src="http://upload-images.jianshu.io/upload_images/10597162-0227aa92a8ddd2c4" data-type="png" data-w="485" height="263.25pt" width="363.75pt"></p><p class="a a5"><span class="a a5">4、 串行化</span></p><p class="a a5"><span class="a a5">(1) 打开客户端A,设置事务隔离级别为serializable并开启事务。</span></p><p><img class="" data-ratio="0.5016233766233766" src="http://upload-images.jianshu.io/upload_images/10597162-f801bbd3f519f476" data-type="png" data-w="616" height="208.3pt" width="415.3pt"></p><p class="a a5"><span class="a a5">(2) 打开客户端B,同样设置事务隔离级别为serializable,开启事务插入数据,报错。表被锁了,插入失败,mysql中事务隔离级别为serializable时会锁表,因此不会出现幻读的情况,这种隔离级别并发性很低,开发中很少用到。</span></p><p><img class="" data-ratio="0.22207792207792207" src="http://upload-images.jianshu.io/upload_images/10597162-70d1b50e3f902f04" data-type="png" data-w="770" height="92.25pt" width="415.3pt"></p><p style="margin: 10px auto;font-family: Verdana, Arial, Helvetica, sans-serif;font-size: 14px;white-space: normal;background-color: rgb(255, 255, 255);"><span style="font-size: 18px;"><strong><span style="color: rgb(255, 0, 0);">补充:</span></strong></span></p><p style="margin: 10px auto;font-family: Verdana, Arial, Helvetica, sans-serif;font-size: 14px;white-space: normal;background-color: rgb(255, 255, 255);"><span style="font-size: 18px;"><strong><span style="color: rgb(255, 0, 0);">  1、SQL规范所规定的标准,不同的数据库具体的实现可能会有些差异</span></strong></span></p><p style="margin: 10px auto;font-family: Verdana, Arial, Helvetica, sans-serif;font-size: 14px;white-space: normal;background-color: rgb(255, 255, 255);"><span style="font-size: 18px;"><strong><span style="color: rgb(255, 0, 0);">  2、mysql中默认事务隔离级别是可重复读时并不会锁住读取到的行</span></strong></span></p><p style="margin: 10px auto;font-family: Verdana, Arial, Helvetica, sans-serif;font-size: 14px;white-space: normal;background-color: rgb(255, 255, 255);"><span style="font-size: 18px;"><strong><span style="color: rgb(255, 0, 0);"><strong><strong>  3、事务隔离级别为读提交时,写数据只会锁住相应的行</strong></strong></span></strong></span></p><p style="margin: 10px auto;font-family: Verdana, Arial, Helvetica, sans-serif;font-size: 14px;white-space: normal;background-color: rgb(255, 255, 255);"><span style="font-size: 18px;"><strong><span style="color: rgb(255, 0, 0);">  4<strong>、事务隔离级别为可重复读时,如果有索引(包括主键索引)的时候,以索引列为条件更新数据,会存在间隙锁间隙锁、行锁、下一键锁的问题,从而锁住一些行;如果没有索引,更新数据时会锁住整张表。</strong></span></strong></span></p><p style="margin: 10px auto;font-family: Verdana, Arial, Helvetica, sans-serif;font-size: 14px;white-space: normal;background-color: rgb(255, 255, 255);"><span style="font-size: 18px;"><strong><span style="color: rgb(255, 0, 0);">  5、事务隔离级别为串行化时,读写数据都会锁住整张表</span></strong></span></p><p style="margin: 10px auto;font-family: Verdana, Arial, Helvetica, sans-serif;font-size: 14px;white-space: normal;background-color: rgb(255, 255, 255);"><span style="font-size: 16px;"><strong><span style="color: rgb(255, 0, 0);">   6<strong>、隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大,鱼和熊掌不可兼得啊。对于多数应用程序,可以优先考虑把数据库系统的隔离级别设为Read Committed,它能够避免脏读取,而且具有较好的并发性能。尽管它会导致不可重复读、幻读这些并发问题,在可能出现这类问题的个别场合,可以由应用程序采用悲观锁或乐观锁来控制。</strong></span></strong></span></p><p>
</p>

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,524评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,869评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,813评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,210评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,085评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,117评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,533评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,219评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,487评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,582评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,362评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,218评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,589评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,899评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,176评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,503评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,707评论 2 335

推荐阅读更多精彩内容