X
首页 数据库性能优化 监控 Informix Dynamic Server 以获取更高性能(2)
监控 Informix Dynamic Server 以获取更高性能(2)

监控数据库活动


对数据库活动进行监控的目的在于确保每个数据库时刻都将其能力发挥到了极致。这意味着:您必须留意潜在的性能问题,确定其根源并将其消灭在萌芽状态。以下是要留意的几个方面。

表扩展块
扩展块是一块物理上连续的页。然而,如果一个表有多个扩展块,那就不能保证这些扩展块是连续的;扩展块可能会散布在表所驻留的整个 dbspace 上。物理页的连续性对于性能十分重要。当数据页连续时,访问磁盘数据所用的时间就最短,而数据库也能连续地读取行。如果表有太多扩展块,那么那些扩展块极有可能相互交错。这极大地影响了性能,因为当您检索某个表的数据时,磁头需要对属于该表的多个非连续扩展块进行寻道,而不是对具有连续物理页的一个大扩展块进行寻道。这会显著地降低磁盘寻道速度。下面的脚本检测具有多个扩展块的数据库表:

select t.tabname, count(*) num_ext
from sysmaster:sysextents e, airgen:systables t
where e.tabname=t.tabname
and dbsname = "airgen"
and t.tabname not like "sys%"
group by 1
having count(*) > 1
order by 2 desc

输出如下所示:

tabname                       num_ext

nmoattrclassmap                    14
attrclass           

索引唯一性
索引的重复程度很高会严重地影响更新和删除的性能。假定表 customer 的 customer_type 列上有一个索引,而可能的 customer_type 代码只有五种。如果这个表有一百万行,那么可能有 200000 行具有相同的 customer_type 代码。B-树存储键值,其后跟一个指向每个物理行的指针列表。在必须删除或更新任何键值时,问题出现了。IDS 必须找遍所有的重复内容,直到找到要删除或更新的正确键为止!

下面的脚本用来标识重复程度很高的索引:

select tabname, idxname, nrows, nunique
from systables t, sysindexes I
where t.tabid =i.tabid
and t.tabid > 99
and nrows > 0
and nunique > 0

输出如下所示:

tabname              idxname                    nrows     nunique

bsc_dte              bscdte_i                       6           6
omcgttready           231_413                       1           1
systemrelease         451_478                       3           3
neclass               452_479                      31          12
sysrelneclassmap      453_480                      33           3
proxynemgrmap         454_481                       1           1
networkmoclass        455_482                     362         199
nenmoclassmap         456_483                     492          12
attrclass             457_484                    1191         924
nmoattrclassmap       458_485                    2901         199
fanout_comp          fanout_comp_i                915         199
fanout_comp          fanout_comp_i2               915         199
fanout_comp          fanout_comp_i3               915          82
fanout_param         fanout_param_i              2894         196

在理想情况下,nunique 列中所出现的全部值都应该与 nrow 列中的全部值相等,即索引中的每个键都是唯一的。根据行数(上面的 nrows 列)和唯一键数(上面的 nunique 列),我们可以计算每个索引唯一性的百分率:

(nunique/nrows)*100

百分率越高,索引的唯一性就越高。为了避免因索引重复程度很高而引起的性能瓶颈,您可以使用复合索引来替换原来的索引,复合索引结合了重复程度很高的列与唯一性比较高的列。利用上面的示例,您可以将主键列 customer_id 添加到原来的索引,将它变成一个复合列(例如,“create index index_name on customer (customer_type, customer_id)”)。

顺序扫描
对表进行顺序存取有时会降低性能,因为数据库引擎必须扫描整个表以选取满足查询条件的行。如果表很小,比如说几百行,那么顺序存取不会对性能造成什么影响;因为当数据库引擎第一次扫描它时,该表会驻留于内存中,而当数据库引擎下一次扫描它时,可以直接从内存检索该表中的所有数据。这实际上是使用顺序扫描的有效方式。但如果表很大,比如说超过了 100000 行,那么重复的顺序扫描会对性能造成致命的影响。下面的脚本将标识具有多重顺序扫描的表:

select dbsname, tabname, sum(seqscans) tot_scans
from sysptprof
where seqscans > 0
and dbsname not like "sys%"
group by 1,2
order by 3 desc

输出如下所示:

dbsname              tabname                     tot_scans

airgen_10_0          systemrelease                    2352
airgen_10_5_old      systemrelease                    1596
airgen_10_5          systemrelease                    1596
airgen_10_0          fanout_comp                      1587
airgen_10_5_old      sysusers                         1248
airgen_10_0          sysusers                         1241
airgen_10_5          sysusers                         1231
airgen_10_0          join_map                         1036
airgen_10_0          fanout_param                      958
airgen_10_0          func_call                         770
airgen_10_5          nenmoclassmap                     586
airgen_10_5_old      nenmoclassmap                     586

