因为PostgreSQL的索引种类十分丰富,因此PostgreSQL的索引设计比其他数据库更为复杂。另外由于PostgreSQL的MVCC多版本控制与其他数据库不同,数据修改对索引的影响也比其他数据库大。可能有些DBA遇到过在Oracle等数据库上因为一张表上的索引数量过多而导致的数据表的写入与修改性能很差的故障,这种问题在PostgreSQL上会更为严重。虽然HOT技术的引入让UPDATE操作对索引的影响降低了,不过vacuum是早晚要清理掉过期的行元的,因此PostgreSQL上,索引自动维护的成本会比其他数据库要大。
大多数应用系统在开发时并没有进行过全面的索引设计,一些开发商会在创建表的时候为主键和外键设计了一些索引,并且在测试时发现某些表缺少索引。系统交付上线过程中和运维过程中,DBA会不断的发现系统中的某些SQL性能存在问题,并通过添加索引来解决这个问题,甚至不同的DBA和开发人员都会对类似的SQL做类似的优化工作-添加索引。这种工作模式带来的问题是往往核心业务表上面都有很多索引,其中存在很多类似、重复的索引。以前在做Oracle优化的时候就经常发现存在这样的问题,索引多了以后会带来数据库插入、修改、删除的额外负担,影响这些业务的性能。除了导致写成本增加外,还会导致CBO优化器经常用错索引,导致某些比较敏感的业务模块出现性能问题。
PostgreSQL数据库由于多版本实现方式与vacuum的存在,过多的不合理的索引会带来比Oracle更多系统性能问题,这种情况在并发量较大,负载较高,数据量较大的系统中更容易出现。统筹索引的创建对于任何一种数据库都是十分有效的优化手段,对于PostgreSQL更是如此。
我们该如何统筹创建索引呢?实际上这种统筹设计应该从研发开始,在做数据模型设计的时候,不仅仅要考虑表和字段,更重要的是要考虑数据的访问方式,通过理解数据的访问方式,对表做好索引的设计。除了主键外键索引外,一些和相关数据相关的访问都需要考虑是通过索引还是通过通过表扫描。如果某个访问必须通过表扫描来完成,那么我们要考虑是否可以通过表分区或者历史数据归档来提高该访问的效率。
首先,在每次修改表上的索引的时候,无论是在研发阶段还是在运行维护阶段,都应该首先分析一下这张表上的已经存在的索引,是否能够通过利用原有的索引或者在原有索引基础上做一些改造(添加索引字段,调整字段顺序、添加附加字段等方式)来进行优化,尽可能避免或者减少同一个字段在多个索引中出现的情况。比如我们有些用户访问数据库需要使用(a,b)的复合索引,有些需要使用(a)的单列索引,有些访问需要使用(a,c)的索引,那么设计一个(a,b,c)的索引可以满足以上三种访问需求,就没必要创建三个索引了。
其次,索引优化的最好做法是定期进行索引统筹审计,将某些关键业务表或者存在严重性能问题的SQL涉及的表的相关SQL尽可能全面的找出来,分析其断语与谓词,分析其数据扫描的模式,然后统一进行索引设计,通过统筹考虑,用最少数量的而索引来解决这些SQL存在的性能问题。
再其次,尽可能避免大量创建单列索引,合理设计复合索引。复合索引不仅仅能够让一些SQL的访问效率进一步提升,另外也可以为多种SQL提升访问效率。PostgreSQL支持SKIP SCAN的索引访问方式,哪怕索引字段不是第一个字段,CBO优化器也可以启用SKIP SCAN,利用这个索引为某条SQL提升扫描效率。
最后如果某些SQL的执行计划不尽如人意,发现这些SQL涉及的字段在索引中不是第一个字段,使用了INDEX SKIP SCAN的索引扫描方式。那么除非该SQL的性能确实已经无法忍受了,添加一个该字段开头的索引确实有效,这种情况才去添加索引。否则可以考虑忍受这种不是最优的索引优化模式,不要轻易添加新索引。