数据库常用锁

数据库常用锁

一、锁概述

锁是计算机中一种用于协调多个进程或线程并发访问共享资源的机制。在数据库系统中,除传统的计算资源(CPU、RAM、I/O)以外,数据也是一种供许多用户共享的重要资源,如何保证数据的并发访问一致性和有效性是数据库系统必须解决的核心问题。而数据库锁就是为了解决这一问题而设计的,在MySQL数据库里就提供了多种类型的锁,通过合理使用这些数据库锁,可以避免数据竞争和并发访问引起的问题,确保数据库操作的正确性和可靠性。

二、按加锁范围分类

MySQL锁按加锁范围分类有全局锁、表锁和行锁。

  1. 全局锁

    • 定义

      针对整个数据库的锁。粒度最大的锁。

    • 加锁

      flush tables with read lock;
      
    • 释放锁

      unlock tables;
      
    • 使用场景

      数据库备份和恢复操作。当数据库进行备份或者恢复时,需要保证数据库不会被其他事务修改,否则可能会导致备份数据不一致或者恢复数据丢失。此时可以使用全局写锁来阻止其他事务对数据库进行写操作,以确保备份或恢复操作的正确性。但需要注意的是,全局锁会对数据库的正常操作产生严重影响,因此在使用全局锁时需要谨慎操作。

  2. 表锁

    • 定义

      针对整张表的锁。包含读锁和写锁。

    • 加锁

      # 加写锁
      lock tables tablename write;
      
      # 加读锁
      lock tables tablename read;
      
    • 释放锁

      unlock tables;
      
    • 读锁解释

      代表当前表为只读状态,是一种共享锁,读锁除了会限制其他线程的操作外,也会限制加锁线程的行为,限制如下

      • 加锁线程只能对当前表进行读操作,不能对当前表进行更新操作,不能对其它表进行所有操作
      • 其它线程只能对当前表进行读操作,不能对当前表进行更新操作,可以对其它表进行所有操作
    • 写锁解释

      是一种独占锁,写锁除了会限制其他线程的操作外,也会限制加锁线程的行为,限制如下

      • 加锁线程对当前表能进行所有操作,不能对其它表进行任何操作
      • 其它线程不能对当前表进行任何操作,可以对其它表进行任何操作
  3. 行锁

    • 定义

      针对一张表中行记录的锁。MySQL的行锁是在引擎层实现的,并不是所有的引擎都支持行锁,比如,InnoDB引擎支持行锁而 MyISAM引擎不支持。InnoDB 引擎的行锁主要有三类:Record Lock记录锁、Gap Lock间隙锁和Next-key Lock临键锁。

    • Record Lock记录锁

      • 定义

        是针对索引记录的锁,锁定的总是索引记录。

      • 加锁

        select id from user where id = 1 for update;
        

        for update 就是显式在索引id上加行锁(排他锁),防止其它任何事务修改或删除id=1的行,但是对user表的其他操作还是可以正常执行。

      • 释放锁

        commit;
        
        rollback;
        

        事务提交或回滚时锁被释放

    • Gap Lock间隙锁

      • 定义

        锁住两个索引记录之间的间隙上,由InnoDB隐式添加。

      • 加锁

        select id from user where age = 21 for share;
        

        会在匹配的行之间形成一个“间隙”,阻止其他事务在这个间隙中插入新的行。

      • 释放锁

        commit/rollback;
        

        事务提交或回滚时锁被释放

    • Next-key Lock临键锁

      • 定义

        是Record Lock + Gap Lock的组合,用来锁定一个范围,并且锁定记录本身锁,它是一种左开右闭的范围,可以用符号表示为:(a,b]。

      • 使用

        InnoDB的默认事务隔离级别是可重复读,在这种级别下,如果使用select … in share mode或者select … for update语句,那么InnoDB会使用临键锁,可以避免幻读。

三、按使用方式分类

MySQL锁按使用方式分类有共享锁和排他锁。

  1. 共享锁

    • 定义

      共享锁,Share lock,也叫读锁。它是指当对象被锁定时,允许其它事务读取该对象,也允许其它事务从该对象上再次获取共享锁,但不能对该对象进行写入。

    • 加锁

      # 方式1
      select ... lock in share mode;
      # 方式2
      select ... for share;
      
  2. 排他锁

    • 定义

      排它锁,Exclusive Lock,也叫写锁或者独占锁。它是指当对象被锁定时,既不允许其它事务读取该对象,也不允许其它事务对该对象进行写入。

    • 加锁

      select ... for update;
      

四、按思想层面分

  1. 乐观锁

    乐观锁假设数据在一般情况下不会发生冲突,因此在读取数据时不会立即加锁,而是在更新数据时才会检查是否有其他事务对数据进行了修改。乐观锁通常通过版本号或时间戳等机制来实现,当更新数据时,会检查数据的版本号或时间戳是否与当前一致,如果一致则更新成功,否则认为数据已经被其他事务修改,需要进行相应的处理。乐观锁适合处理读操作频繁、写操作较少的场景。

  2. 悲观锁

    悲观锁则是假设数据在一般情况下会发生冲突,因此在读取数据时会立即加锁,阻止其他事务对数据进行修改。悲观锁通常通过数据库的锁机制来实现,例如表级锁或行级锁。悲观锁适合处理写操作频繁、并发访问量大的场景。

在MySQL中,无论是乐观锁还是悲观锁,都是利用MySQL提供的锁机制来实现的。

五、死锁

在 MySQL 中,死锁是指两个或多个事务相互等待对方持有的锁,从而导致所有事务都无法继续执行的情况。MySQL 中的死锁通常发生在多个事务同时并发访问数据库时,每个事务都持有一些资源并请求其他事务持有的资源,导致循环等待。

MySQL 会自动检测死锁,并在检测到死锁时进行处理。通常情况下,MySQL 会选择将其中一个事务回滚,释放资源,从而解除死锁。被回滚的事务会收到一个死锁错误,开发人员可以通过捕获该错误并重新执行事务来解决死锁。

为了尽可能减少死锁的发生,可以采取一些措施,例如:

  1. 尽量减少事务持有锁的时间,避免长时间持有锁;
  2. 尽量在事务中按固定的顺序获取锁,避免不同事务以不同的顺序获取锁;
  3. 使用合适的事务隔离级别,例如 READ COMMITTED 或者 SERIALIZABLE,以减少死锁的可能性。

另外,可以通过监控和分析数据库中死锁的发生情况,来找出可能导致死锁的原因,并进行相应的优化。

你可能感兴趣的:(数据库)