• 数据库中锁与事务


    前言

    本文总结一下数据库中基础知识:锁以及事务。

    锁的分类

    对数据库中数据的操作我们可以分为写和读。读时加锁吗?写时加锁吗?这两种类型的加的锁通常被称为共享锁(Shared Lock) 和排他锁(exclusive lock) 也叫读锁(read lock)和写锁(write lock)。

    锁的分类:共享锁和排他锁

    • 读锁是共享的,互不阻塞
    • 写锁是排他的,一个写锁阻塞其他的一个写锁和读锁。防止一个用户写入时,其他用户读取错误数据。

    那加锁时,对于那些范围的数据进行排他处理呢?加锁的范围越小,我们程序的并发性越高。锁也会带来,加锁,检查锁,释放锁的开销。

    锁的范围分类:行锁 表锁。

    • 行锁:对行进行加锁。最大程度的支持并发操作,同时也带来了最大的锁开销。行级锁只在存储引擎层实现。
    • 表锁:对整张表进行加锁,开销最小的策略。

    共享锁和排他锁都是悲观锁。乐观锁一般通过版本号的方式来更新:update user set name = 'xxx' where id = 1 and version = xxx

    事务

    事务指的是一系列的sql语句打包成事务处理,这么一组sql要么全部执行成功,要么全部执行失败。事务的实现与之前所说的共享锁和排他锁有关。

    说一个经典的转账案例:在A的账户余额充足的情况下,A给B转100 元,A的账户减100 B的账户加100 这两个操作要么都成功要么都失败,不能一个成功,一个失败。

    事务的特征 ACID

    • A 原子性(atomicity):事务作为最小的工作单元,事务不可被分割,事务内的语句要全部执行成功要么全部执行失败进行回滚。不能只执行一部分操作。强调事务不可被分割。
    • C 一致性(consistency):数据库总是从一个一致性状态到两一个一致性状态转换。一致性保证如果后半部分的因为崩溃sql没有提交,前半部分也不会被保存到数据库中。强调执行的完整性。
    • I 隔离性(isolation):一个事务所作的修改在最终提交以前,对其他事务是不可见的。隔离性会分隔离级别。
    • D 持久性(durability):提交之后就能永久的保存到数据库中。此时即使数据库崩溃,修改的数据也不会丢失。

    事务隔离性的隔离级别

    隔离级别:

    • 未提交读:READ UNCOMMITED

      • 事务中的修改在没提交时,对其他事务也是可见的。其他事务可以读到未提交的数据称之为脏读(Dirty Read)。
      • 脏读
      • 别人对一个数据写的时候你还能读。写操作时候对数据不锁定。
    • 提交读 READ COMMITED

      • 满足未提交读的缺陷,在事务的修改提交前,对其他事务是不可见的。但是一个事务在未处理完一个事务的过程中,对某个数据的读可能是不同的,因为其他的事务在这个过程中进行了修改和提交。称之为不可重复读。(在事务内读到某个数据可能是不一样的,其他的事务对数据进行了操作)对于写的数据加锁,对读的数据未加锁。
      • 不可重复读
    • 可重复读 REPEATABLE READ

      • 可重复读解决上面两个问题,通过事务内读数据的时候也对数据进行锁定。但是没有解决当某个事务读取某个范围的记录时。另一个事务又在该范围内插入新的数据。这种现象称之为幻读。
      • MYSQL 默认事务隔离级别
    • SERIALIZABLE 可串行化

      • 最高的隔离级别。强制事务的串行执行。可串行化在读每行数据时都进行加锁。读的时候也加锁。
    隔离级别 脏读 不可重复读 幻读 加锁读
    READ UNCOMMITED YES YES YES NO
    READ COMMITED NO YES YES NO
    REPEATABLE READ NO NO YES NO
    SERIALIZABLE NO NO NO YES

    sql 中的锁

    • 无锁
      • select ... from
    • 共享锁
      • select ... lock in share mode
    • 排它锁
      • update
      • delete
      • insert
      • select ... for update

    只有「明确」指定主键,才会执行锁,否则将会执行表锁

    无锁:select * from user wehre id = -1 for update // id 不存在
    
    行锁:select * from user wehre id = 2 for update
    
    表锁:select * from user wehre name = '12' for update // 主键不明确
    

    数据库中的锁和代码锁

    数据库中加锁的应用场景:

    • 粒度小,适合集群

    代码中的应用锁:

    • 粒度大,需要封装,可以用于分布式和集群。

    行锁和表锁的算法实现

    行锁:

    • Record Lock(普通行锁)
      • 对于键值在条件范围内,且存在的记录,使用" Record Lock ",即普通的行锁机制;
    • Gap Lock(间隙锁)
      • 对于键值在条件范围内但并不存在的记录,叫做" 间隙(GAP) ",InnoDB会对这个“间隙”加锁,这种锁机制就是所谓的" Gap Lock "(间隙锁);
    • Next-Key Lock(行 & 间隙)
      • 对于存在与不存在的数据同时加锁,则称为" Next-Key Lock ";
      • Next-Key Lock包含Record Lock和Gap Lock;

    表锁:

    • 意向锁
      • 当一个事务带着表锁去访问一个被加了行锁的资源,那么,此时,这个行锁就会升级为意向锁,将表锁住。
      • 常用的意向锁有:意向共享锁,意向排它锁,共享意向排它锁
    • 自增锁
      • 事务插入自增类型的列时获取自增锁
      • 如果一个事务正在往表中插入自增记录,所有其他事务的插入必须等待

    行锁和表锁是锁粒度的概念,共享锁和排它锁是他们的具体实现。

    References

  • 相关阅读:
    B-tree/B+tree/B*tree
    java拆装箱(转)
    C语言身份证信息查询系统(修改版)
    UC编程:字符读取与行读取
    UC编程:通过fwrite()和write()比较标准库函数和系统调用的速度
    UC编程:输入输出重定向(系统调用)
    UC编程:输入输出重定向(标准IO)
    UC编程:环境变量的查询与修改
    Perl基础(1)chop与chomp的区别
    假期“实习”20天生存实录
  • 原文地址:https://www.cnblogs.com/wei57960/p/12961463.html
Copyright © 2020-2023  润新知