一条 SQL 变慢之后,给过滤字段加索引是很自然的反应。麻烦在于,大表上的索引不是随手试一下就没有代价:构建需要时间和资源,留下来还会增加存储与写入维护成本。如果只是想知道优化器是否愿意使用这个索引,完整建一遍再删除,多少有些笨重。
HypoPG 是 PostgreSQL 的假想索引扩展,允许在不创建真实索引文件的情况下,观察优化器会如何改变执行计划。11 月 1 日发布的 1.4.5 修正了索引估算函数中的长期问题,以及可能引起崩溃的关系标识处理问题。这次更新没有增加一个漂亮界面,却直接关系到这项工具能否可靠地承担“先试算”的工作。
理解它的关键,是把“执行计划”与“实际执行”分开。数据库收到查询后,会根据统计信息、索引结构和成本模型选择访问路径。HypoPG 提供一份虚拟的索引描述,让优化器在规划时考虑它。这个索引并没有保存真实数据,也不能拿来读取记录,但可以回答一个非常实用的问题:假如这个结构存在,计划是否会改变?
这也解释了它为什么适合调优的前半程。面对几个可能的索引方案,先看哪些方案根本不被采用,哪些方案能把全表扫描变成索引扫描,再决定值得投入资源验证的候选。它省掉的是一部分盲目的建索引试验,并没有替代后面的性能测试。
官方 README 给出的示例足够小,可以在已经安装 HypoPG 扩展文件的测试数据库中复现。下面这些 SQL 要放在同一个数据库连接里执行;示例会创建一张测试表,应该使用没有同名表的测试环境:
CREATE EXTENSION hypopg;
CREATE TABLE hypo AS
SELECT id, 'line ' || id AS val
FROM generate_series(1,10000) id;
EXPLAIN SELECT * FROM hypo WHERE id = 1;
SELECT * FROM hypopg_create_index('CREATE INDEX ON hypo (id)');
EXPLAIN SELECT * FROM hypo WHERE id = 1;
SELECT hypopg_reset();
第一次 EXPLAIN 建立没有候选索引的参照,随后把普通的建索引语句作为字符串交给 hypopg_create_index,再看同一条查询的计划。README 展示的例子中,访问方式从顺序扫描变成了索引扫描。这里最值得观察的是计划结构,而不是机械地对照某个成本数字;数据库版本和统计信息不同,估算结果也可能变化。
假想索引只存在于当前连接对应的后端中,其他连接进行计划分析时不会自动看到它。这是一个很实际的设计:同一个库里可以研究某条查询,不必把候选结构广播给所有会话。反过来,通过会切换后端连接的工具执行这组语句时,也需要确认创建和查看计划落在同一个会话里。
还有一个容易踩的坑:这里应该用不带 ANALYZE 的 EXPLAIN。EXPLAIN ANALYZE 会真的执行查询,而虚拟索引没有真实数据可供访问,因此不会用它执行。看到预测成本下降,不能把它写进报告当成已经测出的耗时提升;冷热缓存、并发、磁盘以及数据分布,仍然要靠实际验证。
我会把 HypoPG 放在“筛选方案”的位置。先缩小候选范围,再在合适的测试环境创建真实索引,确认收益是否覆盖存储和写入成本。对数据量较大、索引方案较多的团队,这个顺序比边猜边建更容易控制试错成本。对只需要查看慢查询的人,它也不是完整的监控平台,不负责自动发现所有瓶颈。
用完之后,hypopg_reset() 可以清掉当前会话的假想索引,关闭连接也会清理它们。真正好的数据库工具,有时就是把一个昂贵的实验拆成一次便宜的判断。
项目地址:https://github.com/HypoPG/hypopg
|