提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
锁的分类
从数据库系统的角度,锁模式可分为以下6 种类型:
共享锁(S):又叫 他读锁。可以并发读取数据,但不能修改数据。也就是说当数据资源上存在共享锁时,所有的事务都不能对该数据进行修改,直到数据读取完成,共享锁释放。
排它锁(X):又叫 独占锁、写锁。对数据资源进行增删改操作时,不允许其它事务操作这块资源,直到排它锁被释放,从而防止同时对同一资源进行多重操作。
更新锁(U):防止出现 死锁 的锁模式,两个事务对一个数据资源进行先读取再修改的情况下,使用共享锁和排它锁有时会出现死锁现象,而使用更新锁就可以避免死锁的出现。
资源的更新锁一次只能分配给一个事务,如果需要对资源进行修改,更新锁会变成排它锁,否则变为共享锁。
意向锁:表示 SQL Server 需要在 层次结构中的某些底层资源上 获取共享锁或排它锁。例如,放置在 表级 的 共享意向锁 表示事务打算在表中的页或行上放置共享锁。在表级设置意向锁可防止另一个事务随后在包含那一页的表上获取排它锁。
意向锁可以提高性能,因为 SQL Server 仅在 表级 检查意向锁来确定事务是否可以安全地获取该表上的锁,而无须检查表中的每行或每页上的锁以确定事务是否可以锁定整个表。
意向锁包括意向共享 (IS)、意向排它 (IX) 以及与意向排它共享 (SIX)。
架构锁:在执行 依赖于表架构的操作 时使用。架构锁的类型为:架构修改 (Sch-M) 和架构稳定性 (Sch-S),执行表的数据定义语言 (DDL)操作(例如添加列或除去表)时使用架构修改锁,当编译查询时,使用架构稳定性锁。
大容量更新锁(BU):向表中大容量复制数据并指定了 TABLOCK 提示时使用。 大容量更新锁允许进程将数据并发地大容量复制到同一表,同时防止其它不进行大容量复制数据的进程访问该表。
事务隔离级别与锁的关系
在 读取未提交 隔离级别下,读取数据不需要加 共享锁,这样就不会跟被修改的数据上的 排他锁 冲突;
在 读取已提交 隔离级别下,读操作需要加 共享锁,但是在语句执行完以后释放共享锁;
在 可重复读 隔离级别下,读操作需要加 共享锁,但是在事务提交之前并不释放共享锁,也就是必须等待事务执行完毕以后才释放共享锁;
可串行化 是限制性最强的隔离级别,因为该级别 锁定整个范围的键,并一直持有锁,直到事务完成。
读取未提交
最低的隔离级别,一个事务可以读到另一个事务未提交的结果,所有的并发事务问题都会发生。
读取已提交
只有在事务提交后,其更新结果才会被其他事务看见,可以解决 脏读问题,但是不可重复读或幻读仍有可能发生。Oracle 默认采用的是该隔离级别。
可重复读
在一个事务中,对于同一份数据的读取结果总是相同的,无论是否有其他事务对这份数据进行操作,以及这个事务是否提交,除非数据是被本身事务自己所修改。可以解决 脏读、不可重复读。MySQL 默认采用可重复读隔离级别。
可串行化
事务 串行化执行,隔离级别最高,完全服从 ACID,牺牲了系统的并发性,也就是说,所有事务依次逐个执行,所以可以解决并发事务的所有问题。
什么是死锁?如何解决死锁?
死锁是指两个或多个事务在同一资源上相互占用,并请求锁定对方的资源,从而导致恶性循环的现象。
常见的解决死锁的方法
如果不同程序并发存取多个表,尽量约定 以相同的顺序访问表,可以大大降低死锁机会;
在同一个事务中,尽可能做到 一次锁定所需要的所有资源,减少死锁产生概率;
对于非常容易产生死锁的业务部分,可以尝试使用 升级锁定颗粒度,通过 表级锁 定来减少死锁产生的概率。
死锁的必要条件 * 4:互斥、不可抢占、等待时不释放已有资源、等待关系成环
什么是乐观锁和悲观锁?如何实现?
DBMS 中的 并发控制 的任务是确保在 多个事务同时存取数据库中同一数据 时不破坏事务的隔离性和统一性以及数据库的统一性。乐观并发控制(乐观锁)和悲观并发控制(悲观锁)是并发控制主要采用的技术手段。
悲观锁:假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作。在查询完数据的时候就把事务锁起来,直到提交事务。这对于长事务来讲,可能会严重影响系统的并发处理能力。实现方式:使用数据库中的锁机制。
乐观锁:假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性。乐观锁适用于 读多写少 的应用场景,这样可以提高吞吐量。实现方式:一般会使用版本号机制或 CAS 算法实现。
乐观锁虽然“乐观”,但也有他的保护措施,判断是否有其他线程对数据进行修改。
两种判断方式:
- 版本号,数据每次更新,版本号都会更新,记下初始时的版本号,要修改前查看版本号,跟之前的不一样就说明有其他线程修改了该数据,本线程不再更新,更新失败
- CAS算法,思路是用三个变量:需要读写的内存位置V、进行比较的预期原值A、拟写入的新值B,
如果位置V的值仍为预期的A时说明没有其他线程改动它,可以修改为新值B,否则不做修改