1. 索引分类
索引是一种有序的存储结构,也是一项极为重要的SQL优化手段,可以提高数据检索的速度。通过在表中的一个或多个列上创建索引,很多SQL语句的执行效率可以得到极大的提高。
Btree索引
Btree是索引是最常见的索引类型,也是KingbaseES的默认索引,采用B+树(N叉排序树)实现,由于树状结构每一层节点都有序列,因此非常适合用来做范围查询和优化排序操作。Btree索引支持的操作符有 >,<,>=,<=,=,IN,LIKE等 ,同时,优化器也会优先选择Btree来对ORDER BY、MIN、MAX、MERGE JOIN进行有序操作。
精确匹配为类似:id = 1005,name =‘abc’之类的条件。而范围匹配则为类似:id < 1005,id > 10 and id <= 100等条件。
如果SQL语句仅访问被索引的列,则整个查询只需要通过索引即可完成,无需查找表数据。如果该语句还需要访问索引列以外的其他列数据,则会使用索引指向的tid来查找表中的行数据。
Hash索引
Hash索引查询效率比Btree高得多,相比Btree索引需要从根节点查询到叶子节点,且需要多次IO及CPU操作,Hash索引因其索引结构的特殊性,理想情况下仅需一次检索即可定位到数据。
与此同时,Hash也存在一定的局限性,只适合做 等值查询 。由于Hash索引是通过比较哈希值来做定位,因此当查询列上有较大比例的重复值时,会带来严重的哈希冲突,进而影响查询速度,这种情况下就不建议使用Hash索引。
Bitmap索引(执行节点)
Bitmap索引是用一个位图来存放记录索引信息的结构。它用一个bit位来表示某个属性对应的值是否存在,存在为1,不存在为0,比较适合那种值比较单一(比如:性别字段)的情况。Bitmap索引有很大的压缩比空间,比较适合OLAP应用。
Kingbase在为where x=33 or x=44 or x= 55类似的查询创建查询计划的时候,将where过滤分解成3个独立的基于x字段索引的扫描,每个扫描使用一个查询子句,之后再将这些扫描结果OR在一起并生成最终的结果。另外一个例子是,如果我们在x和y上分别存在独立的索引,那么一个类似WHERE x = 5 AND y = 6的查询,就会分别基于这两个字段的索引进行扫描,之后再将各自扫描的结果进行AND操作并生成最终的结果行。
Bitmap索引主要适用于 当表具有很多属性且查询可能会涉及其中任意组合时的情况 。
GIN索引
GIN是通用倒排序索引(Generalized Inverted Index),它是一个存储对(Key,postion list)的集合,其中Key是一个键值,而postion list是包含Key的位置值。比如('Tom','10:25 14:3 29:5')就表示关键字'Tom'在这些位置(元组TID)上存在。当我们用关键字'Tom'去查询的时候,能一下就定位到包含关键字的元组有这三个。
‘Tom’-> TID5->TID10->TID100
‘Ada’-> TID5->TID99
…
‘Bob’-> TID99->TID110->TID200
给定查询的关键字‘Tom’,立即就能知道包含该关键字的元组在哪里能找到。
通用倒排序索引(GIN)主要适用于 包含多个组合值的查询,如数组、全文检索 等。
GiST索引
GiST是通用的搜索树(Generalized Search Tree)。它是一种平衡树结构的访问方法,在系统中作为一个基本模版,可以使用它实现任意索引模式。B-trees,R-trees和许多其它的索引模式都可以用GiST实现。
GiST索引适用于多维数据类型和集合数据类型,和Btree索引类似,同样适用于其他的数据类型。GiST可以用来做 位置搜索,如包含、相交、左边、右边 等。
SP-GiST索引
SP-GiST是“space-partitioned GiST”的缩写,即空间分区GiST索引。它主要是通过一些新的索引算法提高GiST索引在某个情况下的性能。它与GiST索引一样,是一个通用的索引框架,基于此框架可以开发出自定义的空间分区索引。
要实现一个自定义的SP-Gist索引,需要实现5个自定义函数:
- Config:返回索引实现中的一些静态信息
- Choose:选择如何把新的值插入到索引内部tuple中
- Picksplit:决定如何在一些叶子tuple上创建一个新的内部tuple
- Inner_consistent:在树的搜索过程中返回一系列树杈上的节点
- Leaf_consistent:返回叶子节点是否满足查询
SP-GiST适用于 空间可以递归分割成不相交区域的结构,包括四叉树、k-D树和基数树。
BRIN索引
BRIN索引是块范围索引的简称,由于其存储了表的连续数据块区间以及对应的数据取值范围,因此BRIN索引的体积和维护代价相比其他索引低很多。
BRIN索引适用于 存储流式的数据日志 。例如:按照时间插入的数据。由于数据是按照时间插入,因此数据块上记录的范围信息很少会出现交叉情况,索引过滤后需要比较的数据块也会少很多;反之,如果数据交叉严重,通过索引无法过滤掉任何一个数据块时,操作起来会比全表扫描更加耗时。
2. 索引使用技巧
使用表达式索引
KingbaseES支持创建基于标量表达式的索引。
最常见的就是创建基于函数的索引。比如在做大小写无关的比较时,经常使用lower(name) 或者upper(name) 函数。但是因为用了函数,无法利用字段name上的普通索引,所以此时需要一个函数索引:
create table t1(name text); create index idx_t1 on t1(upper(name)); explain select * from t1 where upper(name) = 'ada'; QUERY PLAN ------------------------------------------------------------- Bitmap Heap Scan on t1 (cost=4.21..14.37 rows=7 width=32) Recheck Cond: (upper(name) = 'ada'::text) -> Bitmap Index Scan on idx_t1 (cost=0.00..4.21 rows=7 width=0) Index Cond: (upper(name) = 'ada'::text) (4 行记录) |
表达式索引里面存储的值是表达式的值,所以它并不是在进行索引查找时去计算表达式的,而是在插入数据行或者更新数据行时进行计算的。因此在插入或者更新数据时,函数索引会慢一些。
使用局部索引
局部索引(partial index) 是建立在一个表的子集上的索引,而该子集是由一个条件表达式定义的(叫做局部索引的谓词)。该索引只包含表中那些满足这个谓词的行。
由于不是在所有的情况下都需要更新索引,只有新增的符合局部索引条件表达式的数据才会写入到索引里,因此局部索引会提高数据插入和数据更新的效率,减少了索引的维护成本。又因为局部索引比普通索引要小,因此可以更好的提高确实需要索引部分的查询效率。
比如,t1里面有100万行记录,SQL应用进程查询的是 id < 500的那批记录,则可以为它建立局部索引:
create table t1(id int); create index idx_t1 on t1(id) where id < 500; explain select * from t1 where id < 400; QUERY PLAN ------------------------------------------- Bitmap Heap Scan on t1 (cost=4.35..24.97 rows=850 width=4) Recheck Cond: (id < 400) -> Bitmap Index Scan on idx_t1 (cost=0.00..4.13 rows=850 width=0) Index Cond: (id < 400) (4 行记录) explain select * from t1 where id > 400; QUERY PLAN ----------------------------------------------------- Seq Scan on t1 (cost=0.00..41.88 rows=850 width=4) Filter: (id > 400) (2 行记录) |
可以看出,符合索引条件范围的查询,能够使用创建的局部索引,不符合条件的则不能。
KingbaseES支持带任意谓词的局部索引,只要涉及被索引的表的字段就可以。不过谓词必须和那些希望从该索引中获益的查询中的where条件相匹配。准确的说只有在系统识别出该查询的where条件简单地包含了该索引的谓词时,此局部索引才能用于该查询。KingbaseES还不可以完全识别形式不同但数学上相等的谓词,但可以识别简单的不相等的包含,谓词条件必须准确匹配查询的where条件,不然系统将无法识别该索引是否可用。
由于条件匹配发生在执行计划的规划期间而不是运行期间,因此带绑定变量的条件不能使用局部索引。
使用联合索引
联合索引是在建立在某个关系表上多列的索引,也叫复合索引。创建联合索引时,应该将最常被访问的列放在索引列表前面。当where子句中引用了联合索引中的所有列,或者前导列,联合索引可以加快检索速度。
create table student(id int, name text, school text); create index idx_student on student(id, name, school); |
当查询条件为:1)id;2)id和name;3)id和school;4)id、name和school时,都可以使用idx_student联合索引。
当查询条件不包括id时,则无法使用联合索引。
使用索引提升Like模式匹配性能
KingbaseES产品支持基于Like表达式的通配符模式匹配,通过合理的应用索引,可以提高查询性能。
普通的btree索引能够在2种情况下支持like操作符使用索引
- 精确匹配型like
- 索引字段为“C”collate
- like const表达式
- like var 表达式
- 限制:
Btree索引应用于like模糊查询时,只能做前匹配(a like‘abc%’)或者精确匹配(a like ‘abc’),不能做后匹配(a like ‘%abc’)和中间匹配(a like ‘%abc%’),这是由btree索引只能做大于、大于等于、等于、小于、小于等于等操作决定的。
删除不必要的索引
通过查看系统表sys_stat_user_indexes,来观察哪些索引表从来没有被使用。对于长期不使用的索引可以删除它们,以便减少磁盘占用空间和维护索引的代价。
定期vacuum和重建索引
KingbaseES数据库执行delete操作后,表中的记录只是被标示为删除状态,并没有释放空间,在以后的update或insert操作中该部分的空间不能够被重用。可以利用数据库磁盘空间工具VACUUM,其作用是删除那些已经标示为删除的数据并释放空间。但vacuum工具不能够对相应的索引进行清理,需要手动去重建索引。
另外,对于Btree索引,只有那些已经完全清空的索引页才会得到重复使用,对于那些仅部分空间可用的索引页将不会得到重用,如果一个页面中大多数索引键值都被删除,只留下很少的一部分,那么该页将不会被释放并重用。在这种极端的情况下,由于每个索引页面的利用率极低,一旦数据量显著增加,将会导致索引文件变得极为庞大,不仅降低了查询效率,而且还存在整个磁盘空间被完全填满的危险。对于重建后的索引还存在另外一个性能上的优势,因为在新建立的索引上,逻辑上相互连接的页面在物理上往往也是连在一起的,这样可以提高磁盘页面被连续读取的几率,从而提高整个操作的IO效率。
可以在以下几个级别重建索引:
- 数据库: reindex database d1;
- 表:reindex table t1;
- 单个索引:reindex index idx1;
重置索引后,需要执行 ANALYZE命令重新分析一下表。
其他建议
索引的使用通常能够提高select,update以及delete语句的性能,但会降低insert语句的性能,因此索引并非是越多越好,使用索引应该遵循以下原则:
- 仅当要通过索引访问表中很少的一部分记录(1%~20%)
- 更新较少的表可以考虑使用覆盖索引
覆盖索引扫描,允许直接从索引得到元组,覆盖索引扫描要求查询中的某个表所需要的数据均可以从这个表上的同一个索引的索引页面中获得。
使用覆盖索引之前,需要先对表进行 VACUUM 才能使覆盖索引被优化器使用。对于更新频繁的表,需要多次 VACUUM。
不合适建索引的场合:
- 索引列上有函数(确定性的函数可以创建函数索引)
- 索引列选择率差,不如全表扫描
- 不要索引常用的小型表,其维护代价有时会高于其收益
- 不要索引大型字段(有很多字符),这样作会让索引占用太多的存储空间




