数据库系列4 SQL优化

参考:
关于基数、选择性、直方图和回表 https://www.jianshu.com/p/44ebb7646b34
关于SQL优化的误区和小技巧 https://www.jianshu.com/p/8a4d8f1ccc56
关于逻辑算子 http://www.imooc.com/article/278660

一.SQL优化

1.目的
  • 减少IO
  • 减少CPU占用(少使用order by/group by/distinct)
2.技巧
  • 少用join
  • 少排序(无索引时)
  • 少select *,查询指定字段,不要返回不用的字段
  • 少用or,或用in替换
  • 用join替代子查询
  • 避免类型转换
  • 查询结果条数过多时,使用分页
3.SQL优化场景
  • 只有大表才会产生性能问题

二.几个概念

1.基数(Cardinality)
  • 某个列的唯一键的数量称为基数
  • 主键列的基数等于表的总行数
  • 基数的高低影响列的数据分布
2.选择性(Selectivity)
  • 选择性 = 基数/总行数 * 100%
  • 索引适用于选择性高的表
  • 优化点:确定必须创建索引的列有利于SQL优化
3.直方图(Histogram)
  • 基数较低的列数据分布可能会不均衡,查询这种列时可能走全表扫描也可能走索引,此时容易走错执行计划
  • 如果没有对基数低的列收集直方图统计信息,基于成本的优化器(CBO)会认为该列数据分布式均衡的
  • 直方图可帮助CBO对基数低、数据分布不均衡的列精确估算rows值
  • 优化点:确定必须创建直方图的列有利于SQL优化
4.回表(Table Access By Index RowId)
  • 对某个列创建索引之后,索引会包含该列的键值以及键值对应行所在的RowId
  • 回表就是通过索引中记录的RowId访问表中的数据
  • 回表一般是单块读,回表次数过多严重影响SQL性能,此时应该放弃索引扫描而采取全表扫描
  • 优化点:返回表中5%以下的数据时走索引,5%以上的数据时走全表扫描(避免过多回表)
5.逻辑算子
  • DataSource:数据源,也就是我们SQL语句中的表。select name from table1中的table1
  • Selection:选择,如select name from table1 where id = 1中的where后的过滤条件
  • Projection:投影,指搜索的列,如select name from table1 where id = 1中的列name
  • Join:连接,如select * from table1 table2 where table1.name=table2.name就是把两个表做Join。连接条件是最简单的等值连接,当然还有其他我们熟知的inner join,left join,right join等等
  • Sort:排序,如select * from table1 order by id里面的order by。无序的数据通过这个算子处理后,输出有序的数据
  • Aggregation:分组,如select sum(score) from table1 group by name中的group by。按照某些列进行分组,分组后可以进行一些聚合操作,比如Max、Min、Sum、Count、Average等等
  • Apply:子查询,如select * from (select id,name from table1) as t中的(select id,name from table1) as t。可以进行嵌套查询
  • 选择、投影、连接就是最基本的算子
6.逻辑查询计划
  • 逻辑查询计划就是SQL语句通过SQL解析之后由各个逻辑算子组成的树状结构。
select user.name from user,score where user.id = score.id and score.num > 60

变成查询计划之后如图所示:
1)DataSource,user,score表,负责读取数据。
2)Join,user.id=score.id
3)Selection过滤,score.num > 60
4)Projection投影,user.name

三.常见误区

1.count(1)和count(primary_key)优于count(*)
  • 统计记录条数时,很多人认为count(1)或者count(primary_key)优于count(),其实对于某些场景,count()表现可能更好,因为数据库对count(*)计数操作进行了特别的优化
2.count(column)和count(*)是一样的
  • 实际上count(column)统计结果集中有多少个column字段不为空的记录,count()则是表示整个结果集有多少条记录
3. select a,b from ... 比 select a,b,c from ...可以让数据库返回更少的数据量
  • 实际上大多数关系型数据库是按照行(row)的方式存储,但数据读取操作都是以一个固定大小的IO单元(block/page)为单位
  • 大多数情况,一个IO单元存储了多行,而每行均存储了该行的所有字段,所以在查询中,取一个字段还是多个字段并不影响在数据表中需要访问的数据量
4. order by 一定需要排序操作
  • 索引数据实际上是有序的
  • 当需要的数据和某个索引顺序一致,且该查询通过这个索引来执行时,数据库将省略排序操作而直接返回数据(数据库知道直接返回的数据已经满足我们的排序需求)

四.关于索引的SQL语句优化技巧

