美文网首页数据库SQL
数据库系列4 SQL优化

数据库系列4 SQL优化

作者: 莫小归 | 来源:发表于2019-02-28 12:11 被阅读0次

    参考:
    关于基数、选择性、直方图和回表 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
    待续

    相关文章

      网友评论

        本文标题:数据库系列4 SQL优化

        本文链接:https://www.haomeiwen.com/subject/hhchuqtx.html