Mysql - 锁常见问题

通过一些问题来讨论 Mysql 中的锁

  • mysql 有哪些锁,介绍一下?
  • mysql是怎么实现乐观锁和悲观锁的?
  • 哪些情况下会使用乐观锁,哪些情况使用悲观锁,可以举一些 sql 例子吗?
  • 间隙锁的原理?什么时候会加间隙锁?

1. Mysql 有哪些锁,介绍一下?

按照锁的粒度,可以分为全局锁,表级锁和行锁

全局锁会使整个数据库处于只读状态,在做全库逻辑备份时经常用到;表级锁在操作数据数会锁定整张表或表结构,具体可以分为表锁,MDL 锁和意向锁;行锁可以做到只锁定需要操作的记录行或间隙,具体可以分为记录锁,间隙锁和 临建锁,也就是记录锁加间隙锁的组合,它锁的是一个范围

2. Mysql 是怎么实现乐观锁和悲观锁的?

Mysql 中通常使用版本号或者时间戳来实现乐观锁。以版本号为例,可以在数据表中添加一个版本号字段,然后当某个事务中需要更新一条记录时,直接用 where 语句对版本号进行等值判断,如果与给定的版本号相同则更新,不相同则整个事务回滚或重试。用时间戳实现也是类似的方式。整个过程不需要锁整个表,只需简单判断版本号即可

3. 哪些情况下会使用乐观锁,哪些情况使用悲观锁,可以举一些 sql 例子吗?

乐观锁适用于读多写少,也就是并发冲突很少的场景,这样可以省去锁的开销,提高系统的吞吐量。 悲观锁适用于读少写多的场景,这样可以避免在并发冲突多的情况下,使用乐观锁带来的不断重试问题。这用悲观锁为每个事务上锁就比较合适

举例来说,可以考虑这样的场景:有用户 A 和用户 B,同时去买一个同一件商品,但商品的数量只有一个

乐观锁的实现是在商品表中添加一个 version 字段,然后直接指定版本号更新。假设 Mysql 为 RR(可重复读)隔离级别,都要买 id = 1的商品,version 初始为 0

用户 A 对应 sql 为

begin;
select num, version from t_goods where id = 1;  # T1 时刻先查出商品数量和对应的版本号
update t_goods set num = num - 1, version = version + 1 where id = 2 and version = 0;  # T3 时刻更新商品数量时带上版本号,并使其自增
commit;

用户 B 对应的 sql 为

begin;
select num, version from t_goods where id = 1;  # T2 时刻先查出商品数量和对应的版本号
update t_goods set num = num - 1 where id = 2 and version = 0;  # T4 时刻更新商品数量时带上版本号,并使其自增
commit;

虽然用户 A 和用户 B 在 T1,T2时刻都看到还有一件商品,但最终只有用户 A 在 T3 时刻能够获得最后一件商品,此时 version 自赠为 1,那么用户 B 在 T4 时刻将无法看到这条记录,也就无法购买成功

悲观锁的实现直接在事务开始时给所有事务上锁,直到事务结束时才释放锁

用户 A 和 用户 B 的 sql 都是一样的

begin;
select num from t_goods where id = 1 for update;  # 先使用 for update 将商品数据加上 next-key lock
update t_goods set num = num - 1 where id = 1;  # 然后再更新
commit;

由于用户 A 在事务开始时就对数据加了 临键锁,用户 B 只能等到用户 A 在事务结束释放锁时再执行,最终还是只有用户 A 能购买

4. 间隙锁的原理?什么时候会加间隙锁?

对于行锁来说,数据行是可以加上锁的实体,但其实数据行之间的间隙也是可以加上锁的实体,对于的锁就是间隙锁。间隙锁锁的是数据库中两个值的间隙,目的是防止往中国间隙中插入一条新纪录,以此来防止幻读

间隙锁通常出现在 InnoDB 引擎的 RR(可重复读)隔离级别下,并且行锁加间隙锁,也就是临建锁是加锁的基本单位。但在某些情况下,临建锁也会退化成行锁或间隙锁


诚恳欢迎大家提出意见Orz

......(待续未完

你可能感兴趣的:(Mysql,mysql,数据库,面试)