MySQL锁机制讲解:搞懂这些才能让数据库不卡顿
你有没有遇到过这种情况:网站突然变慢,用户提交订单卡住,后台查数据库发现一堆请求堵着不动?很多时候,问题就出在MySQL的锁机制上。别小看这个“锁”,它就像马路的红绿灯,管不好就会堵得水泄不通。
为什么需要锁?
想象一下两个人同时修改同一条库存记录,一个在减库存,一个在查库存,数据对不上怎么办?MySQL通过锁来控制多个操作对同一数据的访问顺序,避免脏读、不可重复读和幻读这些问题。
常见的锁类型有哪些?
MySQL里的锁分好几种,最常见的是表锁和行锁。表锁就是整个表都被锁定,其他操作只能排队等。比如你执行一条ALTER TABLE语句,整张表都动不了,这时候前端页面可能就卡住了。
行锁更精细,只锁住某一行数据。比如两个用户同时下单买不同商品,各自锁住对应的库存行,互不影响。这就是InnoDB引擎的优势,支持行级锁,适合高并发场景。
共享锁和排他锁的区别
共享锁(S锁)允许其他事务读,但不能改。比如你用SELECT ... LOCK IN SHARE MODE查询一条数据,别人还能读,但想UPDATE就得等着。
排他锁(X锁)更霸道,自己能读能改,别人啥都不能干。执行UPDATE、DELETE或者SELECT ... FOR UPDATE时,就会加上排他锁。
SELECT * FROM products WHERE id = 100 FOR UPDATE;这条语句会把id为100的这行锁住,直到当前事务结束,其他事务无法对该行进行修改或加锁。
间隙锁防止幻读
有时候你会发现,明明查了一次没数据,再查一次却多出一条,这就是“幻读”。InnoDB用间隙锁(Gap Lock)来解决这个问题。它不仅锁住已有数据,还锁住索引之间的“空隙”,防止别人插入新数据。
比如你查price在100到200之间的商品,MySQL不仅锁住现有记录,还会锁住这个范围的间隙,不让别人插进来一条price=150的商品。
死锁是怎么发生的?
两个事务互相等待对方释放锁,就形成了死锁。比如事务A锁了商品表的第1行,要去锁第2行;事务B锁了第2行,偏要锁第1行,结果谁也动不了。MySQL会自动检测死锁,挑一个“牺牲品”回滚,让另一个继续执行。
开发时尽量按固定顺序操作多张表,减少死锁概率。比如都先操作用户表再操作订单表,别你先用户我先订单,容易撞车。
如何查看锁状态?
想知道现在谁在占着锁不放?可以用下面这条命令:
SHOW ENGINE INNODB STATUS\G输出信息里有TRANSACTIONS部分,能看到当前活跃事务和锁等待情况。虽然信息有点乱,但关键时刻能帮你定位问题。
线上系统如果频繁出现锁等待,可以查performance_schema.data_locks这张表,直观看到哪些行被谁锁了。
优化建议不是口号
别动不动就SELECT * FROM大表还加FOR UPDATE。锁的时间越长,别人等得越久。尽量缩小事务范围,尽快提交。能用索引就别全表扫描,不然行锁可能升级成表锁,影响一大片。
比如做库存扣减,确保where条件走索引,否则本来只想锁一行,结果锁了整个表,其他商品的订单全被拖累。
合理设计索引,避免不必要的锁冲突。联合索引的顺序也很关键,影响间隙锁的范围。这些细节决定了系统能不能扛住双十一的流量高峰。