层次锁(转)

层次锁,更多地是在数据库设计中被提到,但也有少数分布式锁系统也实现这个概念。下面内容主要还是从数据库设计的角度来理解层次锁的概念。

1. 事务锁

粗略说来,当执行SQL语句时数据库都会开启事务,在SQL执行完毕commit时,会把所有受影响数据写到磁盘文件并结束事务。在事务执行期间,为了保证事务的ACID,SQL所影响到的表、行等数据都会被不同程度的进行锁定,我们笼统称为“事务锁”。从使用者的角度来看,一般数据库都提供了表锁和行锁,其中行锁要比表锁粒度要细很多,被认为是支持高并发事务的关键。

但仅靠表锁和行锁是不够的,比如一个事务进行了行锁定,而另一个事务需要表锁定,那如何判断表锁定是否可以成功?同样,一个事务锁定了某些行,而另一事务需要读取这些行,是否允许读取?当考虑到不同的数据库操作和隔离级别时,锁的实现会变得非常复杂。

2. 锁的类型

有一个关键问题是,数据库锁用来锁什么,从数据库实现上来说是锁数据库内部的数据库结构,因为各数据库实现不一,因此锁的内容也就有所区别。对Oracle来说存在表、行、回滚段、Redolog等结构;SQLServer存在表、页、行等结构,这些结构在逻辑上存在上下级关系,比如SQLServer中,每个表的数据有多个页构成,而页中有包含了多行的内容,而一般认为行没有下级数据结构,为事务操作的最小单位。既可以对表,也可以对页、行对象加锁,抽象一点,称这些能加锁的对象为Node。

数据库中存在下面类型的锁:

  • 共享锁(S:Share)
    共享锁也即读锁,多个事务可以同时读某个Node上的数据,但会阻塞写。其目的是提高数据读取的并发性,如果加锁的Node为行,则得到熟知的行锁。但共享锁可以加在任意的Node结构上,并不仅限于行。
    好像每次执行SELECT语句都对行加共享锁,理解起来非常容易。其实考虑到并发原因,很多数据库并没有读取时便对数据行加共享锁,像Oralce、Mysql都使用了回滚段的设计方式,从而保证读取数据是“无锁”的;但对数据行的索引可能加了共享锁。
  • 排他锁(X:Exclusive)
    排他锁即写锁,某个时刻仅有一个事务能拥有某个Node的排他锁,其目的是保证更新的原子性,即实现ACID中的A。与共享锁一样,排他锁也可加在任何一个Node上,如果加在表上则为锁定整张表,加在行上则锁定该行。
    排他锁的场景就比较多了,SELECT * FROM T FOR UPDATE便会触发行锁;而LOCK TABLE T则会触发表锁
  • 更新锁(U:Update)
    更新锁在SQLServer的文档中提到的比较多,根据微软介绍,更新锁是为了解决一种常见的死锁场景:
    1.事务A获取行的共享锁
    2.事务B获取行的共享锁
    3.事务A转换共享锁为排他锁(因事务B持有共享锁,故等待)
    4.事务B也期望转为共享锁为排他锁,等A。导致死锁
    为解决该问题SQLServer引入更新锁,同一时刻只有一个事务可以持有更新锁,如果该事务随后修改了数据则更新锁转换为排他锁;否则转换为共享锁。即共享锁是先“持有”后转换。
    一般我们执行SELECT * FROM T FOR UPDATE时便会触发更新锁。
  • 意向锁(Intent Lock):

上面三种锁是描述了某个Node被修改时的锁类型,但因为Node存在上下级关系,但Node被加锁时,我们需要评估上级Node的加锁情况,比如当给行加各种锁的时候,表的加锁情况是神马?

之所以这样做的原因是因为存在一个需求:

  1. 事务A给表中的某些行加了共享、排他锁,而事务B希望锁住整张表(比如用户显式调用LOCK TABLE T)
  2. 事务B能否给整张表加排他锁需要检查表各行的加锁情况,若某些行已经加了排他锁,则不能给整张表加排他锁。就是表锁依赖行锁。

若逐行去检查,在数据量非常大的时候性能会有巨大的损伤,因此引入一种能描述表被加锁情况的锁类型---意向锁。当程序对Node进行加锁操作时,需首先声明上级Node的锁类型。比如,对行加排他锁之前需对表加意向排他锁。

这样事务A在更新数据之前会对表加意向排他锁,而事务B在加排他锁之前发现A加的意向排他锁,所以必须等待A完成,从而避免了逐行检查的问题。

从下层节点来说,意向锁是对所有下级Node加锁情况的整体说明;但从上层Node来说,意向锁描述的是其下层Node正在被加某种锁(比如排他锁),或说下层Node有加某种锁的意向,这样正是“意向"一词的由来。

