锁类型选择的确是sybase数据库设计的一个需要重要考虑的问题!也是sybase开发和管理人员比较头疼的问题,为此,来点关于lock的介绍性资料!
所支持的加锁机制
全页加锁
全页加锁既是一个新术语,它又是由ASE(Adaptive Server Enterprise)在过去所支持的一种加锁类型。这种类型有下列特性:
对所有可被访问的页面在页面级加锁
当各种类型的页面以任何方式发生改变时,对这些排它性的页面进行加锁;而且这种加锁机制一直保持到该事务终止;
当下一个所需的页面已经成功地获得,对那些已经释放的的当前访问页进行共享页面加锁(如果采用了第三层ANSI隔离,则把这种加锁机制保持到该事务终止为止)
采用页级时间印记(timestamp)以确定是否发生改变,详细信息记录在事务日志中,以便在系统恢复时以向前或向后方式使用。
这种加锁方式常常提供性能最高的解决方案,特别是当应用设计时已经考虑了这些特性时更是如此。但是,有一些应用系统,当发生某些活动时,这种对整个页面进行加锁的方式就可能会对系统性能产生有重大意义的影响。对于那些面对诸如文件系统或其它已经支持更细小尺度加锁机制的数据库厂家产品的一般环境而设计的应用系统而言,这种情况尤其如此。
此外,还存在一系列问题,它们要围绕着更加困难的条件进行工作。它们通常要采用更加具有Sybase特性的解决方案。对于商用的应用软件制造厂商而言,对他们是一个挑战,因为这将要求他们必须跨越他们所支持的数据库平台,去完成维护其原代码的工作,而这个工作有相当工作量。在这个领域的基本问题如下:
对已经按照升序值创建的非群聚性索引的最末端叶型页面存在着争议
对非群聚性索引的表进行插入和查询时可能发生死锁;
在按照群聚性的索引值进行更新和对非群聚性索引的表进行查询访问之间可能发生死锁;
在没有作索引的表的最后一行可能发生冲突(尽管对最后的特定地址可以使用分区) ;
有可能使行数很少的表之间发生潜在的冲突(尽管对特定的地址可以使用填充因子[ fillfactors]和每页最大行数[ max_rows_per_page]这两个参数)
对每个页面两边进行加锁的需要常常被分割开来;
如果一个表特别小,以致在一个单一页面中进行驻留,那么对单一行的访问实际上将破坏对整个表的加锁机制。
仅对数据加锁
仅对数据加锁机制试图去解决本文前一节所关注的主要问题(其他的议题将在其它功能领域中加以解决)。这种加锁方式支持两类不同的工作方式: 数据行加锁和数据页加锁。在这两种情况中,对于它们所支持的加锁方式,都与以前的加锁机制有所不同。仅对数据加锁具有下列特性:
在索引页面中不会破坏事务加锁。相反,而是采用了一种称之为锁存的机制。锁存是一种类似于旋转锁(spinlocks)的同步方法,它们与事务无关并且只保留很短的周期(一般而言,当一个任务在数据库中物理上改变一小片数据时,这个周期相当于在共享存贮区中在一个2K的页面改变某些字节数据的时间)一旦完成之后,这个任务将直接打开这个锁存。当这种情况还可能临时同其它组块时 ,因为这种锁存不能对服务器任务进行有上下文的切换,也不能涉及死锁,并且只能保持主要的一小段时间,所以它们不能产生有显著意义的争用。
采用一个RID对单一行进行数据行加锁(行标识[RID----Row ID]是逻辑页号与所在页面上该行号的组合);
支持固定的行标识 RIDs, 它可以是向前的,允许不进行其RID的改变,就完成数据行的移动。当一行变大超过了它的可用空间时,采用上述结果对非群聚索引不需要进行任何改变。
不需要进行任何争用就可以在表的尾部进行插入操作,这一功能已经增加进来。.
支持采用范围加锁、下一个关键字加锁和无限大加锁等方式对逻辑范围值进行加锁
支持由最顶层操作所导致的页面分割。这些情况直接加以提交,"系统"事务可以导致在更短一点的时间周期里保持分裂的页面处于锁定状态。
为了支持这些变化,需要对采用的存贮表结构进行一系列改善。这些改进的主要效果如下:
群聚索引现在被存贮为象许多人所熟悉的IBM DB2产品所采用的“放置索引”("placement indexes.”)方式。这种结构类似于非群聚性的索引,需要类似的空间总量。这种修正的结构导致了在数据初始存贮时可以按照顺序跨数据页进行存储,但是当发生插入时,它们就要尽可能紧密存放以便在正确的逻辑页面中不存在页面分割。此外,在数据页中的数据顺序在新行增加时是不进行维护整理的。这种索引的应用使每个群聚化的索引周游增加了一次I/O操作。
行位移表已经增加到索引页和数据页中。这种增加和新的行索引行存贮格式具有使每个索引页面所存贮的索引条目个数减少的潜在能力。
固定行标识(RIDS)。当一行移动时,对于分配新行位置的向前地址被放在用于驻留该行的位置上。当这种移动需要改变非群聚性索引时,对该行的访问需要增加一次I/O操作以得到‘向前’的位置。
一般而言,索引将更小和更短,这是因为如下原因:
从每个叶级页面中采用双重键限制机制来限制双重键(Duplicate key)例如,如果值“绿”("GREEN”)在下列行标识(RIDs)值等于123-1,234-2,和345-3的行中, 就分别存贮值“绿”("GREEN”),123-1,234-2,345-3,而不是存贮值“绿”("GREEN,”)三次。在每个索引页中每个值只存贮一次。
在非群聚性索引树的非叶型结点中将后缀实行压缩(例如,如果键值是"GREEN”和"HAMILTON”,而在这两个值之间发生分裂,那么就在非页级索引页面中存储"G”和"H”)。
数据页和数据行加锁
只对数据加锁机制支持两种方式:数据页加锁和数据行加锁。这些与它们的工作方式和所提供的功能相类似。这两种方式仅在对数据访问产生阻碍作用时,在加锁的尺度上有所区别。在数据页加锁方式下再采用数据行加锁方式具有两种作用(一种起正向作用,另一种起反向作用)。首先,较小尺度加锁机制的使用可能导致减少争用与冲突,然而当大量数据发生变化时,就有可能对加锁产生大量阻碍的情况发生。
特定使用的加锁类型
除非对配置参数加以特定,对所有的表都予置了隐含的全页面加锁机制。
sp_configure ‘lock scheme’, [allpages | datapages | datarows]
当数据库从原先版本的服务器中转储出来重新加载时,所有的表都被定义为全页面加锁的表。当建立一个新表时,可以不使用这个缺省值,可采用如下的句法格式:
create table <tablename>;… lock [allpages | datapages | datarows]
为了在使用的一个表中改变加锁类型,可以采用如下的句法格式:
alter table <tablename>; lock [allpages | datapages | datarows]
在一个现存的表中改变加锁方式,将引起下列三种行动后果发生:
首先,如果一张表从全页加锁转变为仅对数据加锁,或者从仅对数据加锁转变为全页加锁,在这两种类型之间就要对表进行选择以允许进行存贮格式改变。如果这是一个分区表,就要同时假定必要的并行级别和工作线程已经配置好的情况下,才能执行。
其次, 对表中的群聚性索引必须重新创建。因为我们能保证数据,所以如果从全页加锁方式转换为只对数据加锁时,这种重新创建可以通过"with sorted_data"来完成。然而,当从仅对数据加锁机制转换为全页加锁方式时,就要进行并行的索引创建操作。(请注意:如果这是一个分区表时,那么并行等级和工作线程的数目必须加以配置才能允许进行这种改变,否则这种迁移将会失败)
最后,非群聚性的索引将被重建,如果服务器已经为并行处理所加以配置,当进行本步骤时将加以采用。
由于这些活动同潜在的工作量有关,从全页加锁机制改变为仅对数据加锁或从仅对数据加锁改变为全页加锁机制都可能是耗费时间的活动。为了标注这一点,有以下一些选择:
如果可能的话,应该配置使用并行方式。这至少对执行非群聚性索引的哈斯(杂凑,即hashed)创建方法是必须的,但是如果可能的话,采用分区表和分区扫描将使系统得到更大的改进。
在选择进入和创建群聚性的索引之后,该任务将被设置检查点(checkpointed )所以,如果有充分的硬件资源,通过允许在任何一个时间点上,检查点任务可以具有多于10个(系统缺省值)的异步I/O请求,利用dbcc进行调谐将能够带来有益的效果。(‘maxwritedes’, number)
进一步作为降低使用检查点成本的一种方法,在相关的高速缓冲池(cache pool)、大数据量的I/O操作中,采用对高淘汰程度进行标记的方法,并允许清洁程序(好象家庭主妇一样)保持特别活跃的状态,将为那些检查点需要从高速缓冲池中刷新较“脏”的页面的而增加的I/O操作次数,并因此花费了在检查点上的时间,都能够大大减少作出贡献。
如果预先进行了配置,则可以对并行的选择进入可以使用预先分配的盘区。所以,通过将sp_configure number of pre-allocated extents设置为16也将对系统性能有明显的积极的效果。
备注:在仅对数据加锁类型之间进行改变不需要对数据进行备份, 而且执行起来只需很短的一段时间。
[以上为摘自sybase公司站点上的资料]
据我个人使用经验,我觉得对于并行性较高的应用要充分考虑使用行级锁,这样对于提高并发性能至关重要!当然,事务都存在利弊两方面,使用行级锁,也会带来一些相应的弊端,比如使用的锁越多,占用的内存也越多,在使用行级锁的表上频繁的进行数据删除、插入操作久而久之会造成数据库碎片的大量生成,数据库性能会下降,这就需要定时进行reorg操作,但该操作比较耗时,且影响业务!
提问:(zxar )
几个疑问,盼指点
1。allpages-locks是不是就是表锁,我以前一直是这么理解的,但是现在有疑问的是,如果我的where从句里的条件是针对索引的,它是对所有满足条件的页加锁,还是不管条件,对表加锁,包括索引页吗
2。data-only-locks得锁就是针对一页而言吗,我不明白的是,为什么称之为data-locks,难道不对索引页加锁,难道这就是跟allpages-locks得区别,而不是页数的区别
3。还有这些锁具体对页存储数据,到底起了什么作用,因为对于这两个锁,每个页的开销是不一样得,而且对于clusted,nonclusted,也有影响
回复:(jazy)
我的理解是:
1、全页锁(allpages lock) 对查询的表及索引页加锁,也就是table lock
2、页锁 (data lock) 对所查询的结果所在页加锁,对索引不加锁
3、行锁 (row lock) 对某行数据加锁
好像一个lock占用的内存为120byte!
锁只是一种保护机制,并不影响数据存储!
提问:(aladdin)
如何缓解系统表上的资源争用,尤其是tempdb系统表上的锁争用.有实际处理经验吗?
回复:(changing)
tempdb系统表的锁竞争确实比较难解决,根本上还是要从应用设计开发上解决。也就是临时表的使用要适度,也可以考虑建一些共享的临时表,这样不需要频繁地创建和删除临时表。
zhhui2000 的意见:
全页锁(allpages lock) 若使用了正确的索引,只对查询的数据页及索引页加锁。另外,DOL(data only lock table)表的聚集索引的插入同allpages表的非聚集索引一样,并不是实时更新聚集索引。故若使用表DOL表应注意索引的维护。
jazy 的亲身经历及建议:
在我做的一个项目中有几个关键表因为对并发的要求比较高,所以,采用了DOL表,但是sybase数据库使用行级锁需要注意一些问题,比如索引更新及碎片整理的问题,若你的DOL表需要频繁的update变长字段和insert或delete记录,那么久而久之,你的表将会出现很多碎片索引信息也将可能出现部分失效,这时你需要更新索引信息(update statistics tablename)或重整DOL表(reorg REBUILD tablename)!当然,为了减少碎片增长速度可以在表设计或修改中增加exp_row_size 项的设置。
所支持的加锁机制
全页加锁
全页加锁既是一个新术语,它又是由ASE(Adaptive Server Enterprise)在过去所支持的一种加锁类型。这种类型有下列特性:
对所有可被访问的页面在页面级加锁
当各种类型的页面以任何方式发生改变时,对这些排它性的页面进行加锁;而且这种加锁机制一直保持到该事务终止;
当下一个所需的页面已经成功地获得,对那些已经释放的的当前访问页进行共享页面加锁(如果采用了第三层ANSI隔离,则把这种加锁机制保持到该事务终止为止)
采用页级时间印记(timestamp)以确定是否发生改变,详细信息记录在事务日志中,以便在系统恢复时以向前或向后方式使用。
这种加锁方式常常提供性能最高的解决方案,特别是当应用设计时已经考虑了这些特性时更是如此。但是,有一些应用系统,当发生某些活动时,这种对整个页面进行加锁的方式就可能会对系统性能产生有重大意义的影响。对于那些面对诸如文件系统或其它已经支持更细小尺度加锁机制的数据库厂家产品的一般环境而设计的应用系统而言,这种情况尤其如此。
此外,还存在一系列问题,它们要围绕着更加困难的条件进行工作。它们通常要采用更加具有Sybase特性的解决方案。对于商用的应用软件制造厂商而言,对他们是一个挑战,因为这将要求他们必须跨越他们所支持的数据库平台,去完成维护其原代码的工作,而这个工作有相当工作量。在这个领域的基本问题如下:
对已经按照升序值创建的非群聚性索引的最末端叶型页面存在着争议
对非群聚性索引的表进行插入和查询时可能发生死锁;
在按照群聚性的索引值进行更新和对非群聚性索引的表进行查询访问之间可能发生死锁;
在没有作索引的表的最后一行可能发生冲突(尽管对最后的特定地址可以使用分区) ;
有可能使行数很少的表之间发生潜在的冲突(尽管对特定的地址可以使用填充因子[ fillfactors]和每页最大行数[ max_rows_per_page]这两个参数)
对每个页面两边进行加锁的需要常常被分割开来;
如果一个表特别小,以致在一个单一页面中进行驻留,那么对单一行的访问实际上将破坏对整个表的加锁机制。
仅对数据加锁
仅对数据加锁机制试图去解决本文前一节所关注的主要问题(其他的议题将在其它功能领域中加以解决)。这种加锁方式支持两类不同的工作方式: 数据行加锁和数据页加锁。在这两种情况中,对于它们所支持的加锁方式,都与以前的加锁机制有所不同。仅对数据加锁具有下列特性:
在索引页面中不会破坏事务加锁。相反,而是采用了一种称之为锁存的机制。锁存是一种类似于旋转锁(spinlocks)的同步方法,它们与事务无关并且只保留很短的周期(一般而言,当一个任务在数据库中物理上改变一小片数据时,这个周期相当于在共享存贮区中在一个2K的页面改变某些字节数据的时间)一旦完成之后,这个任务将直接打开这个锁存。当这种情况还可能临时同其它组块时 ,因为这种锁存不能对服务器任务进行有上下文的切换,也不能涉及死锁,并且只能保持主要的一小段时间,所以它们不能产生有显著意义的争用。
采用一个RID对单一行进行数据行加锁(行标识[RID----Row ID]是逻辑页号与所在页面上该行号的组合);
支持固定的行标识 RIDs, 它可以是向前的,允许不进行其RID的改变,就完成数据行的移动。当一行变大超过了它的可用空间时,采用上述结果对非群聚索引不需要进行任何改变。
不需要进行任何争用就可以在表的尾部进行插入操作,这一功能已经增加进来。.
支持采用范围加锁、下一个关键字加锁和无限大加锁等方式对逻辑范围值进行加锁
支持由最顶层操作所导致的页面分割。这些情况直接加以提交,"系统"事务可以导致在更短一点的时间周期里保持分裂的页面处于锁定状态。
为了支持这些变化,需要对采用的存贮表结构进行一系列改善。这些改进的主要效果如下:
群聚索引现在被存贮为象许多人所熟悉的IBM DB2产品所采用的“放置索引”("placement indexes.”)方式。这种结构类似于非群聚性的索引,需要类似的空间总量。这种修正的结构导致了在数据初始存贮时可以按照顺序跨数据页进行存储,但是当发生插入时,它们就要尽可能紧密存放以便在正确的逻辑页面中不存在页面分割。此外,在数据页中的数据顺序在新行增加时是不进行维护整理的。这种索引的应用使每个群聚化的索引周游增加了一次I/O操作。
行位移表已经增加到索引页和数据页中。这种增加和新的行索引行存贮格式具有使每个索引页面所存贮的索引条目个数减少的潜在能力。
固定行标识(RIDS)。当一行移动时,对于分配新行位置的向前地址被放在用于驻留该行的位置上。当这种移动需要改变非群聚性索引时,对该行的访问需要增加一次I/O操作以得到‘向前’的位置。
一般而言,索引将更小和更短,这是因为如下原因:
从每个叶级页面中采用双重键限制机制来限制双重键(Duplicate key)例如,如果值“绿”("GREEN”)在下列行标识(RIDs)值等于123-1,234-2,和345-3的行中, 就分别存贮值“绿”("GREEN”),123-1,234-2,345-3,而不是存贮值“绿”("GREEN,”)三次。在每个索引页中每个值只存贮一次。
在非群聚性索引树的非叶型结点中将后缀实行压缩(例如,如果键值是"GREEN”和"HAMILTON”,而在这两个值之间发生分裂,那么就在非页级索引页面中存储"G”和"H”)。
数据页和数据行加锁
只对数据加锁机制支持两种方式:数据页加锁和数据行加锁。这些与它们的工作方式和所提供的功能相类似。这两种方式仅在对数据访问产生阻碍作用时,在加锁的尺度上有所区别。在数据页加锁方式下再采用数据行加锁方式具有两种作用(一种起正向作用,另一种起反向作用)。首先,较小尺度加锁机制的使用可能导致减少争用与冲突,然而当大量数据发生变化时,就有可能对加锁产生大量阻碍的情况发生。
特定使用的加锁类型
除非对配置参数加以特定,对所有的表都予置了隐含的全页面加锁机制。
sp_configure ‘lock scheme’, [allpages | datapages | datarows]
当数据库从原先版本的服务器中转储出来重新加载时,所有的表都被定义为全页面加锁的表。当建立一个新表时,可以不使用这个缺省值,可采用如下的句法格式:
create table <tablename>;… lock [allpages | datapages | datarows]
为了在使用的一个表中改变加锁类型,可以采用如下的句法格式:
alter table <tablename>; lock [allpages | datapages | datarows]
在一个现存的表中改变加锁方式,将引起下列三种行动后果发生:
首先,如果一张表从全页加锁转变为仅对数据加锁,或者从仅对数据加锁转变为全页加锁,在这两种类型之间就要对表进行选择以允许进行存贮格式改变。如果这是一个分区表,就要同时假定必要的并行级别和工作线程已经配置好的情况下,才能执行。
其次, 对表中的群聚性索引必须重新创建。因为我们能保证数据,所以如果从全页加锁方式转换为只对数据加锁时,这种重新创建可以通过"with sorted_data"来完成。然而,当从仅对数据加锁机制转换为全页加锁方式时,就要进行并行的索引创建操作。(请注意:如果这是一个分区表时,那么并行等级和工作线程的数目必须加以配置才能允许进行这种改变,否则这种迁移将会失败)
最后,非群聚性的索引将被重建,如果服务器已经为并行处理所加以配置,当进行本步骤时将加以采用。
由于这些活动同潜在的工作量有关,从全页加锁机制改变为仅对数据加锁或从仅对数据加锁改变为全页加锁机制都可能是耗费时间的活动。为了标注这一点,有以下一些选择:
如果可能的话,应该配置使用并行方式。这至少对执行非群聚性索引的哈斯(杂凑,即hashed)创建方法是必须的,但是如果可能的话,采用分区表和分区扫描将使系统得到更大的改进。
在选择进入和创建群聚性的索引之后,该任务将被设置检查点(checkpointed )所以,如果有充分的硬件资源,通过允许在任何一个时间点上,检查点任务可以具有多于10个(系统缺省值)的异步I/O请求,利用dbcc进行调谐将能够带来有益的效果。(‘maxwritedes’, number)
进一步作为降低使用检查点成本的一种方法,在相关的高速缓冲池(cache pool)、大数据量的I/O操作中,采用对高淘汰程度进行标记的方法,并允许清洁程序(好象家庭主妇一样)保持特别活跃的状态,将为那些检查点需要从高速缓冲池中刷新较“脏”的页面的而增加的I/O操作次数,并因此花费了在检查点上的时间,都能够大大减少作出贡献。
如果预先进行了配置,则可以对并行的选择进入可以使用预先分配的盘区。所以,通过将sp_configure number of pre-allocated extents设置为16也将对系统性能有明显的积极的效果。
备注:在仅对数据加锁类型之间进行改变不需要对数据进行备份, 而且执行起来只需很短的一段时间。
[以上为摘自sybase公司站点上的资料]
据我个人使用经验,我觉得对于并行性较高的应用要充分考虑使用行级锁,这样对于提高并发性能至关重要!当然,事务都存在利弊两方面,使用行级锁,也会带来一些相应的弊端,比如使用的锁越多,占用的内存也越多,在使用行级锁的表上频繁的进行数据删除、插入操作久而久之会造成数据库碎片的大量生成,数据库性能会下降,这就需要定时进行reorg操作,但该操作比较耗时,且影响业务!
提问:(zxar )
几个疑问,盼指点
1。allpages-locks是不是就是表锁,我以前一直是这么理解的,但是现在有疑问的是,如果我的where从句里的条件是针对索引的,它是对所有满足条件的页加锁,还是不管条件,对表加锁,包括索引页吗
2。data-only-locks得锁就是针对一页而言吗,我不明白的是,为什么称之为data-locks,难道不对索引页加锁,难道这就是跟allpages-locks得区别,而不是页数的区别
3。还有这些锁具体对页存储数据,到底起了什么作用,因为对于这两个锁,每个页的开销是不一样得,而且对于clusted,nonclusted,也有影响
回复:(jazy)
我的理解是:
1、全页锁(allpages lock) 对查询的表及索引页加锁,也就是table lock
2、页锁 (data lock) 对所查询的结果所在页加锁,对索引不加锁
3、行锁 (row lock) 对某行数据加锁
好像一个lock占用的内存为120byte!
锁只是一种保护机制,并不影响数据存储!
提问:(aladdin)
如何缓解系统表上的资源争用,尤其是tempdb系统表上的锁争用.有实际处理经验吗?
回复:(changing)
tempdb系统表的锁竞争确实比较难解决,根本上还是要从应用设计开发上解决。也就是临时表的使用要适度,也可以考虑建一些共享的临时表,这样不需要频繁地创建和删除临时表。
zhhui2000 的意见:
全页锁(allpages lock) 若使用了正确的索引,只对查询的数据页及索引页加锁。另外,DOL(data only lock table)表的聚集索引的插入同allpages表的非聚集索引一样,并不是实时更新聚集索引。故若使用表DOL表应注意索引的维护。
jazy 的亲身经历及建议:
在我做的一个项目中有几个关键表因为对并发的要求比较高,所以,采用了DOL表,但是sybase数据库使用行级锁需要注意一些问题,比如索引更新及碎片整理的问题,若你的DOL表需要频繁的update变长字段和insert或delete记录,那么久而久之,你的表将会出现很多碎片索引信息也将可能出现部分失效,这时你需要更新索引信息(update statistics tablename)或重整DOL表(reorg REBUILD tablename)!当然,为了减少碎片增长速度可以在表设计或修改中增加exp_row_size 项的设置。