从上面的输出可以看出 airgen_10_0 表的顺序扫描数很高。如果它是一个具有几千甚至几百万行的大表,那么您可能需要考虑向该表添加一些索引,或者考虑使用程序伪指令来强制内部查询优化器为访问该表中的数据选择索引而不是顺序扫描。

监控会话活动


有关会话活动的统计信息在确定潜在的性能问题及故障诊断方面很有用。使用本文前面讨论的监控工具,我们可以收集哪些会话活动统计信息呢?

常规会话统计信息
sysmaster 数据库中的 syssessions 表存储各个会话的常规信息,如登录名、登录时间、会话所登录的主机机器、操作系统的进程标识和当前状态等等。可以使用以下查询来查询该表来获取全部此类信息:

select sid, username, hostname, connected logint_time,
hex(state) s_state
from syssessions

输出类似于:

    sid username   hostname        logint_time s_state

233989 omcadmin   localhost        1041348844 0x00080021
233982 omcadmin   gcsys-e1         1041348608 0x00080001
233981 omcadmin   nysys4-e1        1041348608 0x00080001
233980 omcadmin   nysys5-e1        1041348608 0x00080001
233979 omcadmin   ffsys-e1         1041348608 0x00080001
233973 omcadmin   nysys1           1041348608 0x00080001
233781 wsadmin2   gcmmi            1041346036 0x00080001
233697 omcadmin   localhost        1041344008 0x00080001
233694 wsadmin4   nymmi1           1041343932 0x00080001
233693 wsadmin4   nymmi1           1041343932 0x00080001
230550 omcadmin   nysys1           1041293396 0x00080001
230476 omcadmin   ffsys-e1         1041292665 0x00080001
230421 omcadmin   gcsys-e1         1041292365 0x00080001
230278 omcadmin   nysys5-e1        1041291208 0x00080001

s_tate 是一个指出会话的当前活动状态的十六进制数,请参考 Administrator's Guide 的第 27-38 页,以获取对这其中每个活动状态的详细描述。login_time 是一个指出会话登录时间的整数,使用 C 程序很容易将它转换成常规时间格式。可以向 Informix 技术支持请求以获取该 C 程序。

sysmaster 数据库中的 syssesprof 表提供了各会话的更多详细信息。使用以下查询,您可以更好地理解各个会话是如何与数据库交互的:

select sid, (isreads+bufreads+bufwrites+pagreads+pagwrites) access, locksheld,
seqscans, total_sorts, dsksorts
from syssesprof

输出如下所示:

  Sid    access       locksheld     seqscans   tot_sorts disksorts
233982      246              0          2            0           0
230421     7789             12        456         1000           0
225679     9981            213        669          876           2
247869    10098            440        578           98           2
    78       70             45          6            0           0
   447       46             89          6            0           0

access 字段显示了会话命中数据库的次数。locksheld 显示各会话正使用多少锁。seqscans 表明各会话使用顺序扫描访问数据的次数;如果该数字太高,比如说高于 100000,那么您可能要质疑会话是否曾经使用过索引来检索数据,而且要更仔细地检查其查询执行计划以确定它是否最佳。total_sorts 和 dsksorts 表明各会话使用内存进行排序操作的效率。您可能会使用以下公式来计算各会话使用内存进行排序的百分数:

((total_sorts - dsksorts)/total_sorts)*100

该百分数越高,排序操作效率也就越高。您可以将 syssessions 表与 syssesprof 表连接以进一步确定每个会话的用户名和主机机器名,以便了解哪些地方可能会出现数据库和系统问题。以下查询可以检索所有这类信息:

select username, hostname,
(isreads+bufreads+bufwrites+pagreads+pagwrites) access, locksheld, seqscans,
total_sorts, dsksorts
from syssessions s, syssesprof f
where s.id =f.sid

您也可以将 syssessions 表与 syslocks 表连接以获取关于锁的更多详细信息,如哪个会话当前锁定了哪个数据库中的哪个表,以此来帮助您确定各用户之间潜在的锁冲突:

select owner, username, hostname, dbsname, tabname, type
from syssessions s, syslocks l
where sid  = owner
and tabname not like "sys%"

输出类似于:

Owner username hostname dbsname  tabname  type

1422 wsine  apple         prod        customer        S
1567 jlinder    sys3524         dev            products        S
2237 ejhonson case            prod           orders          X
6679    cjz020          sys4800         dev            shipment        S
889654  jfjianing       omega           test           prices          X
77622   hong            build50         test           items           S