意向锁与共享锁、排他锁、共享锁不同的时,只能加在存在下级Node的Node对象上,比如可以加在表、页上,但不能加在行上,因为行是事务加锁的基本元,不存在有”意向“的下级Node。

意向锁具体有分:意向共享锁(Intent Share)、意向排他锁(Intent Exclusive)、共享意向排他锁(Share Intent Exclusive),下面逐一说明

  • 意向共享锁(IS:Intent Share)
    表明事务正在读该Node的下层数据(非全部),并且下层Node被加了共享锁
    比如若执行SELECT语句,在SQLServer中,表、页、索引都会被加意向共享锁
  • 意向排他锁(IX:Intent Exclusive)
    表明事务正在修改该Node的下层数据(非全部),并且下层Node被加排他锁
    在执行UPDATE、SELECT FRO UPDATE、DELETE、INSERT语句是便是这种情况
  • 共享意向排他锁(SIX:Share Intent Exclusive)
    表明事务正在读取该Node的全部下层数据,并且下层Node被加排他锁(非全部)。比如事务对表加了SIX,对页加了IX,对行加了X,每个Node在同一时刻仅能有一个SIE。
  • 通过显式调用LOCK TABLE + SRX可获得该效果

3. 相容矩阵

因为对同一Node可能存在多个事务期望对该Node加不同的锁,这就需要列出锁直接的相容性,只有相容的锁才允许被同时加在同一Node上,否则后面事务就需要等待。

下表直接引用自微软网站:http://msdn.microsoft.com/en-us/library/aa213041(v=sql.80).aspx

  Existing granted mode
Requested mode IS S U IX SIX X
Intent shared (IS) Yes Yes Yes Yes Yes No
Shared (S) Yes Yes Yes No No No
Update (U) Yes Yes No No No No
Intent exclusive (IX) Yes No No Yes No No
Shared with intent exclusive (SIX) Yes No No No No No
Exclusive (X) No No No No No No

4.层次锁、区间锁、锁膨胀

因为意向锁的引入,导致锁之间也存在上下级关系:比如表的锁为SIX、页的锁为:IX、行的锁为:X,这样对某个Node及下级Node来说,存在着层次锁的概念,也即多粒度锁。

各事务可以使用不同粒度的锁与Node及其下级Node进行交互,从而提高事务的并发性。

还有一类锁,在SQLServer中称为“区间锁”(Key-Range),在Mysql中称为”间隙锁“(Gap Lock),主要应对下面场景:

假如事务执行:SELECT * FROM T WHERE ID>10 AND ID<20,在READ_REPEATABLE的隔离级别下,无论事务执行都少次都希望能返回相同的结果,也即不能在10<ID<20之间更新任何数据,这就需要锁住(10,20)这个区间,这便是区间锁的由来。

与上面列出的锁类型不同的是,这类锁物理上并不存在,只是有前面几种锁类型组合出的逻辑锁。

在Oracle、SQLServer中,当行锁多到一定的程度后,为了降低锁带来的开销,数据库引擎会把行锁升级为表锁(或页锁),但在Mysql中不存在这个问题,因为Mysql说他们的行锁算法极度节省空间。

5. 总结

本文主要罗列了一些数据库锁的概念,这些概念中有些并不通用,不是在所有数据库中都存在,主要目的是为了因此层次锁的概念,层次锁的实现在各数据库中也差异很大。

层次锁的优点是可以通过多粒度锁的方式锁定对象,从而为高并发带来极佳的性能。

转自:http://blog.csdn.net/chen77716/article/details/7214132

【参考资料】

时间: 2024-10-12 05:45:53

层次锁(转)的相关文章

【转】apache开源项目的介绍

Jakarta项目是ASF(The Apache Software Foundation)的一部分.ASF是一个非赢利组织,她鼓励基于开放的软件许可下进行合作.注重实效的开发,并提供各个领域的高质量软件,她涉及到Http服务器,编译工具,类库,开发架构,服务器端Java技术,J2EE容器,数据库工具,日志工具,XML解析等等诸多领域.ASF提供的java项目有一部分在Jakarta中,还有一些成为独立的诸如Tomcat的项目,Jakarta项目则提供了多种多样开源的java解决通用方案. 先介绍

团队-团队编程项目爬取豆瓣电影top250-代码设计规范

一.程序风格: 1.严格采用阶梯层次祖师程序代码 各层次锁紧的分割采用VC的缺省风格 , 即没层次锁紧为4格.括号位于下一行.要求相匹配的大括号在同一列 ,对继行则要求在缩进4格. 2.对变量的定义.尽量位于函数的开始位置. 二.命名规则: 1.变量名的命名规则 1).变量命名规则要求用"匈牙利法则".即抬头字母用变量的类型,其余部分用变量的英文意思或者其英文意思的缩进,尽量避免用中文的拼音,要求单词的第一个字母应大写 对非通用的变量,在定义时加入注释说明,比那辆定义尽量可能放在函数的

