鼎文哥 發表於 2025-12-22 12:00:37

一文简单介绍mysql的事务、锁以及MVCC

<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li><a href="#_label0">四种隔离级别</a></li><li><a href="#_label1">MVCC</a></li><li><a href="#_label2">快照读/当前读</a></li><li><a href="#_label3">锁和MVCC对比</a></li><li><a href="#_label4">总结&nbsp;</a></li></ul></div><p class="maodian"><a name="_label0"></a></p><h2>四种隔离级别</h2>
<p>一般默认是RR,即可重复读。如果是互联网公司,为了避免锁等待,一般是RC。</p>
<ol><li><p>读未提交(Read Uncommitted)<br />会导致脏读。读到了其他事务还没提交的数</p></li><li><p>读已提交RC(Read Committed)<br />一个事务只能读取到其他事务已经提交的更改。<br />会导致不可重读:由于另一个事务提交的影响,导致两次读取的结果不一样。</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122211563426.png" /></p></li><li><p>可重复读RR(Repeatable Read)<br />默认的隔离级别。在这个级别下,保证在同一个事务内,多次读取<strong>同一数据</strong>的结果是一致的。<br />但是会有幻读问题,幻读是一种特殊的不可重复读。为啥说是特殊的不可重复读呢?因为幻读是<strong>范围性的不可重复读</strong>。事务A执行select &hellip;where前后,如果其他事务执行insert语句插入了满足where条件的记录,那么事务A的两次查询结果会不一样。<br />需要注意的是,mysql添加了next-key临键锁(行锁+间隙锁)来解决了幻读。<br />4.可序列化<br />读加共享锁,写加排他锁,读写互斥。使用的悲观锁的理论,实现简单,数据更加安全,但是并发能力非常差。如果你的业务并发的特别少或者没有并发,同时又要求数据及时可靠的话,可以使用这种模式。</p></li></ol>
<p class="maodian"><a name="_label1"></a></p><h2>MVCC</h2>
<p>明确一点,锁、MVCC这些东西都是为了保证ACID的。</p>
<p>锁机制是通过锁定资源(如表、行)来防止并发操作之间的冲突,确保数据一致性。</p>
<p>加锁完全可以解决上面提到的脏读、不可重复读、幻读的问题。就像&ldquo;可序列化&rdquo;隔离级别一样,如果什么都加锁,会导致读写时,必然有一方等待,降低了并发能力。所以为了提高性能,基于乐观锁思想,才采用了MVCC来解决问题。所以数据库是乐观锁+悲观锁一起来使用的。<br />MVCC(多版本并发控制)则是通过维护数据的多个版本,让读操作无需等待写操作完成,从而提高并发性能,同时保证事务隔离性。(会将每一次修改的数据用一个链表链接好,相当于历史记录)。mysql 数据库中,每一条数据都保存一个创建时的版本号和一个删除版本号,MVCC就是根据这两个来发挥作用的。</p>
<p>多版本并发控制的一个简单使用就是,查询时<code>select version,xxx,xx where id = 1</code>读出来,此时version=1。修改数据时版本号+1,<code>update xx ... where id = 1 and version =2</code>,如果返回1说明修改成功,如果返回0,可以提示用户有冲突等。</p>
<p class="maodian"><a name="_label2"></a></p><h2>快照读/当前读</h2>
<ul><li>快照读:读取的是历史数据,非最新的数据</li><li>当前读 :读取最新数据</li></ul>
<ul><li>快照读:就是select<br />select * from table &hellip;.;</li><li>当前读:特殊的读操作,插入/更新/删除操作,属于当前读,处理的都是当前的数据,需要加锁。<br />&nbsp;<div class="jb51code"><pre class="brush:sql;">select * from table where ? lock in share mode;
select * from table where ? for update;
insert;
update ;
delete;</pre></div></li></ul>
<p>MVCC:解决了读操作时的不可重复读和幻读<br />临键锁:解决了写操作时的幻读(当前读、更新、删除)。<br />为啥这么说呢?<br />看下,在RR隔离级别下,如何解决不可重复读、幻读呢?可重复读其实读到的是历史数据,这叫快照读。只会读取历史数据,不用加锁。其他事务此时insert或update或delete的话,他们的创建版本号或者删除版本号会改变。此时insert的话,假如创建版本号是10。而另一个RR事务版本号是5。MVCC规定,事务版本号是5的话,select的时候,只能查到 创建版本号&lt;=5的数据。所以不会查到创建版本号是10的数据。其实这也解决了幻读。从这里可以看出,MVCC只能解决快照读场景下面的幻读,不能解决当前读场景下的幻读。举个例子:<br />&middot;SELECT &hellip; FOR UPDATE&middot;,这是当前读,直接加锁需要读取最新的数据,那么MVCC就没啥用了。此时再插入一行新记录,那么select也会查出该条记录来。所以该场景下出现了临键锁。</p>
<p>注意临键锁是加在索引上面的,如果没有索引,可能会锁全表。</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122211563454.png" /></p>
<p class="maodian"><a name="_label3"></a></p><h2>锁和MVCC对比</h2>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122211563412.png" /></p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122211563484.png" /></p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122211563485.png" /></p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122211563461.png" /></p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122211563414.png" /></p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122211563467.png" /></p>
<p class="maodian"><a name="_label4"></a></p><h2>总结&nbsp;</h2>
頁: [1]
查看完整版本: 一文简单介绍mysql的事务、锁以及MVCC