INFORMIX锁机制及如何分析其锁冲突(第一部分)
本文讲述INFORMIX数据库锁的基本原理,由2部分组成。IDS是OLTP应用及内嵌式系统的最佳解决方案。通过本文可以帮助你理解数据库锁的使用方法,便于你及时处理、分析锁冲突。
介绍
在多用户数据库系统中拥有成千上万的并发用户在同时访问数据,因此我们需要有某种机制来保护数据以维护其数据一致性。除了事物日志机制外,锁机制就是我们采用的另一个主要手段。
然而,锁机制经常会导致冲突及等待现象的发生。这通常是一个DBA在日常管理工作中会碰到的一个普遍问题。如果缺乏一些适当的脚本或手段,往往会导致在分析锁问题时变得越来越复杂并出现错误。
本文将阐述IDS的锁机制,帮助你分析出现的锁冲突及锁等待的情况。以下的示例基于数据库stores_demo,该数据库可通过以下命令
创建:
锁类型
IDS包含几种不同的锁。通常有:
共享锁
共享锁可以被放置在没有设置排他锁的记录上。其他用户可以在相同的记录上放置共享锁或更新锁,但是不能再放置排他锁。
更新锁
更新锁是一种在使用更新游标时产生的特殊类型的锁。更新锁只能被放置在当前没有设置更新锁或排他锁的记录上。一旦记录被加上更新锁,它的数据在被修改的同时就会立刻升级为排他锁。
排他锁
排他锁只能被放置在没有任何锁的记录上。一旦排他锁被放置在该记录上,则其他锁也就不能在增加到该记录了。该记录将被被会话独占使用。
内部锁
内部锁包含多种特殊类型的锁。举个例子,当一条记录被修改时,该记录上被放置一个排他锁,同时所涉及的数据库表上也被放置一个排他的内部锁。这是为了确保当该表的数据在排他使用时其他的会话不能在该表上再设置共享或排他锁。
锁粒度
IDS允许应用开发人员在不同的对象上放置锁。有以下对象。
数据库锁
数据库可以以排他或共享的方式被锁住。在排他的情况下将防止其他任何人再访问该数据库。在共享的情况下允许并发用户读取或修改该数据库的数据,但是不允许在该数据库上再放置排他锁。
数据库共享锁:
数据库共享锁在你打开数据库的同时将被自动设置。这将防止没有其他用户可以在该数据库上再放置排他锁或删除数据库。一些数据库工具:onunload也会在数据库上放置共享锁
SQL语句:
在这里你可以看到数据库上加有共享锁HDR+S。tblsnum=100002表明了数据库的表空间。rowid=207是该数据库信息存储在数据库
sysmaster,表sysdatabases中记录的16进制信息。数据库表sysmaster:sysdatabases通常也被称作数据库的表空间。
数据库及他们的16进制rowid信息可以通过以下sql语句获得:
Listing 2. Query on database sysmaster retrieving the row ID of a database tablespace entry
数据库排他锁:
一个排他锁可以被排他的设置在数据库上。数据库工具:dbexport在倒出数据的时候就会在数据库上放置一个排他锁。
SQL 语句:
Listing 3. Exclusive lock on a database
在这里你可以看到一个排他锁(HDR+X)被放置在数据库上,其对应的rowid为207
表锁:
与数据库锁类似,数据库表也可以以排他或共享的形式被锁住。排他锁可以防止其他用户读取或修改该表信息。一种例外是该会话运行在脏读的隔离级下。这样就可以从已设置排他锁的表中读取数据,但是该数据可能是不准确的(正在修改中,没有真正提交)。在表上设置共享锁可以允许其他用户读取该表数据,但是不允许修改数据。
表共享锁:
共享锁可以以共享的形式放置在数据库表上。数据库工具:onunload、oncheck在使用的时候会在数据库表上设置共享锁(依赖于表的锁模式是页还是记录)
SQL 语句:
Listing 4. Share lock on a table
你可以看到一个共享锁(HDR+S)和rowid被设置为0(rowid=0)。rowid为0表明是一个表锁。tablespace number (tblsnum=10013b)构成该表的16进制partition number。
会导致这种情形的2种可能:
oncheck命令
Data Dictionary查询命令
Listing 5. Identifying a table through its hexadecimal partition number
注意到你需要使用0x00对通过onstat -k获得的16进制信息进行补足,使其形成8位完整的16进制数。同时需将小写字母转换为大写字母,例如0x10013b-》0x0010013B。
如果是分片表的partnum,你需要在sysfragments表里面查找相应信息:
Listing 6. Identifying a fragmented table through its hexadecimal partition number
本例中的partition number 0x0010013B不是分片表的一个分片,因此在sysfragments表中不存在对应信息。但是对于分片表则会有信息记录在sysfragments表中,请使用前面提供的语句进行查询。
oncheck -pt <hex_partnum>不能对分片表信息进行查询,它不会显示真实的分片表名,仅有分片信息。
表排他锁:
排他锁可以被显式的或隐式的被设置在对应的表上。例如:alter table
SQL语句:
Listing 7. Exclusive lock on table
在这里一个排他锁(HDR+X)被设置在表上,对应的tblsnum为10013b
页或记录级锁:
锁的级别(页或记录锁)可以在创建表的时候进行指定,或使用alter table对已有表进行修改。
建表:
修改表:
缺省锁模式,在创建表的时候采取何种锁模式,可以通过在配置文件中配置DEF_TABLE_LOCKMODE来进行指定或通过环境变量IFX_DEF_TABLE_LOCKMODE来指定。在建表时所指定的锁模式会优先于通过DEF_TABLE_LOCKMODE或IFX_DEF_TABLE_LOCKMODE所指定的锁模式。
使用页锁意味着即使只修改一条记录也会将该记录所在的整个数据页进行加锁。依赖于记录的大小及数据页的大小,采用这种方式会降低数据库的并发处理性能。特别需要注意的是采取页锁不仅会对相关数据页有影响,而且对索引页同样也会有影响。这样会进一步降低数据库的并发处理性能,因为索引页相对于数据页来说通常会包含更多的记录。
你可以通过以下2种方式来确定当前表采取了何种锁模式:
Listing 8. Determining the lock mode of a table using oncheck (a) or a dictionary query (b)
a) Command:
b) Query:
以下为IDS中锁模式的缩写标志:
B 视图(需要检查其依赖的锁模式)
P 页级锁
R 记录锁
可以通过以下SQL语句产生相应的修改已存在表的锁模式的语句:
Listing 9. Using meta SQL to convert all tables to row locking
Meta SQL:
页锁:
对于配置为使用页锁的表,在数据处理的过程中页锁会被设置在对应的数据页上。锁的类型可能为排他锁(插入、修改、删除)或共享锁(设置隔离级为重复读)
SQL语句:
Listing 10. Exclusive lock on a page
本例出现了3中类型的锁:
1.数据库上的共享锁(HDR+S)。tblsnum=100002是该数据库的partnum。
该共享锁可以防止其他用户在该数据库上设置排他锁
2.表上的内部排他锁(HDR+IX)。tblsnum=0表明这是一个表锁。
内部排他锁可以防止其他用户在该表上设置共享或排他锁。
3.数据页上的排他锁(rowid=100,如果rowid的后2位为00,则表明该锁为页锁)
排他锁可以防止其他用户在该数据页上放置共享锁或排他锁。
记录锁:
如果数据库表被设置为记录级锁,则在数据的处理过程中,只有相关的记录上被设置上锁。锁的类型可能为排他锁(插入、修改、删除)或共享锁(设置隔离级为重复读)
SQL语句:
Listing 11. Share lock on a row
Output from onstat -k:
本例出现了4种类型的锁:
1、数据库上的共享锁(HDR+S)。tblsnum=100002是该数据库的partnum。
该共享锁可以防止其他用户在该数据库上设置排他锁
2、表上的内部排他锁(HDR+IX)。tblsnum=0表明这是一个表锁。
内部排他锁可以防止其他用户在该表上设置共享或排他锁。
3、在记录上设置的共享锁(rowid=10a)
记录上设置的共享锁可以防止其他用户在该记录上设置排他锁。
4、索引上设置的共享锁(K-1)
在索引上设置的共享锁可以防止其他用户在该索引值上设置排他锁。
你可以通过以下SQL查询到被设置记录锁的相关记录:
Listing 12. Identifying a locked row
Query:
索引键锁:
类似于数据记录上的锁,IDS同样在索引键上设置对应的锁,以保护其内容。键值锁可以确保唯一索引保持其唯一性,在事物未提交以前不允许相同值的记录插入到同样的索引位置。
索引键锁可以通过在onstat -k的输出中于key#/bsiz字段中由K来表示出来。
数据库的日志模式
IDS中可以使用的数据库日志模式为以下4种:
无日志模式
缓冲日志模式
非缓冲日志模式
ANSI日志模式
这些日志模式在后续章节中进行阐述。
无日志模式:
运行在无日志模式下的数据库。不能执行begin、rollback、commit操作,事物信息不会被记录在逻辑日志中。每条SQL语句的执行,自动提交。所涉及的锁只会影响当前正在执行的语句。
缺省隔离级:
脏读。这是在采用无日志模式的数据库上唯一可以使用的隔离级
SQL语句:
缓冲日志模式:
事物信息被记录在逻辑日志中。如果你想包含多条SQL操作于一个事物中,则显式的begin操作是必须的。事物的结束以commit或rollback为标志。如果没有显式的begin作为事物的开始,则每条SQL语句将被看为独立的事物。
如果commit命令被执行,整个事物的信息并不是立即被写入磁盘上的逻辑日志。这些信息将会被保存在共享内存中的log buffer里。
当log buffer满或使用非缓冲日志方式的数据库事物、使用ANSI日志模式的数据库事物在事物提交的时候这些信息才真正的被写入到磁盘上。
你应该衡量由此带来的对于数据库事物吞吐率提升的好处与当数据库宕机时造成的数据丢失风险之间的利弊。如果你需要较高的事物吞吐率,同时需要保存关键的业务数据,使用高速磁盘或使用电池的ramdisk是一个不错的选择。
缺省隔离级:
提交读
使用该日志模式的数据库缺省隔离级为提交读,但是用户可以使用set isolation to <isol_level> 命令设置适当的其他隔离级
SQL命令:
非缓冲日志模式:
与缓冲日志模式类似,但是使用该日志模式的数据库在事物提交时将会立即将相关的事物信息写入磁盘。你不会丢失任何已提交的事物信息。
缺省隔离级:
提交读
使用该日志模式的数据库缺省隔离级为提交读,但是用户可以使用set isolation to <isol_level> 命令设置适当的其他隔离级
SQL命令:
ANSI日志模式:
使用该日志模式的数据库的缺省隔离级为重复读,这意味着在处理的每条记录上将会被设置上共享锁。这会导致锁冲突及所等待现象的出现。隔离级别可以通过set isolation to <isolation_level> 命令进行切换。
缺省隔离级:
重复读
使用该日志模式的数据库可以使用set isolation to <isol_level>命令来进行隔离级的切换
SQL命令:
Database logging modes and default isolation levels
Database logging mode Create statement Default isolation level
No logging create database stores_demo Dirty read
Buffered logging create database stores_demo with buffered log Committed read
Unbuffered logging create database stores_demo with log Committed read
Mode ANSI (unbuffered logging) create database stores_demo with log mode ansi Repeatable read
当前数据库日志模式可以通过onmonitor工具或查询sysmaster数据库来获得:
onmonitor (status-databases)
N -- 无日志
B -- 缓冲日志模式
U -- 非缓冲日志模式
U* -- ANSI模式 (unbuffered logging)
sysmaster query
Listing 13. Determining the logging mode of a database
Query:
你可以通过ontape或ondblog工具进行数据库日志模式的切换。需要注意的是一旦数据库设置为ANSI日志模式,它将不能被切换为其他的日志模式。
隔离级:
在IDS中依赖于采用不同日志模式的数据库,有4中不同的隔离级可供使用。后面将一一予以介绍。
IDS isolation levels and their ANSI counterparts
Informix SQL ANSI SQL
Dirty read Read uncommittted
Committed read Read committed
Cursor stability Not available
Repeatable read Serializable
脏读(ANSI: Read uncommitted)
如果你在读取数据的时候采取该隔离级,那么你将不会在涉及到的数据上设置锁,也不会碰到任何锁的问题。但是,你有可能得到的是一个不准确的数据,因为该数据可能还没有被提交。
脏读是未带日志模式的数据库唯一可供选择的隔离级别。
informix SQL
ANSI SQL
提交读(ANSI: Read committed)
使用该隔离级可以确保所读出的数据是已经被提交后的。
运行在提交读隔离级下的会话会尝试在需要读取的数据上设置共享锁,但不真正设置。这样就能确保不会读取到其他正在并发修改的数据。然而,在读取数据以后,由于没有在该数据上设置任何锁,因此其他的并发事物可以对该数据进行修改。
提交读是使用缓冲日志模式和非缓冲日志模式数据库锁采用的缺省隔离级,但非使用ANSI日志模式的数据库所采取的隔离级。
Informix SQL
ANSI SQL
游标稳定读(ANSI: --)
游标稳定读隔离级使用在更新游标中。
IDS在当前读取的数据上放置一个更新锁。如果该数据在此时被修改,则转换为一个排他锁并一直保持到事物结束,并不依赖于游标所处的当前位置。如果读取到下一条数据而当前的数据没有被修改,那么当前数据上的更新锁将被释放,同时在下一条数据上放置更新锁。如果在set isolation命令中加上retain update locks子句则会改变这种情况。这样在读取到下一条数据的时候上一条数据上所放置的更新锁将不
会被释放。
更新锁只能被放置在当前没有被设置任何更新锁或排他锁的数据上。然而,一旦更新锁被放置,其他会话仍然是可以在该数据上放置共享锁的。这种情况下如果你试图去更新这些数据将会导致错误,因为由于存在共享锁IDS将不能把一个更新锁升级成为一个排他锁。这是我们希望得到的一种结果。如果你确定在后续的操作中你肯定需要更新数据,那么你可以使用dummy update将所有的更新锁都升级为排他锁。这将避免其他的并发会话在相同的记录上设置共享锁。
Informix SQL
ANSI SQL
--
重复读(ANSI: Serializable)
使用重复读隔离级将会在所读取的记录上都设置共享锁(如果使用页级锁将会在所涉及的数据页上都设置共享锁)。这将可以避免其他用户更改这些数据。
retain update locks子句在这种隔离级下将不能使用,因为这种隔离级本身就会自动保持共享锁或更新锁。
重复读这种隔离级是采用ANSI日志模式的数据库所使用的缺省隔离级别。
Informix SQL
ANSI SQL
Informix SQL 与 ANSI SQL之间的区别:
在informix使用的SQL命令set isolation to <isol_level>和ANSI使用的SQL命令set transaction isolation level <isol_level>之间具有2个重要的区别。
在informix中所执行的命令是基于会话的。也就是说在整个会话中你一旦设置好隔离级就会立刻生效直到你下一次使用set isolation to <isol_level>命令重新设置为止。
对应的在ANSI中所执行的命令是基于事物的。隔离级设置好以后将会只对当前事物有效。一旦事物结束,则隔离级别将会自动切换为当前打开数据库所具有的缺省隔离级。更多的信息,请参照数据库日志模式一节。
在数据库事物中你将不能使用ANSI的SQL命令set transaction isolation level <isol_level>来设置新的隔离级别。这是不允许的。但是informix允许在事物中进行隔离级别的切换。
会话的隔离级别:
你可以通过执行onstat -g sql命令来得到当前正在执行的会话所使用的隔离级别。
Listing 14. Listing isolation levels of individual database sessions
当前数据库所采用的隔离级别将显示在Iso Lvl字段下,以下为其缩写方式:
DR -- 脏读
CR -- 提交读
CS -- 游标稳定读
RR -- 重复读
小结:
现在你对数据库锁的类型、锁的粒度、数据库的日志模式、隔离级别有了一个初步的认识。在后续部分我们将对以下内能进行介绍:
锁的等待时间
锁的动态分配
死锁
锁的等待情况
本文作者:Eric Herber
INFORMIX锁机制及如何分析其锁冲突(第二部分)
介绍
在多用户数据库系统中拥有成千上万的并发用户在同时访问数据,因此我们需要有某种机制来保护数据以维护其数据一致性。除了事物日志机制外,锁机制就是我们采用的另一个主要手段。
然而,锁机制经常会导致冲突及等待现象的发生。这通常是一个DBA在日常管理工作中会碰到的一个普遍问题。如果缺乏一些适当的脚本或手段,往往会导致在分析锁问题时变得越来越复杂并出现错误。
本文将阐述IDS的锁机制,帮助你分析出现的锁冲突及锁等待的情况。以下的示例基于数据库stores_demo,该数据库可通过以下命令创建:
锁的动态分配
它是如何工作的
在onconfig配置文件中的参数LOCKS(最大值:8000000)指定了整个实例在启动时初始化可供使用的锁资源。然而后续的IDS版本具有动态分配锁资源的能力(大约是9.3X以后)。如果锁的使用量达到最初设置的初始值,则IDS会自动扩大其内部锁资源表的大小。这个过程将会重复15次。每次扩大最多可以增加100000个锁资源,因此IDS锁资源的最大值为9500000。
8.000.000 inital + (15 x 100.000) additions = 9.500.000 total
动态锁资源的分配功能能够使当锁的使用量在达到预先设置的最大值以后,仍然可以继续分配锁资源,确保应用的继续进行,同时又能保证锁资源表不是特别大,以达到较高的运行性能。
在IDS 10.0.FC5及后续版本,可设置的LOCKS最大值扩大为500000000,每次可动态增加的锁资源扩大为1000000,可重复扩充的次数增大为100,因此锁资源使用上限扩大为600000000。但应注意的是锁资源并不是设置得越大越好,因为这会浪费资源降低系统运行性能,因此应根据需要合理设置。
非优化的应用
有时候,动态分配锁资源会将一些设计不好的应用程序掩盖掉。比较好的一种方式是应该能够对单个会话所能申请的最大锁资源进行控制。这样就能够帮助DBA快速定位那些执行不好的应用,同时又不好影响其他应用的正常运行。
目前当数据库达到锁资源的上限以后,如果仍需申请锁资源,那么所有申请资源正在运行的会话都会接收到锁表溢出的错误。这意味着很多应用都会由于一个编写不好的应用所牵连。这并不是一个理想的状态。
锁等待时间
针对每个数据库会话进行设置
每个数据库会话都可以单独设置锁等待时间,以防止由于锁等待超时而影响到整个应用。IDS缺省是不设置锁等待时间的,这也就是说一旦数据库检测到锁冲突存在,数据库会立刻返回错误给对应的应用。
-244: Could not do a physical-order read to fetch next row
107: ISAM error: record is locked
为此你可以对每个会话设定相应的锁等待时间:
set lock mode to not wait
set lock mode to wait
set lock mode to wait <#sec>
分布式事物:
对于分布式事物,配置参数DEADLOCK_TIMEOUT将会指定本地IDS数据库等待远端数据库锁资源的最长时间,如果在此时间内未能获得相应的锁资源则返回错误给应用。
推荐方法:
如果指定无限制的锁等待(set lock mode to wait) 这并不是一个好方法。这会加剧锁冲突和死锁现象的出现,这是一个不好的编程习惯。
在OLTP环境中一般设置锁等待时间为5-10秒是比较合理的。如果在指定的时间内不能获得对应的锁资源,数据库会返回一个SQL错误给应用。这时应用可以决定是否重新发起该操作或回滚整个事物。
检查会话的设置情况
你可以通过onstat -g sql命令获得当前正在运行会话的隔离级及锁等待的模式:
Listing 1. Session isolation level - onstat -g sql
本例显示了2个采用不同隔离级的数据库会话:
Session 18, CR Committed Read, Not Wait
Session 16, RR Repeatable Read, Wait 10
后续一个比较好的功能可能是:在没有显式使用set lock mode to wait...命令设置锁等待时间的情况下,数据库具有在数据库或实例一级设置缺省的锁等待时间的能力。
死锁:
死锁出现在2个用户同时拥有各自的锁资源,同时他们又互相申请属于对方的锁资源。为了避免死锁情况的出现,会话在申请一个锁资源的时候,IDS通过扫描内部锁表来确定是否可能产生死锁,一旦发现有此可能即会发送ISAM error code -143 (deadlock detected)给相关的会话。
检测到的可能死锁数目:
通过onstat -p命令中的(deadlks column)字段将会显示自数据库启动或执行onstat -z (zero statistics)命令以来数据库所检测到的可能死锁数目。你可以通过以下SQL命令获得精确的最近一次数据库启动时间:
Sysmaster表:
不幸是IDS没能提供更多的关于如何检测死锁及相关应用的信息。这将使一旦出现死锁情况以后难于对其如何解决进行分析。sysmaster数据库中的一些表能够提供进一步的信息:
sysprofile:该表中所保存的死锁检测数与onstat -p命令中deadlks字段所提供的信息是对应的
syssesprof:该表中保存了每个数据库会话所检查到的可能死锁数
sysptprof:该表中保存了每个数据库表所检查到的可能死锁数
跟踪死锁:
IDS的错误跟踪机制将有助于分析死锁的现象。因为每次在IDS检测到一个可能出现的死锁的时候将会抛出ISAM error code -143,因此你可以使用onmode -I 143命令去跟踪该错误。当下一个死锁可能被检查到的时候数据库会立刻产生一个断言错误文件,同时会保存onstat -a的信息。这样你就可对这些收集到的信息进行分析,找出可能会造成死锁的对应会话,它的SQL执行语句及拥有锁资源的其他相关会话信息。
死锁超时:
配置参数DEADLOCK_TIMEOUT不会对本地死锁的检测时间或本地应用的整个等待时间产生任何影响。对于出现在本地的可能死锁现象IDS是能够立刻检测出来的。
如果一个锁等待的情况出现在试图查询或修改远端数据库的数据的时候,本地数据库实例将会按照通过DEADLOCK_TIMEOUT所指定的等待时间上限进行等待,如果超过该时间,将会被判定为可能出现了一个死锁情况。ISAM error code -154 (deadlock timeout expired - possible deadlock) 将被返回给相关应用。
目前IDS还不能跨数据库实例的检查内部锁表信息;因此它只能通过DEADLOCK_TIMEOUT参数来指定分布式的锁超时时间,以此达到检测可能死锁的情况。在分布式事物中可以通过set lock mode to wait去重新设置相应的由DEADLOCK_TIMEOUT参数所指定的死锁等待超时时间。
分析锁冲突:
在OLTP环境中通常会有众多的并发会话同时访问同样的记录。因此,一个比较好的情况是我们使执行的事物尽可能的小以减少其出现锁冲突的可能性。
如果在你的应用中你不同通过set lock mode to wait来设定锁等待时间,当你在访问一个数据,而这个数据已经被其他用户设置上锁的时候,你将立刻得到锁失败的SQL错误。这种错误通常是大家所不希望的,所以在应用中一般通过set lock mode to wait命令来重新设定锁等待时间,这样IDS的SQL执行线程在出现锁冲突的时候就会进行等待,直到获得锁资源或锁超时。但是这种情况又会使整个实例的运行效率下降。由此用户会抱怨响应时间慢或长时间的执行批量作业。通常,你不能将锁等待的时间设置为无限等待。
在一个正在运行的环境中分析锁等待的情况通常具有一定的挑战性。一个比较好的方式是可以通过使用一些恰当的脚本来实时分析这些锁等待的情况。在下节我们将会提供一些有用的onstat命令。你可以由此为起点编写出更好的自动运行脚本,你也可以使用我们提供的lockwt工具。
一些有用的onstat命令:
出现锁冲突以后使用onstat -u命令是一个较好的起点。它包含2个有用的信息:
当前每个会话所拥有的锁资源信息
locks字段将会告诉你该会话拥有多少锁资源。那些使用了较多锁资源的会话通常会导致锁冲突。这并不是必然情况,但使用较大锁资源的应用间接也说明了它可能不是一个比较优化的应用。
会话当前等待锁的情况
在flags字段的首位将会以‘L’的形式表明该会话正在等待一个锁资源
Listing 2. Locks held and sessions waiting for a lock - onstat -u
你可以通过执行onstat -k | grep 'L-'命令来获得所有当前正在等待锁的所有会话信息。通过onstat -g ses <sessid>命令,你可以确定哪个具体的SQL正在被会话执行。你也可以看到当前被打开的数据库(Current Database),当前所使用的隔离级(Iso Lvl),锁的等待时间(Lock Mode)。在status字段,你可以看到还剩余多少时间将到达锁超时所设定的时间。
Listing 3. Analyze sessions in lock wait status - onstat -g ses <sessid>
下一步,需要确定锁资源的拥有者,它将导致锁等待。使用onstat -k命令可以列出数据库实例当前所分配的所有锁资源信息:
Listing 4. Locks currently allocated in IDS - onstat -k
在这部分第二个字段wtlist能够提供一些有用信息。它包含了正在等待该锁资源的用户线程16进制地址信息。但是由于当前实例所配置的锁资源可能较多,因此onstat -k命令的输出也较大。你可以使用以下命令快速定位哪个会话是当前申请锁资源的拥有者,例如线程地址55a77bd0正在等待的锁资源:
owner字段将告诉你锁资源拥有者的内存地址信息。然后你可以根据这些信息通过onstat -u和grep命令找到对应的会话信息。这样你就可以使用onstat -g <sessid>命令来具体分析是什么原因导致了锁等待。如果你发现这个锁资源的拥有者也在等待其他的锁资源(在onstat -u输出的flags字段第一个符号为‘L’表示),那么你应该重复上述的步骤找到该锁资源的下一个拥有者。
Lockwt工具:
使用Esql/C编写的工具lockwt非常适合用来分析锁等待的情况。为了能使用该工具,你需要安装Informix Client SDK及C编译器,然后编译该工具。该工具会搜索sysmaster数据库里面的系统表以找到出现的锁等待情况信息。
该工具将显示每个会话所占有锁资源的信息,及哪些会话在等待这些锁资源释放。执行lockwt -r <#sec> 命令可以按照指定的时间间隔重复收集信息(类似于onstat -r命令)。
该工具可以实时监控复杂的锁资源等待情况,同时以简单易读的形式将相关信息显示出来。
Listing 5. Lockwt - Description of the output format
Output from lockwt:
-------------------
(0) (1) (2) (3) (4) (5) (6) (7) (8) (9)
|-------10--------20--------30--------40--------50--------60--------70--------80--------9|
|-------- XML error: The previous line is longer than the max of 90 characters ---------|
WAIT SID :PID PROCNAME USERNAME LKTYPE DATABASE:TABLENAME LKOBJ
0 - 13900:12303 workprocess3 dbuser X rome :orders row
|-------10--------20--------30--------40--------50--------60--------70--------80--------9|
|-------- XML error: The previous line is longer than the max of 90 characters ---------|
1 W 53600:23613 batchp12 dbuser rome :orders
Colno Purpose
(0) Sequence number
(1) Waiting or not waiting, possible values are:
"-" - this session is the holder of the lock and is always listed first.
"W" - this session(s) is(are) waiting for the above session.
(2) Session ID of this session in the database server
(3) Process ID of the UNIX process, remote connections have pid -1
(4) Process name of the UNIX process. If it is a remote connection
(pid = -1), no process name will be available.
(5) UNIX username of this session
(6) Type of lock, possible values are:
"X" - Exclusive Lock
"S" - Shared Lock
"U" - Update Lock
For additional lock types, execute the following sql-statement:
-> select txt from sysmaster:flags_text where tabname = "syslcktab"
(7) Database name
(8) Table name, the lock is on. If it is an index lock and the index is detached
from the table (has it's own partition number), the name of that index
is shown here.
(9) Type of object locked, possible values are:
"table" - this is a table lock
"idx" - this is an index key lock
"page" - this is a page lock
"row" - this is a row lock
Listing 6. Lockwt - Lock wait situation I
Output from lockwt:
-------------------
WAIT SID :PID PROCNAME USERNAME LKTYPE DATABASE:TABLENAME LKOBJ
0 - 13900:12303 workprocess3 dbuser X rome :orders row
1 W 53600:23613 batchp12 dbuser rome :orders
本例中,会话13900 (process "workprocess3")是表orders上锁资源的拥有者,会话53600正在等待这些锁资源的释放,因此你需要通过执行onstat -g ses 13900命令去检查会话13900以证实其运行是否正常。
Listing 7. Lockwt - Lock wait situation II
Output from lockwt:
-------------------
WAIT SID :PID PROCNAME USERNAME LKTYPE DATABASE:TABLENAME LKOBJ
0 W 3894: -1 (remote) eherber1 X rome :status row
1 W 17048: 3140 batchp3 dbuser rome :status
0 - 63296: -1 (remote) eherber1 X rome :customer_order row
1 W 3894: -1 (remote) eherber1 rome :customer_order
本例是一个交复杂的情况。会话17048正在等待会话3894设置在表status上的锁资源释放。但是通过检查下一部分可以发现,会话3894本身也在等待会话63296释放相应的锁资源。这是一个典型的锁等待传递案例,因为会话3894占据了别的会话需要使用的锁资源,而它本身又在等待被其他会话所拥有的锁资源。因此我们应该通过onstat -g ses 63296去具体分析会话63296是否在正常执行。
从工具lockwt的源代码中,你可以抽取那些与sysmaster相关的查询语句以形成你自己的查询语句。
打开游标的问题:
可能你在修改表的时候曾经碰到过类似的问题。即使你能够在该表上设置排他锁,你仍然不能对该表进行修改。以下为示例的整个过程:
Listing 8. Non-exclusive access on a table
Output from dbaccess -e stores_demo <script.sql>:
--------------------------------------------------
begin;
Started transaction.
lock table customer in exclusive mode;
Table locked.
alter table customer add (mycol integer);
242: Could not open database table (informix.customer).
106: ISAM error: non-exclusive access.
出现这种情况有可能是由于有其他的用户在表customer上使用了游标进行数据读取。由于游标并不在具体的数据记录上放置任何锁,否则我们就不可能能够将该表以排他的方式锁住,但是它却能防止其他用户对表的partition信息进行修改。
1、要解决该问题,首先需要找到该表的16进制partnum:
2、如果partnum为0,那么这可能是一个分片表。你需要执行以下命令来找到分片表的partnum:
3、用找到的partnum结合onstat命令搜索当前打开表的信息:
4、从rstcb字段,它表明了相关会话线程的内存地址信息,据此信息使用onstat -u命令进行搜索:
在确定与此相关的会话以后,你可以通过onmode -z <sessid>终止相关的会话。
如果你使用IDS 7.31.xD5, 9.40 or 10,你可以使用环境变量IFX_DIRTY_WAIT。该环境变量可以被设置在服务器端或客户端。该变量指定了在执行DDL命令时将等待数据库完成修改操作所需的时间。如果指定的时间超时,数据库将返回与没有设置该变量时同样的错误。
总结:
在一个拥有众多并发事物正在运行的环境下,实时分析锁冲突的情况是一个比较艰巨的任务。但是通过本文对数据库锁机制的介绍,将有助于大家在分析锁冲突的时候缩短所需时间。
下载lockwt工具:http://www.herber-consulting.de/cgi-bin/MainDriver.pl?action=IfmxUtil
本文作者:Eric Herber
http://www-900.ibm.com/cn/support/viewdoc/detail?DocId=2897869A07000
http://www-900.ibm.com/cn/support/viewdoc/detail?DocId=2897869A08000
本日志由 flyinweb 于 2009-08-14 15:00:55 发表,目前已经被浏览 4332 次,评论 0 次;
引用通告:http://www.517sou.net/Article/183/Trackback.ashx
It is quite useful and interesting too.
VIRT 的上限是64G,也就是36位, cat /proc/cpuinfo的结果是:addre
昨天要准备用线程重写webbench,试验了下Fedora Linux 2.6.35.14
不明白您的具体的意思是什么?
已经发送到你QQ邮箱
http://www.2mysite.net/scriptencoder/screnc.asp 站长你好,看
你好,我发现一个问题,就是从mysqld2同步过来的数据,在mysqld1的
晕,我说是怎么回事情,原来我和你一样,忘记设置了活动分区