如果在锁使用方面存在某些冲突,例如某个用户需要对已被别的用户锁定的表进行专有访问,那么您可以方便地确定该锁的所有者,并根据用户的优先级发出 onmode -z sid 命令来杀死会话,然后释放该锁;sid 这个编号是从上面输出中的 owner 字段中获取的;请注意,只有用户“Informix”可以执行该命令。

查询统计信息
查询统计信息对于故障诊断和查询优化至关重要。onstat -g sql sid 命令捕获当前会话的查询及相关统计信息;其中 sid 是会话标识,它可以通过硬编码手工插入,也可以通过 UNIX shell 脚本动态插入。例如,如果您想知道某个会话正在执行什么查询,那么您可能要首先使用命令 onstat -g ses 来查明其会话标识,然后将该标识插入上面的命令。例如,如果您想监控的会话标识为 28953,那么您可以使用 onstat -g sql 28953 来捕获其当前查询。

输出如下所示:

Informix Dynamic Server 2000 Version 9.21.UC4     -- On-Line -- Up 38 days
11:26:22 -- 1654784 Kbytes

session                                      #RSAM    total      used     
id       user     tty      pid      hostname threads  memory     memory   
134709   omcadmin 3        29580    localhos 1        65536      61120    

tid      name     rstcb    flags    curstk   status
147311   sqlexec  6511e728 Y--P---  1648     6511e728 cond wait(netnorm)

Memory pools    count 1
name         class addr     totalsize freesize #allocfrag #freefrag
134709       V     669b9020 65536     4416     162        6        

name           free       used           name           free       used     
overhead       0          1648           scb            0          96       
opentable      0          6000           filetable      0          920      
log            0          2152           temprec        0          1608     
keys           0          192            ralloc         0          20480    
gentcb         0          1256           ostcb          0          2520     
sort           0          56             sqscb          0          11960    
sql            0          40             rdahead        0          640      
hashfiletab    0          280            osenv          0          1584     
buft_buffer    0          4272           sqtcb          0          3240     
fragman        0          2176          

Sess  SQL            Current            Iso Lock       SQL  ISAM F.E.
Id    Stmt type      Database           Lvl Mode       ERR  ERR  Vers
134709 SELECT         mso_db             CR  Not Wait   0    0    9.03

Current SQL statement :
  SELECT ne_type, config_set_version FROM ne WHERE ne_inst = 46176 AND
    msospace_id = 1

Last parsed SQL statement :
  SELECT ne_type, config_set_version FROM ne WHERE ne_inst = 46176 AND
    msospace_id = 1

输出的第一部分给出了关于正在执行的查询的一些常规统计信息,如对哪个数据库执行查询、其隔离级别以及锁方式。最有趣的两个字段是 SQL error 和 ISAM error。如果这些字段非零,就表明查询出了一些问题,没有正确地执行。可以使用 Informix finderr 实用程序来查明到底是什么问题,随后在解决了该问题之后,杀死该会话并重新执行该查询。

“Current SQL statement”部分显示了正在执行的查询的完整 SQL 语法。它对于诊断问题查询和查询优化非常有帮助。如果您发现了该查询的一些问题,如响应时间太长,消耗了太多的系统 CPU 或内存,那么您可以按其显示状态为该查询制作一个副本,以便稍后研究和分析之用。您随后可以对 Informix dbaccess 实用程序运行同一个查询,以获取该查询性能方面更多详细的统计信息(如其执行计划和连接策略),从而确定问题的根源。根据研究所收集的统计信息,您可以进一步优化查询以获取更好的性能。“Last parsed SQL statement”部分显示了在内存中已经过解析的查询。由于在内存中已经解析过的查询在大多数情况下都是正在执行的查询,因此这一部分与前一部分在大多数时候都是相同的。

最后的一些技巧


要想在监控方面采取更主动的方式,您可以在 IDS 安装期间修改由 Informix 提供的警报程序。警报程序实际上是一个 UNIX shell 脚本,在发生某些错误时 IDS 会自动调用该脚本。IDS 将所有的实例错误分为五个严重性级别:第一级最低,第五级最高。您可以设置警报程序,使之能够在发生实例断言失败时向 DBA 发送电子邮件,或者向 DBA 的寻呼机发送消息。有关如何修改警报程序和样本程序的详细信息,请参考 Administrator's Guide 及 Performance Guide。

此外,您还应该执行一些必要的维护例程,以确保数据库健康运行。一些基本的例程是:

验证和修复数据及索引完整性
为查询优化器更新内部统计信息
回收无用的共享内存段


结束语


监控 Informix Dynamic Server 的性能是一项正在进行的任务。这项任务的价值并不在于收集统计信息本身,而在于确定和解决潜在的系统和数据库问题。

通过本文中所描述的这些有效的监控实践,我们将能够成功地在最初阶段确定系统和数据库问题,这样我们就能够采取更为主动的方式来进行故障诊断和性能调优。