OR博客
MySQL的锁
苗锦洲
创建于:2021-10-03 16:49:20
0
31
230
0
锁是数据库系统区别于文件系统的一个关键特性,用于管理对共享资源的并发访问。

锁是数据库系统区别于文件系统的一个关键特性,用于管理对共享资源的并发访问。

MySQL 的锁

1. 什么是锁

锁是数据库系统区别于文件系统的一个关键特性,用于管理对共享资源的并发访问

分类

  • 粒度

    • 表锁

      • 整个表
    • 行锁

      • 某一行或多行

        • Record Lock:记录锁

          • 一行
        • Gap Lock:间隙锁

          • 左开右闭
          • 查到的:1,4;实际锁住的:2,3,4
        • Next-Key Lock:临键锁

          • 左闭右闭,记录锁 + 间隙锁
          • 查到的:1,4;实际锁住的:1,2,3,4
    • 页级锁

      • 介于行锁和表锁之间
    • ......

  • 不同角度的说法

    • 属性

      • 共享锁
      • 排他锁
    • 状态

      • 意向共享锁
      • 意向排他锁
      • 意向锁:加锁时设置一个标志位,其他事务只需要判断这个标志位,而不需要扫描每个节点判断是否加锁
    • 逻辑层面

      • 乐观锁

        • 版本控制
      • 悲观锁

作用

  • 利用锁解决幻读问题

2. Lock 和 Latch

3. InnoDB 中的锁

3.1 锁的类型

  • 共享锁(S Lock)

    • 允许事务读一行数据
  • 排他锁(X Lock)

    • 允许事务更新或删除一行数据

3.2 一致性非锁定读

  • 读取的记录正在执行 UPDATE 或 DELETE 操作,则会读取事务的快照数据 undo 段

3.3 一致性锁定读

  • SELECT ... FOR UPDATE

    • 事务对读取的记录加 X 锁,其他事务不能再加任何锁
    • RR 隔离级别下,总是读取事务开始时的结果
    • RC 隔离级别下,总是读取最新的快照数据,破坏了隔离性
  • SELECT ... LOCK IN SHARE MODE

    • 事务对读取的记录加 S 锁,其他事务可以加 S 锁,但 X 锁会被阻塞

4. 锁的算法

5. 锁问题

5.0 说明

  • 存在快照读和当前读,快照读是记录版本,不加锁,当前读是最新版本,加锁

    • 快照读

      • 普通 select
    • 当前读

      • select ... lock in share mode; (共享锁)
      • select ... for update;
      • insert;
      • update;
      • delete;

5.1 赃读

  • 数据可能同时存在内存和磁盘中,看读取的是哪一份
  • 一个事务内读取到了另一个事务未提交的修改,破坏了隔离性

5.2 不可重复读

  • 一个事务内读取到了另一个事务提交的数据,导致先后两次读取结果不一样,破坏了一致性

  • 5.3 丢失更新(第二类)

    • 事务 1 和事务 2 先后修改同一条数据,分别先后提交,先提交的会被后提交的覆盖掉

    • 数据库理论上不会出现这种问题,因为事务会等待前面的事务提交完成,更多的是逻辑上的丢失更新

    • 自己就遇到过类似的问题:邮件有一个是否读取字段,用户 1 给用户 2 发送了一个信函,用户 1 对邮件进行修改并更新数据库,但是用户 2 并没有获取数据库最新的版本,本地还是更新前的,此时读取邮件,调用更新接口,将是否读取字段设置为 true,同时信函内容又变回修改前的了

    • 可以加悲观锁或乐观锁

      • 悲观锁:select ... for update
      • 乐观锁:由程序自己实现,判断版本号,比如更新时间,更新前先查询,拿到旧的更新时间,更新的时候再查询,更新时间一样的话就更新,否则通知用户刷新数据。

幻读

  • 没有锁间隙
  • 破坏了一致性

第一类丢失更新,(不会发生)

  • 两个事务修改同一条记录,某一事务完成,另一个事务回滚,造成第一个事务更新丢失
  • 现代的关系型数据库已经不会发生,排他锁

6. 阻塞

7. 死锁

7.1 什么是死锁

  • 并行下,多个事务因竞争资源而产生的相互等待现象,若没有外力干涉将无法继续执行

  • 解决方法

    • 超时回滚

    • 等待图

      • 记录事务等待链表和锁链表

        • 事务等待链表

          • t1
            t2
            t3
            t4
        • 锁的信息列表

          • row1
            t2:x
            t1:s
          • row2
            t1:s
            t4:s
            t2:x
            t3:x
        • 调用关系

          • t1->row1.t1:s
            t2->row2:t2:x
            t3->row2:t3:x
        • 等待图

          • t1<--->t2 ↖ ^ × | ↙ | t4<--- t3
            • 存在回路 t1,t2
            • 回滚 undo 量最小的事务
      • 判断是否存在回路

      • 深度优先

7.2 死锁概率很小

7.3 死锁示例

  • AB-BA 死锁
  • MySQL 自动为外键添加索引,删除索引会报错,need a foreign key constraint

8. 锁升级

降低锁的粒度

  • 一个表的 1000 行的行锁升级为页锁,页锁升级为表锁
  • 目的:如果将锁看为一种稀有资源,降低锁的粒度是为了保护系统资源,一定程度上提高效率
  • InnoDB 不存在锁升级问题,因为不是根据每条记录产生行锁的,而是根据事务访问的每页对锁进行管理,采用的是位图方式。不管锁住的是一页中的一条记录还是多条记录,开销通常都是一致的。
评论
楼主暂时不想被别人评论哦~