MySQL的锁机制

2023-12-13 12:26:35

1.简介

MySQL的隔离性是由锁机制来保证的。锁是计算机协调多个进程或线程并发地访问某一资源你的机制。当多线程并发地访问某个数据时,尤其是在涉及金钱等安全敏感性数据的时候,需要保证数据在任意时刻最多只有一个线程可以对其进行修改,从而保证数据的一致性和完整性。

2.MySQL并发事务访问相同记录

并发事务访问相同记录可以大致分为三种情况:

2.1 读-读情况

读-读情况就是多个事务同时读取某条或多条相同的记录,由于读操作本身不会改变数据,因此是不存在任何数据安全问题的。

2.2 写-写情况

写-写情况即并发事务相继对相同的记录进行修改,在这种情况下会产生脏写的问题,任何一种隔离级别都不允许这种情况的产生,因此多个未提交的事务对同一条记录进行修改时,会让它们排队去执行,这个排队操作是通过锁来保证。这个所谓的锁其实是一个内存中的结构,在事务执行前本来是没有锁的,也就是说一开始是没有锁结构和记录进行关联的,如图所示:
在这里插入图片描述
当一个事物想要对这条记录进行改动的时候,首先会看看内存中有没有与这条记录关联的表结构,当没有的时候就会在内存中生成一个锁结构与之关联。比如事务T1需要对记录进行修改,就需要生成一个索结构与之关联,如下图:
在这里插入图片描述
在锁结构里有很多信息,为了简化理解,就拿两个比较重要的字段展示:

  • trx信息:代表这个锁结构是哪个事务生成的。
  • is waiting:表示当前事务是否在等待

当T1改动这条记录后,就会生成一个索结构与对应的记录进行关联,由于之前没有别的事务对该记录进行修改,所以is waiting是false。我们把这个场景称之为获取锁成功,之后就可以进行后续操作了。
在T1事务对该记录进行提交之前,事务T2也想对该记录进行修改,那么会先看看有没有锁结构与该记录进行关联,发现存在锁结构与该记录关联,那么T2会再生成一个自己的索结构与该记录进行关联,不过此时的is wating则为true,因为事务T2需要等待事务T1提交后才能对该记录进行修改。这个场景我们就称之为获取锁失败。如下图:
在这里插入图片描述
在事务T1提交后,会将与该记录关联的锁结构释放掉,然后查看是否有其他线程等待获取锁,发现T2事务在等待获取锁,会将T2锁结构的is wating改为false,且唤醒T2事务对应的线程,然后T2事务就可以正常执行了。如下图:
在这里插入图片描述

2.3 读-写情况

读-写或者写-读的情况,即一个事物在读取某条记录的时候,其他事务在对该记录进行修改,那么可能会产生 脏读、不可重复读、幻读的问题。MySQL在RR级别就解决了幻读的问题。

2.4 并发问题的读解决方案

怎么解决脏读、不可重复读、幻读的问题呢?有两种解决方案。

  • 方案一:读操作使用MVCC,写操作加锁
    所谓MVCC就是多版本并发控制,当进行常规的select操作(即简单的select 语句,不带for update或者lock in share mode)的时候,这时是快照读。此时在开启事务的时候会维护一个当前活跃事务(就是已开启但是还未提交的事务)列表-trx_list,里面的记录了当前活跃事务的id,由于事务的id是随着开启时间递增的,所以在最小事务id之前的事务都已经提交了,这些事务的id对于当前事务来说自然是可见的。最大事务id则是系统将要分配的下一个事务id,所以在最大事务id之后的事务对于当前事务来说都是未开启的,这些事务所做的修改对于当前事务来说自然是不可见的。同时记录在undo log中维护了各个事务对其修改的历史版本,所以根据事务id之间的关系就可以判断当前事务应该看到该记录的哪个事务版本的数据。由于当前读在开启事务的时候会生成一个ReadView,其中维护了上述的trx_list,且在本事务中多次进行查询都是用的最初的ReadView,因此在本事务中不论读多少次,某个记录的值都不会变,这就解决了脏读、不可重复读的问题。

  • 方案二:读、写都加锁
    在一些业务场景中,不允许读到记录的旧版本数据,每次都必须去读取记录的最新版本。比如在银行的存钱业务场景中,需要把用户当前的余额取出来,然后再加上本次存的钱,最后再把新的余额写到数据库中,这个过程中就不允许别的事务再读取用户的余额了。你可能会想为什么?想象一下这个场景,余额数据为100,取出来后,在根据本次存的钱50算出新的余额150的过程中,又来一个扣款的操作,假设扣款50,此时扣款的事务读到的当前余额还是100,那么在扣款事务的视角来看,扣完款的余额应该是100-50=50,扣款的操作也会再算完后把余额写回数据库。不论是扣款先写把余额改成50,然后存款后写再把余额改成150。或者是存款先写把余额改成150,然后扣款后写把余额改成50,最后的结果都是不对的。所以这种场景必须读也加锁,让读操作也排队执行。

文章来源:https://blog.csdn.net/weixin_46439731/article/details/134905168
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。