SQL优化的一些总结 SQL编写一般要求
SQL编写一般要求
---SQL语句尽可能简单
---分解联接保证高并发
---同数据类型的列值比较
---不在索引列做运算
---禁止使用SELECT *
---避免负向查询和%前缀模糊查询
---保持事务(连接)短小
---改写OR为IN()
---改写OR为UNION
---LIMIT高效分页
---用UNION ALL而非 UNION
---GROUP BY 去除排序
SQL语句尽可能简单
分解联接保证高并发
MySQL> Select post_name from tag JOIN tag_post
on tag_post.tag_id=tag.id JOIN post
on tag_post.post_id=post.id
WHERE tag.tag='二手玩具';
MySQL> Select tag_id from tag WHERE tag='二手玩具';
MySQL> Select post_id from tag_post WHERE tag_id=1321;
MySQL> Select post_name from post WHERE post.id in (123,456,314,141);
同数据类型的列值比较
不在索引列做运算

禁止使用SELECT *
SELECT * FROM tag WHERE id = 999184;
SELECT keyword FROM tag WHERE id = 999184;
避免负向查询和%前缀模糊查询
MySQL> select * from post WHERE title like '北京%' ;
298 rows in set (0.01 sec)
MySQL> select * from post WHERE title like '%北京%' ;
572 rows in set (3.27 sec)
保持事务(连接)短小
保持事务/DB连接短小精悍
改写OR为IN()
Select * from opp WHERE phone='12347856' or phone='42242233' ;
Select * from opp WHERE phone in ('12347856' , '42242233') ;
改写OR为UNION
Select * from opp WHERE phone='010-88886666' or cellPhone='13800138000';
Select * from opp WHERE phone='010-88886666'
union
Select * from opp WHERE cellPhone='13800138000';
LIMIT高效分页(一)
Select * from table WHERE id>=23423 limit 11; #10+1 (每页10条)
select * from table WHERE id>=23434 limit 11;
LIMIT高效分页(二)
Select * from table WHERE id >= ( select id from table limit 10000,1 ) limit 10;
SELECT * FROM table INNER JOIN (SELECT id FROM table LIMIT 10000,10) USING (id) ;
LIMIT高效分页(三)
MySQL> select sql_no_cache * from post limit 10,10;
10 row in set (0.01 sec)
MySQL> select sql_no_cache * from post limit 20000,10;
10 row in set (0.13 sec)
MySQL> select sql_no_cache * from post limit 80000,10;
10 rows in set (0.58 sec)
MySQL> select sql_no_cache id from post limit 80000,10;
10 rows in set (0.02 sec)
MySQL> select sql_no_cache * from post WHERE id>=323423 limit 10;
10 rows in set (0.01 sec)
MySQL> select * from post WHERE id >=
( select sql_no_cache id from post limit 80000,1 ) limit 10 ;
10 rows in set (0.02 sec)
用UNION ALL而非 UNION
MySQL> SELECT * FROM detail20091128 UNION ALL
SELECT * FROM detail20110427 UNION ALL
SELECT * FROM detail20110426 UNION ALL
SELECT * FROM detail20110425 UNION ALL
SELECT * FROM detail20110424 UNION ALL
SELECT * FROM detail20110423;
GROUP BY 去除排序
MySQL> select phone,count(*) from post group by phone limit 1 ;
1 row in set (2.19 sec)
MySQL> select phone,count(*) from post group by phone order by null limit 1;
1 row in set (2.02 sec)
http://www.taobaodba.com/html/851_sql%E4%BC%98%E5%8C%96%E7%9A%84%E4%B8%80%E4%BA%9B%E6%80%BB%E7%BB%93.html SQL的优化是DBA日常工作中不可缺少的一部分,记得在学生时期,曾经在ITPUB上看到一篇帖子,当时楼主在介绍SQL优化的时候,用一个公式来讲解他在做sql优化的时候遵循的原则:
T=S/V(T代表时间,S代表路程,V代表速度)
S指SQL所需访问的资源总量,V指SQL单位时间所能访问的资源量,T自然就是SQL执行所需时间了;我们为了获得SQL最快的执行时间,可以根据公式定义上去反推:
- 在S不变的情况下,我们可以提升V来降低T:通过适当的索引调整,我们可以将大量的速度较慢的随机IO转换为速度较快的顺序IO;通过提升服务器的内存,使得将更多的数据放到内存中,会比数据放到磁盘上会得到明显的速度提升;采用电子存储介质进行数据存储和读取的SSD,突破了传统机械硬盘的性能瓶颈,使其拥有极高的存储性能;在提升V上我们可以采用较高配置的硬件来完成速度的提升;
- 在V不变的情况下,我们可以减小S来降低T:这是SQL优化中非常核心的一个环节,在减小S环节上,DBA可以做的可以有很多,通常可以在查询条件中建立适当的索引,来避免全表扫描;有时候可以改写SQl,添加一些适当的提示符,来改变SQL的执行计划,使SQL以最少的扫描路径完成查询;当这些方法都使用完了之后,你是否还有其他方案来优化喃?在阿里系的DBA职位描述中有条就是要求DBA需要深入的了解业务,当DBA深入的了解业务之后,这个时候能站在业务上,又站DB角度上考虑,这个时候在去做优化,有时候能达到事半功倍的效果。
案例一:通过降低S,来提升T
原理介绍:
我们知道B+索引叶子节点的值是按照索引字段升序的,比如我们对(nick,appkey)两个字段做了索引,那么在索引中的则是按照nick,appkey的升序排列;如果我们现在的一条sql:
select count(distinct nick) from xxxx_nickapp_09_29;
用于查询统计某天日志表中的UV,优化器选择了该表上索引ind_nick_appkey(nick,appkey)来完成查询,则开始从nick1开始一条条扫描下来,直到扫描到最后一个nick_n,那么中间过程会扫描很多重复的nick(最左边普通扫描),如果我们能够跳过中间重复的nick,则性能会优化非常多(最右边的松散扫描):

