MySQL作为常用的关系型数据库,在高并发场景下需要依靠锁机制和并发控制技术来保证数据的一致性,同时尽可能提升系统的处理性能。不同的业务场景对数据操作的要求不同,选择合适的锁和并发控制策略是开发高并发应用的关键。

MySQL锁的基本分类
MySQL的锁可以按照不同的维度进行分类,常见的分类方式包括按锁的粒度、按锁的兼容性以及按锁的实现方式。
按锁粒度分类
锁粒度指的是锁作用的范围大小,MySQL支持三种主要的锁粒度:
- 表锁:锁住整张表,开销小,加锁快,不会出现死锁,但锁冲突概率高,并发度低。适合全表更新、批量数据导入等场景。
- 行锁:锁住表中的某一行或多行记录,开销大,加锁慢,可能会出现死锁,但锁冲突概率低,并发度高。是InnoDB存储引擎的默认锁粒度,适合高并发的单行或少量行操作场景。
- 页锁:锁住相邻的一组记录,开销和加锁速度介于表锁和行锁之间,并发度一般,会出现死锁。在BDB存储引擎中使用较多,目前使用场景较少。
按锁兼容性分类
根据锁之间是否兼容,可以分为共享锁和排他锁:
- 共享锁(S锁):允许事务读取一行数据,多个事务可以同时持有同一行数据的共享锁,但是不能修改被共享锁锁住的数据。
- 排他锁(X锁):允许事务修改或删除一行数据,排他锁和其他锁都不兼容,同一行数据同一时间只能有一个事务持有排他锁。
MySQL并发控制核心技术:MVCC
MVCC即多版本并发控制,是InnoDB实现高并发读的重要机制,它通过在每行记录后面保存两个隐藏的列来实现:一个是行的创建版本号,一个是行的删除版本号。版本号是系统自动递增的事务ID。
MVCC的核心规则如下:
- SELECT查询时,只会读取创建版本号小于等于当前事务ID,并且删除版本号要么为空,要么大于当前事务ID的记录,这样就能保证读取到的记录是在事务开始前就已经存在,且没有被删除的。
- INSERT操作时,会将当前事务ID作为新记录的创建版本号。
- DELETE操作时,会将当前事务ID作为被删除记录的删除版本号,并不会立即物理删除记录。
- UPDATE操作时,会先标记旧记录的删除版本号为当前事务ID,然后插入一条新的记录,新记录的创建版本号为当前事务ID。
MVCC让普通的SELECT查询不需要加锁,大大提升了读操作的并发性能,只有在需要修改数据时才会加锁,这也是MySQL能够支持高并发读写的重要原因。
常见锁的使用示例
共享锁的使用
共享锁通常通过SELECT ... LOCK IN SHARE MODE语句来添加,示例如下:
-- 开启事务 START TRANSACTION; -- 给id为1的用户记录加共享锁 SELECT * FROM user WHERE id = 1 LOCK IN SHARE MODE; -- 此时其他事务也可以给这条记录加共享锁,但是不能加排他锁修改数据 -- 完成操作后提交事务,释放锁 COMMIT;
排他锁的使用
排他锁可以通过SELECT ... FOR UPDATE语句添加,也可以在执行UPDATE、DELETE语句时自动添加,示例如下:
-- 开启事务 START TRANSACTION; -- 给id为1的用户记录加排他锁 SELECT * FROM user WHERE id = 1 FOR UPDATE; -- 此时其他事务不能给这条记录加任何锁,也不能修改数据 -- 执行更新操作 UPDATE user SET balance = balance - 100 WHERE id = 1; -- 提交事务释放锁 COMMIT;
不同场景下的锁策略选择
在实际开发中,需要根据业务场景选择合适的锁和并发控制策略:
| 业务场景 | 推荐锁策略 | 原因 |
|---|---|---|
| 全表数据统计、批量数据更新 | 表锁 | 操作范围大,行锁开销过高,表锁效率更高 |
| 高并发单行数据修改,如余额扣减 | 行级排他锁+MVCC | 行锁冲突概率低,MVCC保证读操作不阻塞,性能最优 |
| 需要读取数据后判断是否修改,避免重复操作 | SELECT ... FOR UPDATE | 加排他锁后其他事务无法修改,避免读取后数据被其他事务变更 |
| 普通查询不需要修改数据 | 不加锁,依靠MVCC读取快照数据 | 不需要加锁,读操作不阻塞写操作,并发性能最高 |
锁相关的注意事项
使用MySQL锁时需要注意避免死锁问题,常见的死锁原因包括两个事务互相持有对方需要的锁,或者加锁顺序不一致。可以通过以下方式减少死锁:
- 尽量让事务按照相同的顺序加锁,避免循环等待。
- 尽量缩小事务的范围,减少锁的持有时间。
- 对于需要加锁的操作,尽量使用低粒度的锁,比如优先使用行锁而不是表锁。
- 可以设置合理的锁等待超时时间,通过
innodb_lock_wait_timeout参数调整,避免事务长时间等待锁。
需要注意的是,MyISAM存储引擎只支持表锁,不支持行锁和MVCC,所以在高并发场景下更推荐使用InnoDB存储引擎,能够获得更好的并发性能。