深入详解MySQL并发控制及事务原理

在如今互联网业务中使用范围最广的数据库无疑还是关系型数据库MySQL,之所以用"还是"这个词,是因为最近几年国内数据库领域也取得了一些长足进步,例如以TIDB、OceanBase等为代表的分布式数据库,但它们暂时还没有形成绝对的覆盖面,所以现阶段还得继续学习MySQL数据库以应对工作中遇到的一些问题,以及面试过程中关于数据库部分的考察。

这边还有各个知识点模块整理文档和更多大厂面试真题,有需要的朋友可以点一点下方链接免费领取

链接:1103806531暗号:CSDN

在这里插入图片描述

今天的内容就和大家聊一聊MySQL数据库中关于并发控制、事务以及存储引擎这几个最核心的问题。本内容涉及的知识图谱如下图所示:
在这里插入图片描述

并发控制

并发控制是一个内容庞大的话题,在计算机软件系统中只要在同一时刻存在多个请求同时修改数据的情况,就都会产生并发控制的问题,例如Java中的多线程安全问题等。在MySQL中的并发控制,主要是讨论数据库如何控制表数据的并发读写。

例如有一张表useraccount,其结构如下:

在这里插入图片描述
此时如果有如下两条SQL语句同一时刻向数据库发起请求:

SQL-A:

update useraccount t set t.account=t.account+100 where username='wudimanong';

SQL-B:

update useraccount t set t.account=t.account-100 where username='wudimanong'

当上述语句都执行完成,正确结果应该是account=100,但在并发情况下,却有可能发生这样的情况:

在这里插入图片描述
那么在MySQL中是如何进行并发控制的呢?实际上与大多数并发控制方式一样,在MySQL中也是利用锁机制来实现并发控制的。

1.MySQL锁类型

在MySQL中主要是通过"读写锁"来实现并发控制。

读锁(read lock):也叫共享锁(share lock),多个读请求可以同时共享一把锁来读取数据,而不会造成阻塞。

写锁(write lock):也叫排他锁(exclusive lock),写锁会排斥其他所有获取锁的请求,一直阻塞,直到完成写入并释放锁。

读写锁可以做到读读并行,但是无法做到写读、写写并行。后面会讲到的事务隔离性就是根据读写锁来实现的!

2.MySQL锁粒度

上面提及的读写锁是根据MySQL的锁类型来划分的,而读写锁能够施加的粒度在数据库中主要体现为表和行,也称为表锁(table lock)、行锁(row lock)。

表锁(table lock):是MySQL中最基本的锁策略,它会锁定整张表,这样维护锁的开销最小,但是会降低表的读写效率。如果一个用户通过表锁来实现对表的写操作(插入、删除、更新),那么先需要获得锁定该表的写锁,那么在这种情况下,其他用户对该表的读写都会被阻塞。一般情况下"alter table"之类的语句才会使用表锁。

行锁(row lock):行锁可以最大程度地支持并发读写,但数据库维护锁的开销会比较大。行锁是我们日常使用最多的锁策略,一般情况下MySQL中的行级锁由具体的存储引擎实现,而不是MySQL服务器层面去实现(表锁MySQL服务器层面会实现)。

3.多版本并发控制(MVCC)

MVCC(MultiVersion Concurrency Control),多版本并发控制。在MySQL的大多数事务引擎(如InnoDB)中,都不只是简单地实现了行级锁,否则会出现这样的情况:"数据A被某个用户更新期间(获取行级写锁),其他用户读取该条数据(获取读锁)都会被阻塞“。但现实情况显然不是这样,这是因为MySQL的存储引擎基于提升并发性能的考虑,通过MVCC数据多版本控制,做到了读写分离,从而实现不加锁读取数据进而做到了读写并行。

以InnoDB存储引擎的MVCC实现为例:

InnoDB的MVCC,是通过在每行记录后面保存两个隐藏的列来实现的。这两个列,一个保存了行的创建时间,一个保存了行的过期时间。当然它们存储的并不是实际的时间值,而是系统版本号。每开启一个新的事务,系统版本号都会自动递增;事务开始时刻的系统版本号会作为事务的版本号,用来和查询到的每行记录的版本号进行比较。

MVCC在MySQL中实现所依赖的手段主要是:“undo log和read view”。

  • undo log :undo log 用于记录某行数据的多个版本的数据。
  • read view :用来判断当前版本数据的可见性

undo log在后面讲述事务还会介绍到。关于MVCC的读写原理示意图如下:

在这里插入图片描述
上图演示了MySQL InnoDB存储引擎,在REPEATABLE READ(可重复读)事务隔离级别下,通过额外保存两个系统版本号(行创建版本号、行删除版本号)实现MVCC,从而使得大多数读操作都可以不用再加读锁。这样的设计使得数据读取操作更加简单、性能更好。

那么在MVCC模式下数据读取操作是如何保证数据读取正确的呢?以InnoDB为例,Select时会根据以下两个条件检查每行记录:

  • 只查找版本号小于或等于当前事务版本的数据行,这样可以确保事务读取的行要么是在事务开始前已经存在,要么是事务自身插入或者修过的。
  • 行的删除版本号要么未定义,要么大于当前事务版本号。这样可以确保事务读取到的行,在事务开始之前未被删除。

只有符合上述两个条件的记录,才能返回作为查询的结果!以图中示范的逻辑为例,写请求将account变更为200的过程中,InnoDB会再插入一行新记录(account=200),并将当前系统版本号作为行创建版本号(createVersion=2),同时将当前系统版本号作为原来行的行删除版本号(deleteVersion=2),那么此时关于这条数据有两个版本的数据副本,具体如下:

在这里插入图片描述

假如现在写操作还未结束,事务对其他用户暂不可见,按照Select检查条件只有accout=100的记录才符合条件,因此查询结果会返回account=100的记录!

上述过程就是InnoDB存储引擎关于MVCC实现的基本原理,但是后面需要注意MVCC多版本并发控制的逻辑只能工作在“REPEATABLE READ(可重复读)和READ COMMITED(提交读)”两种事务隔离级别下。其他两个隔离级别都与MVCC不兼容,因为READ UNCOMMITED(未提交读)总是读取最新的数据行,而不是符合当前事务版本的数据行;而SERIALIZABLE则会对所有读取的行都加锁,也不符合MVCC的思想。

总结

由于时间关系,没有写的很详细,后半部分 MySQL事务、MySQL存储引擎没有展示,有需要完整版的朋友可以点一点下方链接免费领取

链接:1103806531暗号:CSDN

在这里插入图片描述
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_48655626/article/details/109117233