从上面的可以得到一个结论:
如果这条统计uv的sql能够按照右边的loose index scan的方式来扫描话,会大大的减小我们上面提到的S;所以需要通过改写sql来达到伪loose index scan:(MySql优化器不能直接的对count(distinct column)做优化)
root@DB 09:41:30>select count(*) from ( select distinct(nick) from xxxx_nickapp_09_29)t ;
+———-+
| count(*) |
+———-+
| 806934 |
+———-+
Sql内查询中先选出不同的nick,最后在外面套一层count,就可以得到nick的distinct值总和;
最重要的是在子查询中:select distinct(nick) 实现了上图中的伪loose index scan,优化器在这个时候的执行计划为Using index for group-by ,这样mysql就把distinct优化为group by,首先利用索引来分组,然后扫描索引,对需要的nick只扫描一条记录。
真实案例:
该案例选自我们的一个线上的生产系统,该系统每天有大量的日志数据入库,单表的容量在10G-50G之间,然后做汇总分析,计算日志数据中的uv就是其中一个逻辑,sql如下:
select count(distinct nick) from xxxx_nickapp_09_29;
即使在_xxxx分表上加上nick的索引,通过查看执行计划,为全索引扫描,由于单表的数据量过大,sql在执行的时候,会对整个服务器带来抖动,需要对原来的SQL进行改写,使其支持loose index scan;
优化前:
root@DB 09:41:30>select count(distinct nick) from xxxx_nickapp_09_29;
+———-+
| count(*) |
+———-+
| 806934 |
1 row in set (52.78 sec)
执行一次sql需要花费52.78s
优化后:
root@DB 09:41:30>select count(*) from ( select distinct(nick) from xxxx_nickapp_09_29)t ;
+———-+
| count(*) |
+———-+
| 806934 |
+———-+
1 row in set (5.81 sec)
由52.78秒降至5.81秒,速度提升了差不多10倍;
查看SQL的执行计划:
优化写法:
root@DB 09:41:30>explain select count(*) from ( select distinct(nick) from xxxx_nickapp_09_29)t ;
+—-+————-+——————————+——-+—————+———————————+———+—–
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+—-+————-+——————————+——-+—————+———————————+———+—–
| 1 | SIMPLE | xxxx_nickapp_09_29 | range | NULL |ind_nick_appkey | 67 | NULL | 2124695 |Using index for group-by |
+—-+————-+——————————+——-+—————+———————————+———+—–
原始写法:
root@DB 09:41:50>explain select count(distinct nick) from xxxx_nickapp_09_29;
+—-+————-+——————————+——-+—————+—————————-+———+——+–
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+—-+————-+——————————+——-+—————+—————————-+———+——+–
| 1 | SIMPLE | xxxx_nickapp_09_29 | index | NULL | ind_nick_appkey | 177 | NULL | 19546123 |Using index |
+—-+————-+——————————+——-+————–+—————————-+———+——+–
可以看到我们的路程由19546123减小到2124695,减小了9倍多.^_^
案例二:结合业务递增的写入特点,巧妙优化UV统计count(*)
有时候觉得,优化一条sql的最高境界就是让这sql能够从把这条从系统中拿掉,不管怎样,这些都是建立在你足够的了解业务上,就能够推动一些业务产品的升级或者下线,这样的DBA你能做到吗?
下面看一个案例:应用每天都会对入库的分表统计一个总数:select count(*) from xx_01_01;
随着单表的数据量越来越大(单表在20G左右),每次进行count的时候,速度越来越慢,同时需要扫描较多的数据页块,导致整个数据库性能的抖动,通过分析业务的特点,由于每张表采用自增id的方式进行插入,并且没有数据的删除,所以统计全表的总数就可以变通一下:

