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