zoukankan      html  css  js  c++  java
  • sql语句优化原理

    前言

    网上有很多关于sql语句优化的文章,我这里想说下为什么这样。。。写sql语句,能够提高查询的效率。

    1 sql语句优化原理

    要想写出好的sql,就要学会用数据库的方式来思考如何执行sql,那么什么是好的sql,首先要明白数据库是如何执行一个sql,一个事务的。

    1.1 数据库执行sql的大致流程

    粗略流程,所有关系型数据库都是这几步,具体前后顺序根据不同dbms不同配置下略有小差别,以下过程都需要耗时耗资源:

    1.应用程序与数据库服务器建立链接

    2.sql发送到数据库,数据库验证是否有执行的权限

    3.进入语法解析器,进行词法与语法分析

    4.进入优化器生成执行计划,部分dbms会检查是否有可重用的执行计划

    5.根据执行计划依次扫描相关表中的行,不在数据缓冲区的走io

    6.同时对于被扫描的行可能加锁,同时也可能会被其他sql阻塞

    7.扫描的行足够放入查询缓存则开始运算或直接返回,不够则生成临时表,可能消耗io

    8.对sql结果进行计算(可能)

    9.将计算完成的结果全部写入网络io(可能)

    10.如果事务完成则同步事务日志并释放锁,具体方式取决于dbms和当前配置

    11.关闭连接(可选)

    1.2 如何优化

    这么多步骤,每一步都有优化策略,我尽量用简单的语言来描述:

    1.应用程序与数据库服务器建立链接:

    答:引入数据库连接池,避免每次都与数据库建立连接,提高效率。

    2.sql发送到数据库,数据库验证是否有执行的权限

    答:没撒好说的。

    3.进入语法解析器,进行词法与语法分析

    答:也没撒好说的,想要数据库在这里少用点资源就把sql写的简单点,但是差别不大。

    4.进入优化器生成执行计划,部分dbms会检查是否有可重用的执行计划

    答:任何数据库如何生成执行计划都可以写一本几百页的书,关系型数据库选择走什么执行计划都是基于消耗最小化的思路来的,简单来说就是走什么索引,按什么顺序走表,被扫到的数据行最少。如果你的表结构很复杂,有各种混搭的索引,你的join很多,那执行计划分析的时间就会拉长。所以sql对应的表索引简单,join或子查询少就快,复杂了优化器也会得选择困难症。

    5.根据执行计划依次扫描相关表中的行,不在数据缓冲区的走io

    答:存储引擎扫描表的性能消耗参考下面的list,消耗从大到小

    全表扫描>全索引扫描>部分索引扫描>索引查找>唯一索引/主键查找>常量/null

    要走索引对于sql语句也有要求,不能在谓词上作任何运算,扫描行数一般不能超过表的17%左右,这对你数据分布又有要求,比如你查select xxx from human where sex ='man',五五开,还是走扫描。这里我就不展开了,推荐题主一本书《Relational Database Index Design and the Optimizers》

    6.同时对于被扫描的行可能加锁,同时也可能会被其他sql阻塞

    答:如果扫描的行多,sql执行的时间长,被阻塞的概率就高,阻塞别人的概率也高,然后大家一起等,数据库就hung住了。

    7.扫描的行足够放入查询缓存则开始运算或直接返回,不够则生成临时表,可能消耗io

    答:一次取的尽量少,这不单指返回服务端的行数,应该从嵌套最深的一个子查询开始算

    8.对sql结果进行计算(可能)

    答:少用各种复杂的函数啊,count啊,order by啊等等。

    9.将计算完成的结果全部写入网络io(可能)

    答:请尽量少返回一点数据,如果不行请多次分批。

    10.如果事务完成则同步事务日志并释放锁,具体方式取决于dbms和当前配置

    这里举两个代表性栗子:

    sql渣:
    
    for i in (1-1000):
    
    start transaction;
    
    insert into table values (1);
    
    commit;
    
    end for
    
    sql赞:
    
    start transaction;
    
    for i in (1-1000):
    
    insert into table values (1);
    
    end for
    
    commit;
    
    sql赞爆:
    
    insert into table values (1)()...........()(1000);

    首先,sql语法是我临时自创的,这个不是关键,关键在sql渣先生是1000个事务插1000行,日志flush1000次。sql赞先生是一个事务插1000行,事务日志flush1次。sql赞爆最nice。

    这个例子我想表达的意思是如果你要用sql做一件事,那就要尽量让这件事占用的事务总时间最少。

    第二个例子

    sql渣:
    
    update table where id > 0 and id < 1000000;
    
     
    
    sql赞:
    
    update table where id > 0 and id < 1000;
    
    update table where id >= 1000 and id < 2000;
    
    update table where id >2000 and id < 3000;
    
    ....
    
    ....

    这个例子我想表达的意思是如果你要用sql做一件很大的事,那就尽量让大事化成很多小事。

    两个例子好好体会下,一点不矛盾哦。补充一下,这里每个update都是单独事务

    11.关闭连接(可选)

    答:同1,别每次都关,关了也许还要重连。不关的话记得commit就好了,千万要记得commit啊!

     1.3小结

    通过好的sql语句:减少访问的数据行、数据量、列数;减少返回的数据量;简化sql语法;减少sql语句中的函数;来实现更少的内存使用,更少的磁盘I/O读写,更快的得到较好的执行计划,以达到提高查询效率的目的。

    最后,要学会一种思路:如果我是一个数据库,我会怎么执行一个sql,我喜欢怎么样的sql?

    什么样的SQL语句是不好的语句呢:那就是妨碍优化器更好的实现执行逻辑的SQL语句,这类语句包括:

    1.where条件里出现各种花样百出的代码,比如函数、运算等。

    2.语句过大,大量的表join会导致中间结果集不准确,从而限制优化器选择较好的执行计划。等等.........

    3.能写出多好的sql取决于你多了解数据库。

    参看链接:链接:https://www.zhihu.com/question/29619558/answer/45805380

    作者:宋沄剑

    链接:https://www.zhihu.com/question/29619558/answer/45270490

     
    如果错过太阳时你流了泪,那你也要错过群星了。
    在所有的矛盾中,要优先解决主要矛盾,其他矛盾也就迎刃而解。
    不要做个笨蛋,为失去的郁郁寡欢,聪明的人,已经找到了解决问题的办法,或正在寻找。
  • 相关阅读:
    Codeforces Round #422 (Div. 2) D. My pretty girl Noora 数学
    Codeforces Round #422 (Div. 2) C. Hacker, pack your bags! 排序,贪心
    Codeforces Round #422 (Div. 2) B. Crossword solving 枚举
    XJTUOJ wmq的A×B Problem FFT/NTT
    BZOJ 3527: [Zjoi2014]力 FFT
    Educational Codeforces Round 9 E. Thief in a Shop NTT
    focal loss for dense object detection
    国内敏捷项目协作工具亲测推荐
    Leangoo背景更新-看板背景任你选!!!
    Leangoo新功能-卡片ID
  • 原文地址:https://www.cnblogs.com/szrs/p/12551967.html
Copyright © 2011-2022 走看看