改善并发性和锁,让更多用户能够更快地访问数据。
来自 IBM Database Magazine 中文版。
一个很重要但经常被忽视的性能调优步骤是:检查 Informix Dynamic Server (IDS) 如何处理锁和并发性。在由我讲授的高级 Informix 性能调优课程中,调优锁是五个改善性能的重要步骤之一。
锁的数量
当 Informix 启动时,它将读取 ONCONFIG 文件并使用 LOCK 参数创建一个内存结构(我们称之为锁表)来管理锁。在 IDS 11 之前的版本的默认设置中,包含 2,000 个锁,锁的数量非常少。在 IDS 11 中,默认的锁数量为 20,000 个 —— 锁的数量增加了,但对高容量系统而言还是欠缺。
每个打开数据库、表或读取、更新行的用户会话都会在锁表中生成锁。打开数据库会在数据库中生成一个共享锁,以避免其他人删除数据库。打开表将在表中生成一个共享锁,以避免在使用表时更改它。除了 “脏读” 之外,将在每个被读取的行上放置一个共享锁。并且当更新、删除或插入一个行时,将在该行所使用的索引上添加额外的锁。
现在给出一个例子:更新带有 3 个索引的 1,000 个行将生成 1,000 个行锁,3,000 个索引锁,以及 4,002 个数据库和表锁。锁的数量很快就会超过内存中默认的锁表结构。 Informix 能够根据需要动态地增加锁表的容量。然而,为锁表提供的额外空间位于共享内存的另一个位置,这就导致分裂的锁表。如果锁表多次溢出,就会大大降低搜索速度。
为了诊断锁表是否溢出,需要查看 onstat -k 命令的输出。在输出的末尾可以看到锁表的溢出次数。图 1 显示了一个溢出了两次的锁表。最后一行显示当前有 42,239 活动锁,锁的总数为 80,000 。在这个例子中,我将在 ONCONFIG 文件中把 LOCK 参数改为 80,000,这样就不会发生锁表溢出。在我教授的高级性能调优课程中,在执行基准测试时,我们发现在使用默认值的情况下,有时溢出的次数多达 30~40 次。这就解释了为什么修改这个设置能够列入性能调优设置的前五位。
注意:onstat -k 选项将显示所有活动锁,所以显示内容可能很长。如果在 ONCONFIG 文件中定义了大量锁,并有很多用户,那么这个命令会输出数千行内容。
|
锁归属
如何找出哪个用户对某个对象使用了锁?图 1 中的 “所有者” 栏列出了拥有锁的用户在共享内存中的地址。使用 onstat -u 查看所有用户,并通过与 “地址” 栏进行对比识别所有者的用户名。
图 1. onstat -k 命令显示内存中的锁表
|
锁定的表
如何找出哪个对象被锁定了?“ tblsnum ” 栏给出了锁定的表。将这个表与下面的 SQL 语句的输出进行比较,将表的 partnum 转换为十六进制,以找出哪个表被锁定了。
select tabname, hex(partnum) tblsnum from systables where tabid > 99; |
这个 SQL 语句将返回一个表列表及相关的 tblsnum 。图 2 显示的例子告诉您如何识别哪个表被锁定了。
图 2. 识别哪个表被锁定了
tblsnum 100002 有特殊的含义 —— 它表明这是一个数据库锁。每个用户在打开数据库时都将在数据库上创建一个共享锁。图 1 显示了 3 个共享锁。
|
锁级别
Informix 在数据库、表、页、行、字节和索引键这些级别上锁定对象。通过 onstat -k 命令查看表空间、行 ID 和键 / 字节列可以识别锁的级别。表 1 列出了锁级别,以及如何识别它们。
表 1. 锁级别说明
|
锁类型
onstat -k 命令输出的列 “类型”(在以前的发布版中称为 “标志”)描述了有效的锁类型。表 2 列出了锁类型。
表 2. onstat -k 输出中显示的锁类型的说明
并发性:允许更多用户共享数据
并发性主要关于如何让更多用户访问和处理相同的数据,而不通过锁彼此排斥。通过锁排斥所有用户的简单做法就是在排他模式下锁定整个数据库。但这种方法不适合在高容量、多用户的环境中使用。
Informix 提供以下五个级别的并发性,它们是通过 set isolation SQL 命令设置的:
Dirty Read 。这个并发性级别不锁定任何行,并且读取其他用户锁定或正在更改的行。它能够返回可能回滚的未提交数据。对于数据仓库环境或获取数据比读取已提交记录更重要的环境,这个并发性级别非常有用。
Committed Read 。这个级别不锁定任何行,但如果有人执行更新或在行上使用排他锁,它将失败。它仅读取已提交的行。行在读取之后可能还会变更,但它不能有可以被读取的锁。这个级别是使用日志的数据库的默认设置,并且大部分 OLTP 都采用该级别。不过,当一个用户请求被另一个用户锁定的行时,您必须提供错误处理。
Cursor Stability 。这个级别在选择的行上放置一个共享锁,因此当有用户正在读取某行时,其他用户就不能更新它。当获取到另一个行或关闭指针时,这个锁将被释放。
Repeatable Read 。这个级别创建最大数量的锁,因为它为每个读取的行或用户扫描的行放置一个共享锁,以使这些行不能再被更改,重复读取将返回同样的记录和值。在事务提交或回滚之后,就释放这些锁。这个级别是 ANSI 模式的数据库的默认设置。
Last Committed Read 。这个级别是 IDS 11 中的新特性,其工作方式与 Committed Read 非常相似;不过,当锁定以更新某个行时,IDS 将从日志中读取最近提交的记录。只有使用行级锁创建表时这个级别才有效,但它能够大大减少锁错误,并返回最近的有效数据。
|
性能效果
现在给出一个例子,展示并发性和隔离级别如何影响锁和性能。在图 3 中,用户使用一个更新语句锁定记录。图 4 显示了 3 个 SQL 语句试图读取那个被锁定的行时得到的结果。第一个语句使用 Committed Read,但失败了;第二个语句在更改数据时获取它。不过,在释放锁之前,该数据将再次回滚或变更。最后一个语句在该行最后一次提交时读取它,它不会放置任何锁,并且获取到有效数据。
图 3. 锁定列
图 4. 并发性和锁的影响
|
以更快的速度读取更多数据
看一下您的系统使用了多少个锁,并了解新的隔离级别 Last Committed Read 。通过锁调优能够提高用户访问数据的速度;通过选择正确的隔离设置可以让更多用户访问数据。