注册
DM7执行计划优化系列(二)——统计信息

DM7执行计划优化系列(二)——统计信息

Grrr 2021/02/03 3263 7 0
摘要 本系列主要讲解SQL优化过程中关于执行计划的基础知识,本篇文章通过示例讲解了统计信息的概念及收集方式。

上篇DM7执行计划优化系列(一)——执行计划详解讲到执行计划中的估算行数受统计信息的影响。

这里简单介绍一下统计信息,统计信息可以简单理解为将索引(包含原表 ROWID 聚簇索引)的某一列进行统计分析,列出其最大最小值,存在多少个不同值,各个值存在多少个辅助信息。

对于没有统计信息的列,DM7 简单地按照一定比例进行概率过滤
涉及到的 INI 参数为
SEL_RATE_EQU 等值过滤选择率 默认为 0.025
SEL_RATE_SINGLE 一般条件选择率 默认为 0.05

例子:

CREATE TABLE TEST10(ID1 INT,ID2 VARCHAR,ID3 VARCHAR,ID4 VARCHAR); --方便起见,我们插入1W行数据,ID1从1-10000, ID2 为 0a - 4a,ID3全为b,ID为1c - 10000c INSERT INTO TEST10 SELECT LEVEL,LEVEL % 5 || 'A','B',LEVEL || 'C' FROM DUAL CONNECT BY LEVEL <= 10000;
--SEL20 EXPLAIN SELECT * FROM TEST10 WHERE ID1 = 5; 1 #NSET2: [1, 250, 156] 2 #PRJT2: [1, 250, 156]; exp_num(5), is_atom(FALSE) 3 #SLCT2: [1, 250, 156]; TEST10.ID1 = 5 4 #CSCN2: [1, 10000, 156]; INDEX33555457(TEST10)