团队编程项目作业2-爬虫豆瓣top250项目代码设计规范

来自队长http://www.cnblogs.com/z-xx/p/7592127.html 一.程序风格: 1.严格采用阶梯层次祖师程序代码 各层次锁紧的分割采用VC的缺省风格 , 即没层次锁紧为4格.括号位于下一行.要求相匹配的大括号在同一列 ,对继行则要求在缩进4格. 2.对变量的定义.尽量位于函数的开始位置. 二.命名规则: 1.变量名的命名规则 1).变量命名规则要求用"匈牙利法则".即抬头字母用变量的类型,其余部分用变量的英文意思或者其英文意思的缩进,尽量避免用中文的拼音,

C++并发编程 02 数据共享

在<C++并发编程实战>这本书中第3章主要将的是多线程之间的数据共享同步问题.在多线程之间需要进行数据同步的主要是条件竞争. 1  std::lock_guard<std::mutex> #include <list> #include <mutex> #include <algorithm> std::list<int> some_list; std::mutex some_mutex; void add_to_list(int n

分布式任务&amp;分布式锁(li)

目前系统中存在批量审批.批量授权等各个操作,批量操作中可能因为处理机器.线程不同,造成刷新缓存丢失授权等信息,如批量审批同一用户权限多个权限申请后,流程平台并发的发送多个http请求到acl不同服务器,a机器处理了授权a,b机器同时处理了授权b,然后刷新用户缓存.因为在事务里彼此看不见对方提交的数据,刷新时又完全从db中读取要刷新的数据,就造成了互相丢失对方的数据.因此,需要一个分布式锁工具,来协调各个机器.线上的工作同步问题. 分布式锁千万不能用ReentrantLock,因为它的lock和u

Linux内核RCU(Read Copy Update)锁简析-前传

如果你用Linux perf tool的top命令做热点纠察时,你会发现,前10名嫌疑犯里面肯定有好几个都是锁!在进行并行多处理时,不可避免地会遇到锁的问题,这是不可避免的,因为这一直以来也许是保护共享数据的唯一方式,被保护的区域就是临界区.而我们知道,锁的开销是巨大的,因为它不可避免地要么等待,要么让别人等待,然而这并不是开销的本质,开销的本质在于很多锁都采用了"原子操作"这么一个技术,如此一个原子操作会对总线或者cache一致性造成很大的影响,比如要对一个变量进行原子加1,不要认为

SqlServer锁机制与实践

在如今这个云计算,大数据,移动互联网大行其道的时代,各种NoSQL数据库MongoDb.redis.HBase等使用的越来越广泛,大有替代关系型数据库的趋势.但是关系型数据库真的已经落伍了吗?答案是否定的.非关系型数据库不支持ACID属性,不支持事务,无法适应复杂查询的缺点.关系型数据库凭借其强一致性的特点,注定了在类似银行转账,订单支付等场景中,还是唯一的选择.众所周知,SQLSERVER通过锁来提供ACID属性,处理并发访问,本文尝试通过对锁机制的一些学习,让我们明白数据库查询超时,死锁等问

专访李智慧:架构是最高层次的规划和难以改变的决定

http://www.csdn.net/article/2015-09-21/2825759 李智慧(微博.LinkedIn),目前任职宅米网CTO,曾供职阿里巴巴与英特尔亚太研发中心,从事大型网站.分布式系统.大数据平台方面的研发工作.著有<大型网站技术架构 核心原理与案例分析>一书. CSDN:你是如何走上技术这条路的?谈谈毕业这些年来在工作中的收获和体验. 李智慧:我大学学工业自动化,在校的时候一个偶然的原因在图书馆看到一本C语言编程的书,从此迷上编程,但是毕业后从事的却是仪表工程师的工

sql 中各种锁随记

一. 为什么要引入锁  多个用户同时对数据库的并发操作时会带来以下数据不一致的问题:  丢失更新  A,B两个用户读同一数据并进行修改,其中一个用户的修改结果破坏了另一个修改的结果,比如订票系统  脏读  A用户修改了数据,随后B用户又读出该数据,但A用户因为某些原因取消了对数据的修改,数据恢复原值,此时B得到的数据就与数据库内的数据产生了不一致  不可重复读  A用户读取数据,随后B用户读出该数据并修改,此时A用户再读取数据时发现前后两次的值不一致  并发控制的主要方法是封锁,锁就是在一段时间