1、索引的类型:
PostgreSQL提供了多种索引类型:B-Tree、Hash、GiST和GIN,因为它们使用了不一样的算法,所以每种索引类型都有其适合的查询类型,缺省时,CREATE INDEX命令将建立B-Tree索引。
1. B-Tree:
CREATE TABLE test1 (
id integer,
content varchar
);
CREATE INDEX test1_id_index ON test1 (id);
B-Tree索引主要用于等于和范围查询,特别是当索引列包含操做符" <、<=、=、>=和>"做为查询条件时,PostgreSQL的查询规划器都会考虑使用B-Tree索引。在使用 BETWEEN、IN、IS NULL和IS NOT NULL的查询中,PostgreSQL也可使用B-Tree索引。然而对于基于模式匹配操做符的查询,如LIKE、ILIKE、~和 ~*,仅当模式存在一个常量,且该常量位于模式字符串的开头时,如col LIKE 'foo%'或col ~ '^foo',索引才会生效,不然将会执行全表扫描,如:col LIKE '%bar'。
2. Hash:
CREATE INDEX name ON table USING hash (column);
散列(Hash)索引只能处理简单的等于比较。当索引列使用等于操做符进行比较时,查询规划器会考虑使用散列索引。
这里须要额外说明的是,PostgreSQL散列索引的性能不比B-Tree索引强,可是散列索引的尺寸和构造时间则更差。另外,因为散列索引操做目前没有记录WAL日志,所以一旦发生了数据库崩溃,咱们将不得不用REINDEX重建散列索引。
3. GiST:
GiST索引不是一种单独的索引类型,而是一种架构,能够在该架构上实现不少不一样的索引策略。从而可使GiST索引根据不一样的索引策略,而使用特定的操做符类型。
4. GIN:
GIN索引是反转索引,它能够处理包含多个键的值(好比数组)。与GiST相似,GIN一样支持用户定义的索引策略,从而可使GIN索引根据不一样的索 引策略,而使用特定的操做符类型。做为示例,PostgreSQL的标准发布中包含了用于一维数组的GIN操做符类型,如:<@、 @>、=、&&等。
2、复合索引:
PostgreSQL中的索引能够定义在数据表的多个字段上,如:
CREATE TABLE test2 (
major int,
minor int,
name varchar
}
CREATE INDEX test2_mm_idx ON test2 (major, minor);
在当前的版本中,只有B-tree、GiST和GIN支持复合索引,其中最多能够声明32个字段。
1. B-Tree类型的复合索引:
在B-Tree类型的复合索引中,该索引字段的任意子集都可用于查询条件,不过,只有当复合索引中的第一个索引字段(最左边)被包含其中时,才能够得到最高效率。
2. GiST类型的复合索引:
在GiST类型的复合索引中,只有当第一个索引字段被包含在查询条件中时,才能决定该查询会扫描多少索引数据,而其余索引字段上的条件只是会限制索引返回的条目。假如第一个索引字段上的大多数数据都有相同的键值,那么此时应用GiST索引就会比较低效。
3. GIN类型的复合索引:
与B-Tree和GiST索引不一样的是,GIN复合索引不会受到查询条件中使用了哪些索引字段子集的影响,不管是哪一种组合,都会获得相同的效率。 html
使用复合索引应该谨慎。在大多数状况下,单一字段上的索引就已经足够了,而且还节约时间和空间。除非表的使用模式很是固定,不然超过三个字段的索引几乎没什么用处。
3、组合多个索引:
PostgreSQL能够在查询时组合多个索引(包括同一索引的屡次使用),来处理单个索引扫描不能实现的场合。与此同时,系统还能够在多个索引扫描之 间组成AND和OR的条件。好比,一个相似WHERE x = 42 OR x = 47 OR x = 53 OR x = 99的查询,能够被分解成四个独立的基于x字段索引的扫描,每一个扫描使用一个查询子句,以后再将这些扫描结果OR在一块儿并生成最终的结果。另一个例子 是,若是咱们在x和y上分别存在独立的索引,那么一个相似WHERE x = 5 AND y = 6的查询,就会分别基于这两个字段的索引进行扫描,以后再将各自扫描的结果进行AND操做并生成最终的结果行。
为了组合多个索引,系统扫描每一个须要的索引,而后在内存里组织一个BITMAP,它将给出索引扫描出的数据在数据表中的物理位置。而后,再根据查询的需 要,把这些位图进行AND或者OR的操做并得出最终的BITMAP。最后,检索数据表并返回数据行。表的数据行是按照物理顺序进行访问的,由于这是位图的 布局,这就意味着任何原来的索引的排序都将消失。若是查询中有ORDER BY子句,那么还将会有一个额外的排序步骤。由于这个缘由,以及每一个额外的索引扫描都会增长额外的时间,这样规划器有时候就会选择使用简单的索引扫描,即 使有多个索引可用也会如此。 算法
4、惟一索引:
目前,只有B-Tree索引能够被声明为惟一索引。
CREATE UNIQUE INDEX name ON table (column [, ...]);
若是索引声明为惟一索引,那么就不容许出现多个索引值相同的行。咱们认为NULL值相互间不相等。
5、表达式索引:
表达式索引主要用于在查询条件中存在基于某个字段的函数或表达式的结果与其余值进行比较的状况,如:
SELECT * FROM test1 WHERE lower(col1) = 'value';
此时,若是咱们仅仅是在col1字段上创建索引,那么该查询在执行时必定不会使用该索引,而是直接进行全表扫描。若是该表的数据量较大,那么执行该查询也将会须要很长时间。解决该问题的办法很是简单,在test1表上创建基于col1字段的表达式索引,如:
CREATE INDEX test1_lower_col1_idx ON test1 (lower(col1));
若是咱们把该索引声明为UNIQUE,那么它会禁止建立那种col1数值只是大小写有区别的数据行,以及col1数值彻底相同的数据行。所以,在表达式上的索引能够用于强制那些没法定义为简单惟一约束的约束。如今让咱们再看一个应用表达式索引的例子。
SELECT * FROM people WHERE (first_name || ' ' || last_name) = 'John Smith';
和上面的例子同样,尽管咱们可能会为first_name和last_name分别建立独立索引,或者是基于这两个字段的复合索引,在执行该查询语句时,这些索引均不会被使用,该查询可以使用的索引只有咱们下面建立的表达式索引。
CREATE INDEX people_names ON people ((first_name || ' ' || last_name));
CREATE INDEX命令的语法一般要求在索引表达式周围书写圆括弧,就像咱们在第二个例子里显示的那样。若是表达式只是一个函数调用,那么能够省略,就像咱们在第一个例子里显示的那样。
从索引维护的角度来看,索引表达式要相对低效一些,由于在插入数据或者更新数据的时候,都必须为该行计算表达式的结果,并将该结果直接存储到索引里。然 而在查询时,PostgreSQL就会把它们看作WHERE idxcol = 'constant',所以搜索的速度等效于基于简单索引的查询。一般而言,咱们只是应该在检索速度比插入和更新速度更重要的场景下使用表达式索引。
6、部分索引:
部分索引(partial index)是创建在一个表的子集上的索引,而该子集是由一个条件表达式定义的(叫作部分索引的谓词)。该索引只包含表中那些知足这个谓词的行。
因为不是在全部的状况下都须要更新索引,所以部分索引会提升数据插入和数据更新的效率。然而又由于部分索引比普通索引要小,所以能够更好的提升确实须要索引部分的查询效率。见如下三个示例:
1. 索引字段和谓词条件字段一致:
CREATE INDEX access_log_client_ip_ix ON access_log(client_ip)
WHERE NOT (client_ip > inet '192.168.100.0' AND client_ip < inet '192.168.100.255');
下面的查询将会用到该部分索引:
SELECT * FROM access_log WHERE url = '/index.html' AND client_ip = inet '212.78.10.32';
下面的查询将不会用该部分索引:
一个不能使用这个索引的查询能够是∶
SELECT * FROM access_log WHERE client_ip = inet '192.168.100.23';
2. 索引字段和谓词条件字段不一致:
PostgreSQL支持带任意谓词的部分索引,惟一的约束是谓词的字段也要来自于一样的数据表。注意,若是你但愿你的查询语句可以用到部分索引,那么 就要求该查询语句的条件部分必须和部分索引的谓词彻底匹配。 准确说,只有在PostgreSQL可以识别出该查询的WHERE条件在数学上涵盖了该索引的谓词时,这个部分索引才能被用于该查询。
CREATE INDEX orders_unbilled_index ON orders(order_nr) WHERE billed is not true;
下面的查询必定会用到该部分索引:
SELECT * FROM orders WHERE billed is not true AND order_nr < 10000;
那么对于以下查询呢?
SELECT * FROM orders WHERE billed is not true AND amount > 5000.00;
这个查询将不像上面那个查询这么高效,毕竟查询的条件语句中没有用到索引字段,然而查询条件"billed is not true"却和部分索引的谓词彻底匹配,所以PostgreSQL将扫描整个索引。这样只有在索引数据相对较少的状况下,该查询才能更有效一些。
下面的查询将不会用到部分索引。
SELECT * FROM orders WHERE order_nr = 3501;
3. 数据表子集的惟一性约束:
CREATE TABLE tests (
subject text,
target text,
success boolean,
...
);
CREATE UNIQUE INDEX tests_success_constraint ON tests(subject, target) WHERE success;
该部分索引将只会对success字段值为true的数据进行惟一性约束。在实际的应用中,若是成功的数据较少,而不成功的数据较多时,该实现方法将会很是高效。
7、检查索引的使用:
见如下四条建议:
1. 老是先运行ANALYZE。
该命令将会收集表中数值分布情况的统计。在估算一个查询返回的行数时须要这个信息,而规划器则须要这个行数以便给每一个可能的查询规划赋予真实的开销值。 若是缺少任何真实的统计信息,那么就会使用一些缺省数值,这样确定是不许确的。所以,若是尚未运行ANALYZE就检查一个索引的使用情况,那将会是一 次失败的检查。
2. 使用真实的数据作实验。
用测试数据填充数据表,那么该表的索引将只会基于测试数据来评估该如何使用索引,而不是对全部的数据都如此使用。好比从100000行中选1000行, 规划器可能会考虑使用索引,那么若是从100行中选1行就很难说也会使用索引了。由于100行的数据极可能是存储在一个磁盘页面中,然而没有任何查询规划 能比经过顺序访问一个磁盘页面更加高效了。与此同时,在模拟测试数据时也要注意,若是这些数据是很是类似的数据、彻底随机的数据,或按照排序顺序插入的数 据,都会令统计信息偏离实际数据应该具备的特征。
3. 若是索引没有获得使用,那么在测试中强制它的使用也许会有些价值。有一些运行时参数能够关闭各类各样的查询规划。
4. 强制使用索引用法将会致使两种可能:一是系统选择是正确的,使用索引实际上并不合适,二是查询计划的开销计算并不能反映现实状况。这样你就应该对使用和不使用索引的查询进行计时,这个时候EXPLAIN ANALYZE命令就颇有用了。 数据库