数据库执行sql的大致流程(三)
前言
网上有很多关于sql语句优化的文章,我这里想说下为什么这样。。。写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
在所有的矛盾中,要优先解决主要矛盾,其他矛盾也就迎刃而解。
不要做个笨蛋,为失去的郁郁寡欢,聪明的人,已经找到了解决问题的办法,或正在寻找。