数据库中锁与事务

前言

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

锁的分类

对数据库中数据的操作我们可以分为写和读。读时加锁吗?写时加锁吗?这两种类型的加的锁通常被称为共享锁(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

原文地址:https://www.cnblogs.com/wei57960/p/12961463.html