可以看到 CSCN 涉及 1W 行数据,这个没有问题,但是过滤条件 SLCT 的 CARD 标注为 250 行(#SLCT2: [1, 250, 156]),这个和我们的预期是不一致的,因为不存在统计信息,系统按 10000 * 0.025 直接给出 250 的结果。
如果存在多个等值条件呢?

--SEL21 --我们这里保障列与值类型相同 ID2 VARCHAR = '5' EXPLAIN SELECT * FROM TEST10 WHERE ID1 = 5 AND ID2 = '5'; 1 #NSET2: [1, 6, 156] 2 #PRJT2: [1, 6, 156]; exp_num(5), is_atom(FALSE) 3 #SLCT2: [1, 6, 156]; (TEST10.ID1 = 5 AND TEST10.ID2 = '5') 4 #CSCN2: [1, 10000, 156]; INDEX33555457(TEST10)

SLCT 的 CARD 为6,约等于 10000 * 0.025 * 0.025 = 6.25
可以简单推测出存在多个条件,且不存在统计信息的情况下,CARD 是多个选择率的乘积乘以下层输出行数。

–我们再来看一般条件

--SEL22 EXPLAIN SELECT * FROM TEST10 WHERE ID1 > 5; 1 #NSET2: [1, 500, 156] 2 #PRJT2: [1, 500, 156]; exp_num(5), is_atom(FALSE) 3 #SLCT2: [1, 500, 156]; TEST10.ID1 > 5 4 #CSCN2: [1, 10000, 156]; INDEX33555457(TEST10)

SLCT 输出 CARD 为500,和 INI 默认 SEL_RATE_SINGLE 参数 0.05 一致(10000 * 0.05 = 500)
一般来说,除开等值条件外的所有过滤条件我们都认为是一般条件。

同样的,一般条件和等值条件的组合,在没有统计信息的情况下,最终选择率依然是按乘积计算。

--SEL23 EXPLAIN SELECT * FROM TEST10 WHERE ID1 > 5 AND ID2 = '5'; 1 #NSET2: [1, 12, 156] 2 #PRJT2: [1, 12, 156]; exp_num(5), is_atom(FALSE) 3 #SLCT2: [1, 12, 156]; (TEST10.ID2 = '5' AND TEST10.ID1 > 5) 4 #CSCN2: [1, 10000, 156]; INDEX33555457(TEST10)

SLCT CARD = 12 = 10000 * 0.05 * 0.025 = 12.5

现在我们收集统计信息,推荐收集统计信息的方式有两种

  • 收集单列统计信息
    STAT 100 ON 表(列)

  • 收集 SQL 语句涉及列的统计信息
    CREATE VIEW VA AS SQL语句;
    CALL SP_SQL_STAT_INIT(‘SELECT * FROM VA’)

具体到本文的例子中:

STAT 100 ON TEST10(ID1); STAT 100 ON TEST10(ID2);

收集完毕后,我们再看计划中的CARD值

EXPLAIN SELECT * FROM TEST10 WHERE ID1 = 5; 1 #NSET2: [1, 1, 156] 2 #PRJT2: [1, 1, 156]; exp_num(5), is_atom(FALSE) 3 #SLCT2: [1, 1, 156]; TEST10.ID1 = 5 4 #CSCN2: [1, 10000, 156]; INDEX33555457(TEST10)

单列估算准确,ID1 只存在一个为 5 的行

EXPLAIN SELECT * FROM TEST10 WHERE ID2 = '5'; 1 #NSET2: [1, 1, 156] 2 #PRJT2: [1, 1, 156]; exp_num(5), is_atom(FALSE) 3 #SLCT2: [1, 1, 156]; TEST10.ID2 = '5' 4 #CSCN2: [1, 10000, 156]; INDEX33555457(TEST10)

单列估算准确,CARD 最小为1,ID2 不存在为 5 的行

EXPLAIN SELECT * FROM TEST10 WHERE ID1 = 5 AND ID2 = '5'; 1 #NSET2: [1, 1, 156] 2 #PRJT2: [1, 1, 156]; exp_num(5), is_atom(FALSE) 3 #SLCT2: [1, 1, 156]; (TEST10.ID1 = 5 AND TEST10.ID2 = '5') 4 #CSCN2: [1, 10000, 156]; INDEX33555457(TEST10)

多列估算准确,不存在满足两个条件的行

EXPLAIN SELECT * FROM TEST10 WHERE ID1 > 5; 1 #NSET2: [1, 9995, 156] 2 #PRJT2: [1, 9995, 156]; exp_num(5), is_atom(FALSE) 3 #SLCT2: [1, 9995, 156]; TEST10.ID1 > 5 4 #CSCN2: [1, 10000, 156]; INDEX33555457(TEST10)

单列一般条件估算准确,有 9995 个 ID1 > 5

EXPLAIN SELECT * FROM TEST10 WHERE ID1 > 5 AND ID2 = '5'; 1 #NSET2: [1, 1, 156] 2 #PRJT2: [1, 1, 156]; exp_num(5), is_atom(FALSE) 3 #SLCT2: [1, 1, 156]; (TEST10.ID2 = '5' AND TEST10.ID1 > 5) 4 #CSCN2: [1, 10000, 156]; INDEX33555457(TEST10)

多列混合估算准确,不存在满足条件的行

由此可见,统计信息的收集可以大概率的修正对过滤行数的估算,但是并非所有情况都适用:我们知道,统计信息的收集是基于基础列做统计分析,如果列值上被套有函数,则统计信息会失效。

--SEL24 EXPLAIN SELECT * FROM TEST10 WHERE TRIM(ID1) > 5 ; 1 #NSET2: [1, 500, 156] 2 #PRJT2: [1, 500, 156]; exp_num(5), is_atom(FALSE) 3 #SLCT2: [1, 500, 156]; exp_cast(exp11) > 5 4 #CSCN2: [1, 10000, 156]; INDEX33555457(TEST10)

ID1 的值实际上没有变化,但是 SLCT CARD 不准确

另外需要小心的是隐式转换

-- SEL 25 EXPLAIN SELECT * FROM TEST10 WHERE ID2 > 5; 1 #NSET2: [1, 1, 156] 2 #PRJT2: [1, 1, 156]; exp_num(5), is_atom(FALSE) 3 #SLCT2: [1, 1, 156]; exp_cast(TEST10.ID2) > 5 4 #CSCN2: [1, 10000, 156]; INDEX33555457(TEST10)

这个和下面的等价

EXPLAIN SELECT * FROM TEST10 WHERE CAST(ID2 AS INT) > 5; 1 #NSET2: [1, 1, 156] 2 #PRJT2: [1, 1, 156]; exp_num(5), is_atom(FALSE) 3 #SLCT2: [1, 1, 156]; exp_cast(TEST10.ID2) > 5 4 #CSCN2: [1, 10000, 156]; INDEX33555457(TEST10)

不过目前的版本应该已经把简单的 CAST 的选择率修正了,这里的 SLCT CARD 都是正常。

评论
后发表回复

作者

文章

阅读量

获赞

扫一扫
联系客服