1.尽量避免在where子句中使用 > 、<、!= 等范围操作符,否则数据库引擎将放弃索引进行全表扫描
2.尽量避免在where子句中使用 or 条件,或使用union或者in代替or,否则数据库引擎将放弃索引进行全表扫描
select id from t where num=10 or num=20 
select * from t where LOC_ID = 10 or LOC_ID = 20 or LOC_ID = 30;
  • 优化为
select id from t where num=10 union all select id from t where num=20
select * from t where LOC_IN in (10,20,30);
3.慎用 in 和 not in
  • 对于连续数据,使用between代替
  • 使用 exists 代替 in
SELECT * FROM ACCOUNT
    WHERE AC_CODE 
    NOT IN (
              SELECT CODE
              FROM GOODS
              WHERE NUM='001')       //低效

SELECT * FROM ACCOUNT
    WHERE NOT EXISTS
       (SELECT CODE
          FROM GOODS
          WHERE CODE=ACCOUNT.AC_CODE
           AND NUM='001')             //更高效
4. like %aaa% 语句将导致全表扫描
5.避免在 where 子句进行表达式操作
select id form t where num/2 = 100
  • 优化为
select id from t where num = 200
6.避免在 where 子句对字段进行函数操作
select id from t where substring(name,1,3) = 'abc'
  • 优化为
select id from t where name like 'abc%'
7.where字句中的连接顺序
  • oracle采用自下而上的顺序解析where字句
    表之间的链接必须写在其他where条件之前,那些可以滤过大量纪录的条件必须写在where字句的末尾,例如:
select * from table e
      where h>500     
                  and d='001'
                  and 25<(select count(*) 
                               from table 
                               where count=e.count);      //低效

select * from table e
      where  25<(select count(*) 
                          from table 
                          where count=e.count);    
                   and  h>500
                   and d='001';                            //更高效 
8.

五.关于SQL语句执行顺序的优化

1.语法顺序和执行顺序
  • SQL的语法顺序:
select [distinct]...from...[xxx join][on]...where...group by...having...[union]...order by...
  • SQL的执行顺序:
from...[xxx join][on]...where...group by...sum()...having...select [distinct]...order by....
2.from 子句
  • 执行顺序为从后往前、从右到左
  • 最后面的那个表名为驱动表,因为执行顺序为从后往前, 所以数据量较少的表尽量放后
3.where子句
  • 执行顺序为自下而上、从右到左
  • 尽量将可以过滤掉大量数据的条件写在where的子句放在最后

4.group by 和order by 子句

  • 执行顺序都为从左到右
5.select子句
  • 少用*号,使用列名可减少耗时

六.查询优化器

1.查询优化器分类
  • RBO
    基于规则的优化器(Rule-Based Optimizer)
    RBO严格按照既定优化规则优化Sql语句,同一条Sql语句在不同数据环境下有相同优化结果
    在RBO中Sql写法不同很可能影响最终的执行计划,从而影响脚本性能
  • CBO
    基于成本的优化器(Cost-Based Optimizer)
    Sql语句通过优化生成多个执行计划,CBO根据统计信息和成本模型(Cost Model)计算每个执行计划的成本,选取成本最小者执行
2.查询优化器执行过程
  • RBO
    1)Transformation:遍历关系表达式,按既定优化规则转换Sql语句
    2)Build Physical Plan:根据转换的Sql语句形成执行计划
  • CBO
    1)Exploration:根据优化规则进行语句等价转换,同时保留原关系表达式
    2)Build Physical Plan:产生各语句的执行计划
    3)Find Best Plan:执行cost最小的执行计划
3.查询优化器常见优化规则(自动执行)
  • 谓词下推
    优化器自动将外层查询块的 WHERE 子句中的谓词移入所包含的较低层查询块,从而能够提早进行数据过滤以及有可能更好地利用索引
//优化前
EXPLAIN SELECT *
FROM t_student,t_score
WHERE t_score.student_id=t_student.student_id
AND t_score.score_id=2;
//优化后
EXPLAIN SELECT * FROM t_student t1 right JOIN (
SELECT * from t_score WHERE score_id=2
) t2
ON t1.student_id=t2.student_id;
  • 列裁剪
    SQL查询优化器通过只读取需要的数据,省略过没有用到的列,以减少IO提高执行效率
  • 常量折叠
  • 投影消除
  • 最大最小消除
    查询计划会自动优化(最大消除)
//优化前
select max(id) from table1
//优化后
select max(id) from (select id from table1 order by id desc limit 1 where id is not null) t

一个具体Sql优化实例 https://www.jianshu.com/p/0b1571730d3f
待续

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343

推荐阅读更多精彩内容