列举GaussDB(DWS)常见的查询时索引失效场景
摘要:使用GaussDB(DWS)时,有时为了加快查询速度,需要对表建立索引。有时我们会遇到明明建立了索引,查询计划中却发现索引没有被使用的情况。本文将列举几种常见的场景和优化方法。
本文分享自华为云社区《GaussDB(DWS)之查询时索引失效原因》,作者: 飞不起来的小松鼠 。
使用GaussDB(DWS)时,有时为了加快查询速度,需要对表建立索引。有时我们会遇到明明建立了索引,查询计划中却发现索引没有被使用的情况。本文将列举几种常见的场景和优化方法。
1. 返回结果集很大
以行存表的Seq Scan和Index Scan为例:
Seq Scan:就是按照表的记录的排列顺序从头到尾依次检索扫描,每次扫描要取到所有的记录。这也是最简单最基础的扫表方式,扫描的代价比较大;
Index Scan:对于给定的查询,先扫描一遍索引,从索引中找到符合要求的记录的位置(指针),再定位到表中具体的Page去取,即先走索引,再读表数据;
因此,根据两种扫描方式的特点可以看出,大多数情况下,Index Scan要比Seq Scan快。但是如果获取的结果集占所有数据的比重很大时(超过70%),这时Index Scan 因为要先扫描索引再读表数据反而不如直接全表扫描来的快。
2. 没有analyze
analyze会更新表的统计信息,如果表未做analyze或上次做完analyze之后表进行过数据量较大的增删操作,会导致统计信息不准,这时候也可能导致表没有走索引。
优化方法:对表进行analyze更新统计信息即可。
3. 过滤条件使用了函数或隐式类型转化导致没有走索引
如果再过滤条件中使用了计算、函数、隐式类型转化,都可能导致无法选择索引。
示例:create table test(a int, b text, c date); 且在a,b,c三列上都分别创建了索引。
场景1:使用计算
从下面的执行结果可以看到,where a = 101,where a = 102 - 1都能使用a列上的索引,但是where a + 1 = 102没有走索引。
postgres=# explain verbose select * from test where a + 1 = 102;
QUERY PLAN
-------------------------------------------------------------------
Streaming (type: GATHER) (cost=0.19..18.25 rows=6 width=14)
Output: a, b, c
Node/s: All datanodes
-> Seq Scan on public.test (cost=0.00..12.25 rows=6 width=14)
Output: a, b, c
Distribute Key: a
Filter: ((test.a + 1) = 102)
(7 rows) postgres=#
postgres=# explain verbose select * from test where a = 101;
QUERY PLAN
-------------------------------------------------------------------------------------
Streaming (type: GATHER) (cost=0.06..14.27 rows=1 width=14)
Output: a, b, c
Node/s: datanode1
-> Index Scan using test_a_idx on public.test (cost=0.00..8.27 rows=1 width=14)
Output: a, b, c
Distribute Key: a
Index Cond: (test.a = 101)
(7 rows) postgres=#
postgres=# explain verbose select * from test where a = 102 - 1;
QUERY PLAN
-------------------------------------------------------------------------------------
Streaming (type: GATHER) (cost=0.06..14.27 rows=1 width=14)
Output: a, b, c
Node/s: datanode1
-> Index Scan using test_a_idx on public.test (cost=0.00..8.27 rows=1 width=14)
Output: a, b, c
Distribute Key: a
Index Cond: (test.a = 101)
(7 rows)
优化方式:此类场景的优化方式是尽量使用常量代替表达式,或者常量计算尽量写在等号的右侧。
场景2:使用函数
从下面的执行结果可以看到,在索引列上使用函数也会导致无法选择索引:
postgres=# explain verbose select * from test where to_char(c, 'yyyyMMdd') = to_char(CURRENT_DATE,'yyyyMMdd');
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------------
Streaming (type: GATHER) (cost=0.19..21.00 rows=6 width=14)
Output: a, b, c
Node/s: All datanodes
-> Seq Scan on public.test (cost=0.00..15.00 rows=6 width=14)
Output: a, b, c
Distribute Key: a
Filter: (to_char(test.c, 'yyyyMMdd'::text) = to_char(('2021-03-16'::date)::timestamp with time zone, 'yyyyMMdd'::text))
(7 rows) postgres=#
postgres=# explain verbose select * from test where c = current_date;
QUERY PLAN
-------------------------------------------------------------------------------------
Streaming (type: GATHER) (cost=0.06..14.27 rows=1 width=14)
Output: a, b, c
Node/s: All datanodes
-> Index Scan using test_c_idx on public.test (cost=0.00..8.27 rows=1 width=14)
Output: a, b, c
Distribute Key: a
Index Cond: (test.c = '2021-03-16'::date)
(7 rows)
优化方法:尽量减少索引列上没有必要的函数调用。
场景3:隐式类型转化
此类场景是经常遇到的场景,例如b的类型是text类型,过滤条件是where b = 2,在生成计划时,text类型会隐式转化为bigint类型,实际的过滤条件变成where b::bigint = 2,导致b列上的索引失效:
postgres=# explain verbose select * from test where b = 2;
QUERY PLAN
-------------------------------------------------------------------
Streaming (type: GATHER) (cost=0.06..18.25 rows=1 width=14)
Output: a, b, c
Node/s: All datanodes
-> Seq Scan on public.test (cost=0.00..12.25 rows=1 width=14)
Output: a, b, c
Distribute Key: a
Filter: ((test.b)::bigint = 2)
(7 rows) postgres=#
postgres=#
postgres=# explain verbose select * from test where b = '2';
QUERY PLAN
-------------------------------------------------------------------------------------
Streaming (type: GATHER) (cost=0.06..14.27 rows=1 width=14)
Output: a, b, c
Node/s: All datanodes
-> Index Scan using test_b_idx on public.test (cost=0.00..8.27 rows=1 width=14)
Output: a, b, c
Distribute Key: a
Index Cond: (test.b = '2'::text)
(7 rows) postgres=#
优化方法:索引条件上的常量尽可能使用和索引列相同类型的常量,避免发生隐式类型转化。
4. 使用nestloop + indexscan 代替 hashjoin
此类语句的特征是两个表关联的时候,其中一个表上where条件过滤之后的结果集行数很小,同时,最终满足条件的结果集行数也很小。此时,使用nestloop+indexscan的效果往往要由于hashjoin。较优的执行计划如下:
可以看到,第5层的Index Cond: (t1.b = t2.b)已经把join条件下推到了基表扫描上。
postgres=# explain verbose select t1.a,t1.b from t1,t2 where t1.b=t2.b and t2.a=4;
id | operation | E-rows | E-distinct | E-memory | E-width | E-costs
----+--------------------------------------------------+--------+------------+----------+---------+---------
1 | -> Streaming (type: GATHER) | 26 | | | 8 | 17.97
2 | -> Nested Loop (3,5) | 26 | | 1MB | 8 | 11.97
3 | -> Streaming(type: BROADCAST) | 2 | | 2MB | 4 | 2.78
4 | -> Seq Scan on public.t2 | 1 | | 1MB | 4 | 2.62
5 | -> Index Scan using t1_b_idx on public.t1 | 26 | | 1MB | 8 | 9.05
(5 rows) Predicate Information (identified by plan id)
-----------------------------------------------
4 --Seq Scan on public.t2
Filter: (t2.a = 4)
5 --Index Scan using t1_b_idx on public.t1
Index Cond: (t1.b = t2.b)
(4 rows) Targetlist Information (identified by plan id)
------------------------------------------------
1 --Streaming (type: GATHER)
Output: t1.a, t1.b
Node/s: All datanodes
2 --Nested Loop (3,5)
Output: t1.a, t1.b
3 --Streaming(type: BROADCAST)
Output: t2.b
Spawn on: datanode2
Consumer Nodes: All datanodes
4 --Seq Scan on public.t2
Output: t2.b
Distribute Key: t2.a
5 --Index Scan using t1_b_idx on public.t1
Output: t1.a, t1.b
Distribute Key: t1.a
(15 rows) ====== Query Summary =====
---------------------------------
System available mem: 9262694KB
Query Max mem: 9471590KB
Query estimated mem: 5144KB
(3 rows)
如果优化器没有选择这种执行计划,可以通过以下方式优化:
set enable_index_nestloop = on;
set enable_hashjoin = off;
set enable_seqscan = off;
5. 使用hint指定索引时指定的索引方式不对
GaussDB(DWS)的plan hint当前支持指定的Scan方式有三种:tablescan、indexscan和indexonlyscan。
tablescan:全表扫描,比如行存表的Seq Scan,列存表的CStore Scan
indexscan:先扫索引,再根据索引取表记录
indexonlyscan:覆盖索引扫描,所需的返回结果能被所扫描的索引全部覆盖。与index scan相比,index only scan所包含的字段集合,囊括了我们查询语句中的字段,这样,提取出相应的index ,就不必再根据索引取表记录了。
因此,对于需要indexonlyscan的场景,如果hint指定了indexscan,该hint是无法生效的:
postgres=# explain verbose select/*+ indexscan(test)*/ b from test where b = '1';
WARNING: unused hint: IndexScan(test)
QUERY PLAN
--------------------------------------------------------------------
Streaming (type: GATHER) (cost=3.12..16.88 rows=100 width=2)
Output: b
Node/s: All datanodes
-> Seq Scan on public.test (cost=0.00..10.88 rows=100 width=2)
Output: b
Distribute Key: a
Filter: (test.b = '1'::text)
(7 rows) postgres=#
postgres=# explain verbose select/*+ indexonlyscan(test)*/ b from test where b = '1';
QUERY PLAN
--------------------------------------------------------------------------------------------
Streaming (type: GATHER) (cost=3.12..56.51 rows=100 width=2)
Output: b
Node/s: All datanodes
-> Index Only Scan using test_b_idx on public.test (cost=0.00..50.51 rows=100 width=2)
Output: b
Distribute Key: a
Index Cond: (test.b = '1'::text)
(7 rows)
优化方法:使用hint时正确指定indexscan和indexonlyscan。
6. 全文检索gin索引
为了加速文本搜索,进行全文检索时可以创建GIN索引:
create index idxb on test using gin(to_tsvector('english',b));
创建索引时,必须使用to_tsvector的两参数版本,并且只有当查询时也使用了两参数版本,且参数值与索引中相同时,才会使用该索引:
postgres=# explain verbose select * from test where to_tsvector(b) @@ to_tsquery('cat') order by 1;
QUERY PLAN
--------------------------------------------------------------------------
Streaming (type: GATHER) (cost=22.23..27.87 rows=12 width=14)
Output: a, b, c
Merge Sort Key: test.a
Node/s: All datanodes
-> Sort (cost=21.86..21.87 rows=12 width=14)
Output: a, b, c
Sort Key: test.a
-> Seq Scan on public.test (cost=0.00..21.78 rows=11 width=14)
Output: a, b, c
Distribute Key: a
Filter: (to_tsvector(test.b) @@ '''cat'''::tsquery)
(11 rows)
postgres=#
postgres=# explain verbose select * from test where to_tsvector('english',b) @@ to_tsquery('cat') order by 1;
QUERY PLAN
---------------------------------------------------------------------------------------------------
Streaming (type: GATHER) (cost=16.09..22.03 rows=2 width=14)
Output: a, b, c
Merge Sort Key: test.a
Node/s: All datanodes
-> Sort (cost=16.03..16.03 rows=2 width=14)
Output: a, b, c
Sort Key: test.a
-> Bitmap Heap Scan on public.test (cost=12.00..16.02 rows=1 width=14)
Output: a, b, c
Distribute Key: a
Recheck Cond: (to_tsvector('english'::regconfig, test.b) @@ '''cat'''::tsquery)
-> Bitmap Index Scan on idxb (cost=0.00..12.00 rows=1 width=0)
Index Cond: (to_tsvector('english'::regconfig, test.b) @@ '''cat'''::tsquery)
(13 rows)
优化方式:查询时也使用了两参数版本,且保证参数值与索引中相同。
列举GaussDB(DWS)常见的查询时索引失效场景的更多相关文章
- 《MySQL面试小抄》索引失效场景验证
我是肥哥,一名不专业的面试官! 我是囧囧,一名积极找工作的小菜鸟! 囧囧表示:小白面试最怕的就是面试官问的知识点太笼统,自己无法快速定位到关键问题点!!! 本期主要面试考点 面试官考点之什么情况下会索 ...
- Mysql 索引失效场景
例如:一张USER表 有字段属性 name,age 其中name为索引 下面列举几个索引失效的情况 1. select * from USER where name=‘xzz’ or age= ...
- 【php】thinkphp以post方式查询时分页失效的解决方法
好久没有写博客了,最近说实话有点忙,各个项目都需要改bug.昨天晚上一直没有解决的php项目中的bug,就在刚才终于搞定,在这里还需要感谢博客园大神给的帮助! 具体问题描述 最近遇到一个非常棘手的问题 ...
- 十八般武艺玩转GaussDB(DWS)性能调优(三):好味道表定义
摘要:表结构设计是数据库建模的一个关键环节,表定义好坏直接决定了集群的有效容量以及业务查询性能,本文从产品架构.功能实现以及业务特征的角度阐述在GaussDB(DWS)的中表定义时需要关注的一些关键因 ...
- 一文详解GaussDB(DWS) 的并发管控和内存管控
摘要:DWS的负载管理分为两层,第一层为cn的全局并发控制,第二层为资源池级别的并发控制. 本文分享自华为云社区<GaussDB(DWS) 并发管控&内存管控>,作者: fight ...
- Oracle中查询走索引的情况
1.对返回的行无任何限定条件,即没有where子句 2.未对数据表与任何索引主列相对应的行限定条件例如:在City-State-Zip列创建了三列复合索引,那么仅对State列限定条件不能使用这个索引 ...
- MySQL索引失效的几种场景
我们都知道建立索引能够提高查询效率,那么是不是任何情况下都能提高呢,当然不是的的,下面我们就来列举一些常见的索引失效的场景. 借用上一篇文章的dm_person_info表 在card_code列没加 ...
- MySQL性能优化:MySQL中的隐式转换造成的索引失效
数据库优化是一个任重而道远的任务,想要做优化必须深入理解数据库的各种特性.在开发过程中我们经常会遇到一些原因很简单但造成的后果却很严重的疑难杂症,这类问题往往还不容易定位,排查费时费力最后发现是一个很 ...
- MySQL索引失效之隐式转换
常见索引失效: 1. 条件索引字段"不干净":函数操作.运算操作 2. 隐式类型转换:字符串转数值:其他类型转换 3. 隐式字符编码转换:按字符编码数据长度大的方向转换,避免数据截 ...
- MySQL中为避免索引失效所需注意的问题
一.索引介绍 二.索引的优势与劣势 1.优势 类似于书籍的目录索引,提高数据检索的效率,降低数据库的IO成本. 通过索引列对数据进行排序,降低数据排序的成本,降低CPU的消耗. 2.劣势 实际上索引也 ...
随机推荐
- xxl-job安装部署文档
xxl-job安装部署文档 注意:替换yaml文件中的mysql地址 安装方式 kubectl apply -f xxl-job.yaml -n xxxx 安装配置文件 xxl-job.yaml ## ...
- openwrt使用tailscale实现内网穿透
问题 之前一直有电信公网ip,最近发现电信公网ip被撤下来了,打电话再去要发现给的是10开头的ip,电信客服还跟我说10开头就是公网ip,= =,根本就不是,无奈使用zerotier进行打洞,把zer ...
- OA、CRM、SCM、ERP之间的区别和联系是什么?
当然,各个系统之间要集成.集成之后的东西,不叫做ERP.做ERP的人说叫ERP,做PLM的人说叫PLM,卖OA的人更愿意叫OA.其实,那个集成之后的东西,啥也不叫. 英文名 中文名 百科释义 关注 ...
- .Net Core 3.1 服务端配置跨域
一.在Startup.cs 的 ConfigureServices 配置好跨域策略 原文链接:https://www.jianshu.com/p/534b9a6a6ed5 public void Co ...
- [Python急救站课程]绘制蜡笔小新图案
可爱的蜡笔小新想要吗?画起来 import turtle as t '''设置''' t.setup(800, 500) # 创建画布并使其位于屏幕中心 t.pensize(2) # 画笔粗细 t.c ...
- IDEA配置maven引入包时报Unable to import maven project: See logs for details 错误的解决办法
这也是我遇到的问题,在此记录下一,当时百度了一下午试过了各种方法,最后看到了一位大佬的博客解决了这个问题. 所以我也抄一下大佬博客也是记录一下问题的解决过程,以免下次遇到相似问题再浪费不必要的时间 参 ...
- DP:按摩师(3.24leetcode每日打卡)
一个有名的按摩师会收到源源不断的预约请求,每个预约都可以选择接或不接.在每次预约服务之间要有休息时间,因此她不能接受相邻的预约.给定一个预约请求序列,替按摩师找到最优的预约集合(总预约时间最长),返回 ...
- RK3588-MPP解码详解
一. 简介 [RK3588从入门到精通] 专栏总目录 本篇文章进行RK3588-MPP解码的详细解析 二. 环境介绍 硬件环境: ArmSoM-W3 RK3588开发板 软件版本: OS:ArmSoM ...
- 期望最大化(EM)算法:从理论到实战全解析
本文深入探讨了期望最大化(EM)算法的原理.数学基础和应用.通过详尽的定义和具体例子,文章阐释了EM算法在高斯混合模型(GMM)中的应用,并通过Python和PyTorch代码实现进行了实战演示. 关 ...
- 教育管理工具类 APP 在智慧校园中的应用探究?
智慧校园是指利用信息化技术,将学校的管理.教育教学等方面进行整合和优化,实现校园运行的智能化和高效化. 在智慧校园建设中,教育管理工具类APP发挥着重要的作用.本文将详细探究教育管理工具类APP在智慧 ...