MySQL Row and Table Locks


MySQL 行锁和表锁概述

一、前言

MySQL 常用引擎有 MyISAMInnoDB,而 InnoDB 是 MySQL 默认的引擎。MyISAM 仅支持表锁,不支持行锁,而 InnoDB 支持行锁和表锁。

1、如何加锁

MyISAM 在执行查询语句(SELECT)前,会自动给涉及的所有表加 读锁,在执行更新操作(UPDATE、DELETE、INSERT 等)前,会自动给涉及的表加 写锁,这个过程并不需要用户干预,因此用户一般不需要直接用 LOCK TABLE 命令给 MyISAM 表显式加锁。

2、显式加锁

上共享锁(读锁)的写法:lock in share mode,例如:

select name from `user` where age > 20 lock in share mode;

对于 insert、update、delete 会默认加排它锁,对于 select,我们可以这样显式加写锁(排它锁)

上排它锁(写锁)的写法:for update,例如:

select name from `user` where age < 18 for update;

二、表锁

优缺点:

  • 不会出现死锁,但是发生锁冲突的几率高,适合并发量低的场景

MyISAM 引擎

MyISAM在执行查询语句(select)前,会自动给涉及的所有表加读锁,在执行增删改操作前,会自动给涉及的表加写锁。

MySQL 的表级锁有两种模式:

  • 表共享读锁
  • 表独占写锁

读锁会阻塞写,写锁会阻塞读和写

  • 对 MyISAM 表的读操作,不会阻塞其他进程对同一表的读请求,但是会阻塞对同一表的写请求。只有当读锁释放后,才会执行其他进程的操作
  • 对 MyISAM 表的写操作,会阻塞其他进程对同一表的读和写操作,只有当写锁释放后,才会执行其他进程的读写操作

MyISAM 不适合做写为主表的引擎,因为写锁后,其它线程不能做任何操作,大量的更新会使查询很难得到锁,从而造成永远阻塞

三、行锁

优缺点:

  • 会出现死锁,但是发生锁冲突的几率低,适合并发高的场景

在 MySQL 的 InnoDB 引擎支持行锁,与 Oracle 不同,MySQL 的行锁是通过 索引 加载的,也就是说,行锁是加在索引响应的行上的,要是对应的 SQL 语句没有走索引,则会全表扫描,行锁则无法实现,取而代之的是表锁,此时其它事务无法对当前表进行更新或插入操作。

1、for update (排它锁)

insert、delete、update 在事务中都会默认加上排它锁。

如果在一条 select 语句后加上 for update,则查询到的数据会被加上一条排它锁,其它事务可以读取,但不能进行更新和插入操作。

-- A 用户对 id = 1 的记录进行加锁
select * from user where id = 1 for update;

-- B 用户无法对该记录进行操作
update user set count = 10 where id = 1;

-- A 用户 commit 以后则 B 用户可以对该记录进行操作

2、行锁实现

注意点

  • 行锁必须有 索引 才能实现,否则会自动锁全表,升级为表锁
  • 两个事务不能锁同一个索引
  • insert、delete、update 在事务中都会默认加上排它锁

使用场景

比如客户购物的情形:

A 用户消费,service 层先查询该用户的账户余额,若余额足够,则进行后续的扣款操作;这种情况查询的时候应该对该记录进行加锁。

否则,B 用户在 A 用户 查询后消费前 先一步将 A 用户账号上的钱转走,而此时 A 用户已经进行了用户余额是否足够的判断,则可能会出现余额已经不足但却扣款成功的情况。

为了避免此情况,需要在 A 用户操作该记录的时候进行 for update 加锁。

四、间隙锁

当我们用 范围条件 而不是相等条件检索数据,并请求共享或排他锁时,InnoDB 会给符合条件的已有数据记录的索引项加锁;对于键值在条件范围内并不存在的记录,叫做间隙。

InnoDB 也会对这个”间隙”加锁,这种锁机制就是所谓的间隙锁:

-- 用户 A
update user set count = 8 where id > 2 and id < 6;

-- 用户 B
update user set count = 10 where id = 5;

如果用户 A 在进行了上述操作后,事务还未提交,则 B 无法对 2~6 之间的记录进行更新或插入记录,会阻塞,当 A 将事务提交后,B 的更新操作会执行。

五、建议

  • 尽可能让所有数据检索都通过索引来完成,避免无索引导致行锁升级为表锁
  • 合理设计索引,尽量缩小锁的范围
  • 尽可能减少索引条件,避免间隙锁
  • 尽量控制事务大小,减少锁定资源量和时间长度

Author: NaiveKyo
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint polocy. If reproduced, please indicate source NaiveKyo !
  TOC