所以这条sql:select count(*) from xx_01_01;
可以变通为: select max(id)-min(id)+1 from xx_01_01;
执行速度可以得到质的飞跃 ^_^.
案例三:通过提升V,来降低T—随机IO VS 顺序IO
在前面我们提到,提升V的一些方法,通常可以采用提升服务器硬件的方式来达到,但是很多中小型企业来说,现在比较高的成本对于他们来说还是望尘莫及,同时没有成熟的使用经验,对于他们可能还是一件坏事情。总的来说,你的服务器硬件无论在牛,如果SQL写的烂,索引建的不好,那还是不行的。
真实线上案例:在我们的一个核心产品库上,承载着非常大量的随机读,就叫它读库好了。一天读库的load非常的高,通过慢日志发现,有一条sql频繁的出现在慢日中,这条sql的查询条件很复杂,同时该表上的类似相同的索引也非常的多,当时是怀疑索引走错,通过explain 来查看SQL的执行计划:发现执行计划中的using where代表查询回表了,同时由于回表的记录rows较大,所以带来了大量的随机IO:

所以我们只需要在原来的索引冗余掉is_detail字段就可以通过覆盖索引的方法优化掉该sql,避免了查询回表而导致的随机io,用顺序io替换了原来的随机io,SQL的执行速度得到极大提升:(下图会去掉is_detail字段的测试)

