PostgreSQL官方文档对索引的定义,非常简单明了:“索引是提高数据库性能的常见方式。索引使得数据库服务器能够比没有索引时更快地查找和检索特定行。但索引也会增加整个数据库系统的开销,因此应该合理使用。

        这个定义中的关键是最后一个词“合理”。索引是有益的,它们可以提高查询的性能。但这并不意味着我们要为每个查询和每个列创建索引。需要记住的是,虽然索引确实提高了性能,但它们需要维护,这也是一种开销。

        在处理PostgreSQL性能时,以下是我发现的一些关于PostgreSQL索引的重要要点,这些要点将帮助设计增强性能的索引,其中性能收益超过维护开销:

1. 在将新的/修改的索引部署到生产环境时,使用CREATE INDEX命令的CONCURRENTLY选项。这将允许数据库上的“写”操作无缝进行。需要密切监视部署过程中的条件,并注意,部署失败可能会导致无效索引,需要手动删除。

2. 在定义中有多个列的索引必须尽量少用。PostgreSQL查询规划器将主要的“过滤条件”作为索引的前导列,因此定义中的其他列虽然可能用于不等式比较或获取数据,但通常更多是维护开销,而不是性能收益。

3. 我们可以在索引定义中使用“WHERE子句”设计部分索引。这样可以节省空间和时间,但需要非常谨慎地使用,只有当我们绝对确定条件与查询直接或算术上相关时,或者索引可以成为维护开销而没有性能收益时。

4. 如果工作负载包含有时仅涉及列x,有时仅涉及列y,有时涉及列x和y的查询,可以选择在x和y上创建两个单独的索引,依靠索引组合来处理使用两个列的查询。这比在上述情况中创建多列索引的方法更好。

5. 我们可以在列的表达式上创建索引,例如(lower(col1));((first_name || ' ' || last_name))等。索引表达式的维护相对较昂贵,因为每次插入或更新行时都必须计算派生表达式。在检索速度比插入和更新速度更重要时,索引表达式很有用。

6. PostgreSQL支持仅使用索引进行扫描,即仅通过索引回答查询而无需随机访问堆,前提是它主要是B-tree索引,并且查询只能引用存储在索引中的列。只有当表的堆页中的显式位图被设置为全部可见时,这才会产生优势。但是,表中有大部分行不变的情况很常见,因此在实践中,这种扫描类型非常有用。

7. 要有效利用仅索引扫描功能,可以选择创建覆盖索引,这是一种专门设计用于包含常用查询所需列的索引。由于查询通常需要检索的列不仅仅是搜索列,PostgreSQL允许您创建一个索引,其中一些列仅是“有效负载”,并且不是搜索键的一部分。可以通过添加包含额外列的INCLUDE子句来实现。

8. 对于添加非键有效负载列到索引中,特别是宽列,要保持保守。如果索引元组超过索引类型允许的最大大小,数据插入将失败。无论如何,非键列会复制索引表中的数据,并使索引的大小增大,从而可能减慢搜索速度。请记住,除非表的更改速度足够慢,以至于索引仅扫描不需要访问堆,否则在索引中包含有效负载列几乎没有意义。如果必须访问堆元组,从那里获取列的值不会增加额外成本。

9. 后缀截断从上层B-Tree级别中删除非键列。作为有效负载列,它们永远不会用于引导索引扫描。当剩余的键列前缀恰好足以描述最低B-Tree级别上的元组时,截断过程还会删除一个或多个尾随键列。在实践中,没有INCLUDE子句的覆盖索引通常会避免存储在上层的有效负载列。然而,明确定义有效负载列作为非键列可靠地保持上层元组的大小。

10. 原则上,表达式索引可以与仅索引扫描一起使用。然而,PostgreSQL的查询规划器在这种情况下目前并不聪明。它只有在查询所需的所有列都可以从索引中获取时,才认为可以通过仅索引扫描来执行查询。例如,对于在f(x)上进行搜索的查询,除了在f(x)的上下文中需要x外,x并不需要,但规划器没有注意到这一点,并得出结论无法进行仅索引扫描。如果仅索引扫描似乎非常值得,可以通过将x作为包含的列添加来解决此问题。部分索引也支持仅索引扫描。

11. 每个索引列只能支持一个排序规则。如果需要多个排序规则,则可能需要多个索引。索引会自动使用底层列的排序规则。

12. 在检查索引使用情况之前,务必先运行ANALYZE命令。该命令收集关于表中值分布的统计信息。这些信息用于估计查询返回的行数,规划器需要根据每个可能的查询计划分配实际的成本。如果没有实际的统计信息,将假定一些默认值,几乎肯定是不准确的。

13. 使用非常小的测试数据集来检查索引使用是致命的。从10万行中选择1000行可能适合使用索引,但选择100行几乎不适合,因为100行可能适应单个磁盘页,而没有任何计划可以超越顺序获取1个磁盘页。

针对上述每个要点评估设计的每个索引,并进行明智的索引设计过程。

另外,我需要指出,在从SQL Server迁移到PostgreSQL时,请不要直接将SQL Server的索引迁移到PostgreSQL。两种软件的索引架构非常不同。因此,在迁移到PostgreSQL时需要重新评估和重新设计索引。这将需要一些工作,但它会值得。


作者:Yasub Jiruwala