IBAnalyst:技巧与诀窍
本文最初写于2012年,适用于版本1.0 - 2.5,在3.0-5.0版本中有许多变化,无法反映。请阅读文档或联系我们获取支持:[email protected]。
一些在IBAnalyst建议和/或帮助中未解答的问题:
1. 如何重建PRIMARY、FOREIGN或UNIQUE约束上的索引?
答:适用于Firebird版本1.0-2.5。 是的,你不能在约束索引上使用ALTER INDEX xxx INACTIVE/ACTIVE。如果你看到该约束上的索引深度或碎片化,可以使用特殊技巧(gbak在恢复时使用的):
RDB$INDICES有RDB$INDEX_INACTIVE标志,如果索引是活动的(在CREATE INDEX或ALTER INDEX ACTIVE之后),该值为null或0。1表示索引不活动(在ALTER INDEX INACTIVE之后)。但还有值3用于表示约束上的不活动索引。因此,你可以为该索引设置RDB$INDEX_INACTIVE=3,提交,然后将值恢复为0并再次提交–索引将被重建。
对于Firebird 3.0-5.0 - 只需执行 ALTER INDEX indexname ACTIVE
2. 我使用了所有IBAnalyst建议,但这无法帮助加速查询。
答:这是另一个问题,IBAnalyst无法帮助。这里可能有2个原因:
-
索引的统计信息已过时。你可以通过命令SET STATISTICS INDEX xxx刷新索引统计信息(更多详情见 http://www.ibase.ru/proc_selectivity/)。
-
查询中使用的某些条件根本没有合适的索引。
-
查询非常复杂,或优化器无法优化查询,因此需要重构查询。
-
在某些情况下,你会在恢复后立即看到“碎片化表”。
通常Firebird和InterBase(不带参数-use_all_space)在数据页上保留约25%的空间用于将来的插入、更新或删除(以放置记录版本)。但是,对于任何数据库页面大小(1、2、4或8k),对于记录大小较小的表(约12-20字节,例如,有2个整数字段的表平均记录大小=12字节),你会看到约50%的碎片化。
这是正常的,将其视为某种神奇的服务器数字(或行为)。
因此,如果你有这样的记录较小的表,你可以:
a) 忽略这些表的“碎片化”警告
b) 例如,在IBAnalyst选项对话框中将“碎片化%”降低到45%。
4. 不应更新的表的记录版本
如果你看到不应更新的表(例如,带有事件日志的表)上有记录版本 - 不用担心,这些版本是由删除生成的。
因此,你将知道表中有多少当前记录,以及有多少记录被删除。
这仅在MaxVer = 1时成立。如果大于1,则该表正被某个应用程序更新。如果你确定该表永远不应被更新,最好设置一个带有异常的“before update”触发器,以找出是哪个应用程序进行了更新。
5. Blob可能导致表碎片化。
引擎以3种不同方式存储blob:
-
如果blob内容适合数据页(有足够的空闲空间),它将存储在该数据页上其记录(或版本)附近。
-
如果blob内容不适合数据页,它将存储在单独的页面上。
-
如果在情况2中blob无法容纳在一个数据页上,将创建指针页以指向相应的blob页。
情况1取决于存储的blob大小和数据库页面大小。例如,如果你有4K的页面大小和平均大小约5K的blob,它们将不会存储在数据页上,而是存储在额外的blob页上。
但如果你备份数据库并以8K页面大小恢复,blob将适合数据页,它们将与记录一起存储,导致高记录碎片化。
IBAnalyst将这些表标记为Pale(记录列),提示显示该表的估计记录数(基于数据页数)和实际平均填充值(%)。
如果你的查询从该表读取除blob外的任何字段,自然扫描、连接或聚合将运行得非常慢。
避免它的唯一解决方案:创建附加表(与原始表1-1关联)并将所有平均大小小于页面大小的blob列移动到该表。
在这种情况下,不要尝试使用更大的页面大小进行备份/恢复!这将导致当前页面大小下无法容纳在数据页上的blob,在恢复时使用更大的页面大小将被放置在数据页上。因此,你的带blob的表将比之前更加碎片化。
也不建议使用较小的页面大小恢复,因为这可能降低索引和非blob表的性能。
你也不应尝试将blob字段更改为varchar字段 - varchar字段始终作为记录的一部分存储,因此如果记录不适合数据页,它可能有2个或更多片段(被放置在2个或更多数据页上)。
附注:IBAnalyst可能“错误地”报告这些表,例如,表有带数据的blob字段,但它们已从表结构中删除。不幸的是,该警告没有可配置选项,因为我们完全根据服务器报告的数据(统计信息)来计算。
6. VerLen和RecLength的关系
a) VerLen >= RecLength的90%:你在Version列中看到的版本主要是记录删除。删除的记录越多,RecLength将越小(最多到0字节)。此外,如果你用比原始记录中存储的更大的字符串数据更新表,VerLen可能大于RecLen。
b) VerLen <= RecLength的80%:版本主要是记录更新。
我们无法更精确地区分这些情况,因为统计信息显示的是整个表的平均记录和版本大小,而并发事务的可见版本数可能不同。
7. 为什么IBAnalyst将某些索引称为“坏”索引?
选择性值低于0.01的索引在IBAnalyst中被标记为“坏”(参见索引视图帮助)。有几个原因将特定索引称为“坏”:
-
该索引的选择性低于0.01。理论上优化器不应使用该索引,但如果没有其他索引存在(至少用于where、order by或join子句),它会使用。
-
这样的索引导致非常慢的垃圾回收。此问题在InterBase 7.1/7.5中不存在,并将在Firebird 2.0中修复。
-
该索引使恢复过程非常慢,并且创建(create/alter index active)非常慢。这是因为一个索引键的记录号链很大。
-
如果该索引在where子句中使用,内存使用将取决于搜索的值(位掩码大小)。由于记录链可能很大(大量键重复),内存消耗也将很大。
-
如果该索引在“order by”中使用,并且大量重复主要在较低的键值中(取决于索引排序顺序),将会有大量索引页读取,从而减慢查询速度。
这就是为什么IBAnalyst不能忽略此类索引的存在。
索引的最坏情况是当它有Uniques列=1时,即索引列的所有值都相同。这些索引在Summary页面的“Useless indices”中列出。
当然,对于你的应用程序,这样的索引可能是“好”的。例如,如果记录在某个列中有“archive”标志,并且你的应用程序仅搜索该列上当前(非归档)数据的索引。因此,由你决定我们称该索引为“坏”是否正确。
8. 如果“坏”索引是由外键约束创建的怎么办?
嗯,上一段表明最好删除“坏”索引(如果你不使用它来搜索重复项比其他键少的键)。但是,如果这样的索引是由外键创建的,你只能通过删除外键来删除它。删除外键将禁用关系检查约束,这可能是不可接受的。
你可以用触发器替换外键,但有一些限制。外键使用索引控制记录关系,索引“看到”所有记录的所有键,与事务状态无关。但触发器仅在客户端的事务上下文中工作。因此,用触发器替换外键时,你必须确保:
-
记录不会从主表中删除,或以“快照表保留”模式删除
-
主表中主键使用的列将永远不会被修改。您可以通过 before update 触发器来限制这一点。
如果您能维持这些条件,就可以删除特定的外键。当然,不要在该列上手动创建索引。
9. 为什么在数据版本百分比行中只有 12 兆字节的数据,但我的数据库却有 140 兆字节?
-
IBAnalyst 在这里显示的是“纯”数据量,不包含其他数据库结构(索引、元数据等)以及页面碎片。
-
在恢复之后,InterBase 和 Firebird 会在数据页中留下一些空闲空间(15-25%),以便将来更快地进行更新和删除操作。
-
存在特定的服务器行为,当表记录大小较小(大约 11-22 字节)时,服务器会留下约 50% 碎片化的数据页。
10. 在频繁更新的情况下如何提高优化器性能
索引统计信息存储在 RDB$INDICES.RDB$STATISTICS 列中,并通过 3 种方式更新:
-
SET STATISTICS INDEX
-
ALTER INDEX ACTIVE,或 CREATE INDEX …
-
恢复过程(所有索引都会被重建,同时执行“ALTER INDEX ACTIVE”)
优化器使用这些统计信息来准备查询。通过统计值,优化器可以判断索引对于检索记录是“足够好”还是“没有用”。
如果统计信息长时间未更新,优化器可能会生成糟糕的执行计划,因为现有的统计值与实际状况不符,因为表数据可能发生了显著变化(例如,记录数量增加了 5-10 倍,或者相反,所有记录都被删除了)。
您可以用针对特定查询的显式 PLAN 来替换糟糕的自动查询计划,但这不是一个好方法,因为在计划制定后数据可能会发生显著变化。
另一种(也是正确的)方法是定期对所有索引应用 SET STATISTICS 语句来刷新统计信息。您可以安排运行 SQL 脚本来刷新统计信息,使用 ISQL 或现成的工具 gidx(仅限 Windows)。
如果您有一些定期重新加载不同记录的表,这种方法将无济于事。让我们看一个例子:
- 表 A 每天被加载数据 4-5 次。
- 处理完加载的数据后,表 A 中的所有记录都会被删除。
在这种情况下,我们可以看到表 A 上索引的两个正确统计值–当它加载数据时,以及当它为空时。因此,在已加载的表上重新计算的统计信息在表为空时将毫无用处,反之亦然。
为避免这种情况,您只需要在表 A 填充数据时重新计算其索引统计信息。最好是在对该表运行查询之前进行。
自 1.91 版本起,IBAnalyst 会显示索引统计差异,并允许您在任何时刻重新计算。首先您需要查看表记录信息–这是否是通常的平均记录数。如果是,您可以放心地重新计算索引选择性。如果不是–也许最好不要动索引统计信息,因为它可能导致优化器生成更糟糕的查询计划。