MySQL作为常用的关系型数据库,其锁机制直接影响着数据库的并发处理能力和数据安全性。不同的存储引擎支持的锁类型存在差异,其中InnoDB和MyISAM是最常用的两种存储引擎,分别对应不同的锁实现逻辑。

MySQL锁机制基础概念
锁是数据库系统用来控制多个事务同时访问同一资源时的并发控制手段,目的是防止数据不一致问题。MySQL的锁按照粒度可以分为表级锁和行级锁,不同粒度的锁适用场景差异较大。
表锁
表锁是MySQL中粒度最大的锁,会锁定整张表。当对表加表锁时,其他事务无法对该表进行任何写操作,部分存储引擎下读操作也会受限。表锁的开销很小,加锁速度快,不会出现死锁,但是锁冲突概率高,并发度低。
行锁
行锁是MySQL中粒度最小的锁,只会锁定操作涉及的具体行记录。行锁的开销比表锁大,加锁速度慢,可能会出现死锁,但是锁冲突概率低,并发度高,适合高并发的写操作场景。
不同存储引擎的锁支持情况
MySQL不同存储引擎对锁的支持差异很大,以下是两种常用存储引擎的锁特性对比:
| 存储引擎 | 支持表锁 | 支持行锁 | 默认锁粒度 |
|---|---|---|---|
| MyISAM | 是 | 否 | 表锁 |
| InnoDB | 是 | 是 | 行锁 |
表锁的使用与示例
MyISAM存储引擎默认使用表锁,也可以通过显式语句加表锁。表锁分为读锁和写锁两种类型:
- 表读锁:加锁后,当前事务可以读取表数据,其他事务也可以读取表数据,但是所有事务都不能写入表数据,直到读锁释放。
- 表写锁:加锁后,当前事务可以读写表数据,其他事务既不能读也不能写该表数据,直到写锁释放。
显式加表锁的语法如下:
-- 加表读锁 LOCK TABLES user_table READ; -- 加表写锁 LOCK TABLES user_table WRITE; -- 释放所有表锁 UNLOCK TABLES;
以下是一个表锁的使用示例,假设user_table使用MyISAM存储引擎:
-- 事务A加写锁 LOCK TABLES user_table WRITE; -- 事务A可以正常查询和修改 SELECT * FROM user_table WHERE id = 1; UPDATE user_table SET age = 20 WHERE id = 1; -- 此时事务B执行查询会被阻塞,直到事务A释放锁 -- UNLOCK TABLES;
行锁的使用与示例
InnoDB存储引擎支持行锁,行锁是通过给索引项加锁实现的,如果执行的SQL语句没有使用索引,行锁会退化为表锁。InnoDB的行锁也分为共享锁和排他锁两种:
- 共享锁(S锁):允许事务读取一行记录,多个事务可以同时持有同一行的共享锁,但是不能修改被共享锁锁定的行。
- 排他锁(X锁):允许事务修改一行记录,排他锁与其他锁都不兼容,加排他锁后其他事务不能对该行加任何锁。
行锁的常用操作语法如下:
-- 加共享锁,需要在事务中执行 START TRANSACTION; SELECT * FROM user_table WHERE id = 1 LOCK IN SHARE MODE; COMMIT; -- 加排他锁,update、delete语句默认加排他锁,也可以显式加 START TRANSACTION; SELECT * FROM user_table WHERE id = 1 FOR UPDATE; UPDATE user_table SET age = 21 WHERE id = 1; COMMIT;
需要注意的是,行锁只有在事务中才会生效,事务提交或回滚后会自动释放锁。如果SQL语句没有走索引,比如执行UPDATE user_table SET age = 20 WHERE name = 'test';,而name字段没有索引,那么InnoDB会对整张表加锁,此时行锁退化为表锁,会大幅降低并发性能。
行锁与表锁的优缺点对比
| 对比维度 | 表锁 | 行锁 |
|---|---|---|
| 锁粒度 | 整张表 | 单行记录 |
| 加锁开销 | 小 | 大 |
| 加锁速度 | 快 | 慢 |
| 死锁风险 | 无 | 有 |
| 锁冲突概率 | 高 | 低 |
| 并发性能 | 低 | 高 |
如何选择合适的锁机制
在实际业务中,选择锁机制需要结合业务场景和存储引擎特性:
- 如果业务以读操作为主,写操作很少,且数据量不大,可以选择MyISAM存储引擎使用表锁,开销小且足够满足需求。
- 如果业务有大量的并发写操作,且需要保证事务的ACID特性,应该选择InnoDB存储引擎使用行锁,提升并发性能。
- 使用InnoDB行锁时,一定要确保操作的字段有索引,避免出现行锁退化为表锁的情况。
- 如果需要对整张表做批量修改,且业务允许短时间阻塞其他操作,可以显式加表锁,减少行锁的开销和死锁风险。
常见问题与注意事项
在使用MySQL锁机制时,需要注意以下几点:
1. InnoDB的行锁是基于索引实现的,没有索引的字段操作会触发表锁,开发时要检查SQL语句的执行计划,确保走了索引。
2. 行锁在事务中生效,长时间未提交的事务会持有锁很久,容易导致锁等待超时,要尽量避免长事务。
3. 表锁的释放只能通过UNLOCK TABLES或者连接断开,显式加表锁后要及时释放,避免影响其他业务。
以下是一个查看MySQL锁等待状态的SQL语句,可以帮助排查锁冲突问题:
-- 查看当前锁等待信息,需要InnoDB引擎支持 SELECT * FROM information_schema.INNODB_LOCKS; SELECT * FROM information_schema.INNODB_LOCK_WAITS;