总结:SQL优化是很有趣的一件事情,我们在日常工作中可以按照t=s/v的思路来进行优化,也许你第一次运用它的时候有些陌生,但是只要不断的练习,善于总结,你也会发现其中的规律,真是妙哉妙哉。还有一点很重要的是,你的SQL优化不要脱离实际业务,也许你在哪里优化一条sql花了1个小时,但是去和开发同学讨论优化成果的时候,开发同学说这条sql其实可以下线了,那时候真的哭笑不得了 ^_^.
SQL优化的一些总结 SQL编写一般要求的更多相关文章
- 智能SQL优化工具--SQL Optimizer for SQL Server(帮助提升数据库应用程序性能,最大程度地自动优化你的SQL语句 )
SQL Optimizer for SQL Server 帮助提升数据库应用程序性能,最大程度地自动优化你的SQL语句 SQL Optimizer for SQL Server 让 SQL Serve ...
- Oracle之SQL优化专题01-查看SQL执行计划的方法
在我2014年总结的"SQL Tuning 基础概述"中,其实已经介绍了一些查看SQL执行计划的方法,但是不够系统和全面,所以本次SQL优化专题,就首先要系统的介绍一下查看SQL执 ...
- Oracle之SQL优化专题02-稳固SQL执行计划的方法
首先构建一个简单的测试用例来实际演示: create table emp as select * from scott.emp; create table dept as select * from ...
- 有史以来性价比最高最让人感动的一次数据库&SQL优化(DB & SQL TUNING)——半小时性能提升千倍
昨天,一个客户现场人员急急忙忙打电话找我,说需要帮忙调优系统,因为经常给他们干活,所以,也就没多说什么,先了解情况,据他们说,就是他们的系统最近才出现了明显的反应迟钝问题,他们的那个系统我很了解,软硬 ...
- 基于oracle的sql优化
[基于oracle的sql优化] 基于oracle的sql优化 [博主]高瑞林 [博客地址]http://www.cnblogs.com/grl214 一.编写初衷描述 在应有系统开发初期,由于数据库 ...
- SQL优化的若干原则
SQL语句:是对数据库(数据)进行操作的惟一途径:消耗了70%~90%的数据库资源:独立于程序设计逻辑,相对于对程序源代码的优化,对SQL语句的优化在时间成本和风险上的代价都很低:可以有不同的写法:易 ...
- 面试题: 数据库 sql优化 sql练习题 有用 学生表,课程表,成绩表,教师表 练习
什么是存储过程?有哪些优缺点? 什么是存储过程?有哪些优缺点? 存储过程就像我们编程语言中的函数一样,封装了我们的代码(PLSQL.T-SQL). 存储过程的优点: 能够将代码封装起来 保存在数据库之 ...
- 数据库优化 - SQL优化
前面一篇文章从实例的角度进行数据库优化,通过配置一些参数让数据库性能达到最优.但是一些"不好"的SQL也会导致数据库查询变慢,影响业务流程.本文从SQL角度进行数据库优化,提升SQ ...
- 转载 数据库优化 - SQL优化
判断问题SQL判断SQL是否有问题时可以通过两个表象进行判断: 系统级别表象CPU消耗严重IO等待严重页面响应时间过长应用的日志出现超时等错误可以使用sar命令,top命令查看当前系统状态. 也可以通 ...
随机推荐
- shell编程学习笔记【原创】
本文为本人学习笔记,如有转载请注明出处,谢谢 一.Bourne Shell 有如下四种变量: 用户自定义变量 位置变量,即命令行参数 预定义变量 环境变量 二.位置变量 $ 与键入的命令行一样,包含脚 ...
- Linux单机安转Spark
安装Spark需要先安装jdk及安装Scala. 1. 创建目录 > mkdir /opt/spark > cd /opt/spark 2. 解压缩.创建软连接 > tar z ...
- 9.OpenStack安装web界面
安装仪表板 安装仪表板组件 yum install -y openstack-dashboard httpd mod_wsgi memcached python-memcached 编辑/etc/op ...
- Jekyll搭建个人博客
网上也有HEXO 搭建的博客,有人说使用 HEXO 在多台电脑上发布博客,操作起来并不是那么方便,所以使用Jekyll 来搭建. Jekyll配置 1,安装ruby环境 Windows系统使用Ruby ...
- FTP-Filezilla首次配置
最新新弄了个服务器,先吐槽下,之前买镜像都是免费的,昨天试了,竟然收费.... 好吧,用户多了也正常. 代码发布之前都是很暴力的直接远程桌面然后粘贴,有个合作伙伴突然需要FTP,说之前用的就是,我就做 ...
- nodejs微服务
近来公司增加了nodejs微服务 它的主要任务是接收来自于现场的采集数据:作业记录和流转记录,动态构建一个基地的全景实时数据 暂时不涉及数据库. 如果要进行数据库操作,不建议使用本模块, ...
- URl 传参时+号变成空格
前端用base64加密后的数据,传递到后台时发现一个问题: 比如 韩飞 这个名字,base64加密后的字符串为 6Z+p6aOe 但是后端接受到参数为: 6Z p6aOe +号变成了空格,导致后台解密 ...
- 洛谷——P2118 比例简化
P2118 比例简化 题目描述 在社交媒体上,经常会看到针对某一个观点同意与否的民意调查以及结果.例如,对某一观点表示支持的有1498 人,反对的有 902人,那么赞同与反对的比例可以简单的记为149 ...
- 【bzoj2839】【集合计数】容斥原理+线性求阶乘逆元小技巧
(上不了p站我要死了,侵权度娘背锅) Description 一个有N个元素的集合有2^N个不同子集(包含空集),现在要在这2^N个集合中取出若干集合(至少一个),使得 它们的交集的元素个数为K,求取 ...
- 警告Conversion specifies type'int' but the argument has type'size_t'
代码: #import<Foundation/Foundation.h> int main(int argc,const char * argv[]){ const char *words ...