Sql优化

在进行MySQL的优化之前必须要了解的就是MySQL的查询过程,很多的查询优化工作实际上就是遵循一些原则让MySQL的优化器能够按照预想的  合理方式运行而已。更多关于MySQL查询相关参照:http://www.cnblogs.com/clsn/p/8038964.html#_label6 系列文章。


数据库层面优化方式:

   1.硬件

           2.系统配置

           3.数据库表结构

           4.SQL及索引

优化成本及效果比拼图

优化工具:


关于zabbix参考:http://www.cnblogs.com/clsn/p/7885990.html

sql优化10个原则:

 原则1 : 尽量避免在列上进行运算,这样会导致索引失效。

        例如原句为: SELECT * FROM t WHERE YEAR(d) >= 2011;

        优化为:SELECT * FROM t WHERE d >= '2011-01-01';

原则2 : 使用JO IN时,应该用小结果集驱动大结果集。同时把复杂的JO IN查询拆分成多 个Query。因为JOIN多个表时,可能导致更多的锁定和堵塞。

        例如: SELECT * FROM a JOIN b ON a.id=b.idLEFT JOIN c ON c.time=a.dateLEFT JOIN d ON c.pid=b.aidLEFT JOIN e ON e.cid=a.did 

原则3 : 注意L IK E模糊查询的使用,避免% %。

        例如原句为: SELECT * FROM t WHERE name LIKE '%de%'

        优化为:SELECT * FROM t WHERE name>='de' AND name<'df'

原则4 : 仅列出需要查询的字段,这对速度不会有明显影响,主要考虑节省内存。

        例如原句为: SELECT * FROM Member;

        优化为:SELECT id,name,pwd FROM Member;

原则5 : 使用批量插入语句节省交互。

        例如原句为: INSERT INTO t (id,name) VALUES (1,'a');INSERT INTO t (id,name) VALUES (2,'b');INSERT INTO t (id,name) VALUES (3,'c');        

        优化为: INSERT INTO t (id,name) VALUES (1,'a'),(2,'b'),(3,'c'); 

原则6: limit的基数比较大时使用between。

        例如原句为: select * from article as article order by id limit 1000000,10

        优化为:select * from article as article where id between 1000000 and 1000010 order by id

between限定比limit快,所以在海量数据访问时,建议用between或是where替换掉limit。 但是between也有缺陷,如果id中间有断行或是中间部分id不读取的情况,总读取的数量会少 于预计数量! 在取比较后面的数据时,通过desc方式把数据反向查找,以减少对前段数据的扫描,让 limit的基数越小越好!

 原则7 : 不要使用rand函数获取多条随机记录。

        例如: select * from table order by rand() limit 20;

       优化为:SELECT * FROM 'table' AS tl JOIN (SELECT ROUND(RAND() * ((SELECT MAX(id) FROM 'table')-(SELECT MIN(id) FROM 'table'))+(SELECT MIN(id) FROM 'table')) AS id ) AS t2 WHERE tl.id>=t2.id ORDER BY tl.id LIMIT 1;

这是获取一条随机记录,这样即使执行2 0次,也比原来的语句髙效。或者先用java产生随机数,把这个字符串传给MySQL, MySQL里用in查询。

 原则8 : 避免使用NULL。

 原则9 : 不要使用count(id), 而应该是count( * )。 

原则1 0 :不要做无谓的排序操作,而应尽可能在索引中完成排序。 


Mysql查询语句执行原理

数据库查询语句如何执行?

DML语句首先进行语法分析,对使用sql表示的查询进行语法分析,生成查询语法分析树。

语义检查:检查sql中所涉及的对象以及是否在数据库中存在,用户是否具有操作权限等

视图转换:将语法分析树转换成关系代数表达式,称为逻辑查询计划;

查询优化:在选择逻辑查询计划时,会有多个不同的表达式,选择最佳的逻辑查询计划;

代码生成:必须将逻辑查询计划转换成物理查询计划,物理查询计划不仅能指明要执行的操作,也给出了这些操作的执行顺序,每步所用的算法,存储数据的方式以及从一个操作传递给另一个操作的方式。

将DML转换成一串可执行的存取操作的过程称为束缚过程,

Mysql查询语句执行过程

这里简单介绍一下mysql数据库,mysql数据库是一款关系型数据库,所谓关系型数据库就是以二维表的形式存储数据,使用行和列方便我们对数据的增删改查。

  我们以mysql数据库为例,对一条sql语句的执行流程进行分析。(本篇博客不涉及到表连接)

  首先,创建一张student表,字段有自增主键id,学生姓名name,学科subject,成绩grade

  建表语句:

                DROP TABLE IF EXISTS student;

                CREATE TABLE `student` (

                  `id` int(5) NOT NULL AUTO_INCREMENT,

                  `name` varchar(10) DEFAULT NULL,

                  `subject` varchar(10) DEFAULT NULL,

                  `grade` double(4,1) DEFAULT NULL,

                  PRIMARY KEY (`id`)

                ) ENGINE=InnoDB AUTO_INCREMENT=40 DEFAULT CHARSET=utf8;


  初始化数据:

            INSERT INTO student(`name`,`subject`,grade)VALUES('aom','语文',88);

            INSERT INTO student(`name`,`subject`,grade)VALUES('aom','数学',99);

            INSERT INTO student(`name`,`subject`,grade)VALUES('aom','外语',55);

            INSERT INTO student(`name`,`subject`,grade)VALUES('jack','语文',67);

            INSERT INTO student(`name`,`subject`,grade)VALUES('jack','数学',44);

            INSERT INTO student(`name`,`subject`,grade)VALUES('jack','外语',55);

            INSERT INTO student(`name`,`subject`,grade)VALUES('susan','语文',56);

            INSERT INTO student(`name`,`subject`,grade)VALUES('susan','数学',35);

            INSERT INTO student(`name`,`subject`,grade)VALUES('susan','外语',77);

            INSERT INTO student(`name`,`subject`,grade)VALUES('alice','语文',88);

            INSERT INTO student(`name`,`subject`,grade)VALUES('alice','数学',77);

            INSERT INTO student(`name`,`subject`,grade)VALUES('alice','外语',100);

            INSERT INTO student(`name`,`subject`,grade)VALUES('rajo','语文',33);

            INSERT INTO student(`name`,`subject`,grade)VALUES('rajo','数学',55);

            INSERT INTO student(`name`,`subject`,grade)VALUES('rajo','外语',55);

下面我们来看一下,数据在数据库中的存储形式。


 (图1.0)

现在针对这张student表中的数据提出一个问题:要求查询出挂科数目多于两门(包含两门)的前两名学生的姓名,如果挂科数目相同按学生姓名升序排列。

下面是这条查询的sql语句

SELECT `name`,COUNT(`name`) AS num FROM student WHERE grade<60GROUPBY`name`HAVINGnum>= 2 ORDER BY num DESC,`name` ASC LIMIT 0,2;

执行结果:


图(1.1)

以上这条sql语句基本上概括了单表查询中所有要注意的点,那么我们就以这条sql为例来分析一下一条语句的执行流程。

1,一条查询的sql语句先执行的是 FROM student 负责把数据库的表文件加载到内存中去,如图1.0中所示。(mysql数据库在计算机上也是一个进程,cpu会给该进程分配一块内存空间,在计算机‘服务’中可以看到,该进程的状态)


  图(1.2)

2,WHERE grade < 60,会把(图1.0)所示表中的数据进行过滤,取出符合条件的记录行,生成一张临时表,如下图所示。


 图(1.3)

3,GROUP BY `name`会把图(1.3)的临时表切分成若干临时表,分为四个分

组,我们用下图来表示内存中这个切分的过程。


 图(1.4)   图(1.5)  图(1.6)   图(1.7)

4,SELECT 的执行读取规则分为sql语句中有无GROUP BY两种情况。

  (1)当没有GROUP BY时,SELECT 会根据后面的字段名称对内存中的一张临时表整列读取。

  (2)当查询sql中有GROUP BY时,会对内存中的若干临时表分别执行SELECT,而且只取各临时表中的第一条记录,然后再形成新的临时表。这就决定了查询sql使用GROUP BY的场景下,SELECT后面跟的一般是参与分组的字段和聚合函数,否则查询出的数据要是情况而定。另外聚合函数中的字段可以是表中的任意字段,需要注意的是聚合函数会自动忽略空值。

  我们还是以本例中的查询sql来分析,现在内存中有四张被GROUP BY `name`切分成的临时表,我们分别取名为 tempTable1,tempTable2,tempTable3,tempTable4分别对应图(1.4)、图(1.5)、图(1.6),图(1.7)下面写四条"伪SQL"来说明这个查询过程。

SELECT `name`,COUNT(`name`) AS num FROM tempTable1;

SELECT `name`,COUNT(`name`) AS num FROM tempTable2;

SELECT `name`,COUNT(`name`) AS num FROM tempTable3;

SELECT `name`,COUNT(`name`) AS num FROM tempTable4;

最后再次成新的临时表,如下图:


  图(1.8)

5,HAVING num >= 2对上图所示临时表中的数据再次过滤,与WHERE语句不同的是HAVING 用在GROUP BY之后,WHERE是对FROM student从数据库表文件加载到内存中的原生数据过滤,而HAVING 是对SELECT 语句执行之后的临时表中的数据过滤,所以说column AS otherName ,otherName这样的字段在WHERE后不能使用,但在HAVING 后可以使用。但HAVING的后使用的字段只能是SELECT 后的字段,SELECT后没有的字段HAVING之后不能使用。HAVING num >= 2语句执行之后生成一张临时表,如下:


  图(1.9)


6,ORDER BY num DESC,`name` ASC对以上的临时表按照num,name进行排序。

7,LIMIT 0,2取排序后的前两个。


以上就是一条sql的执行过程,同时我们在书写查询sql的时候应当遵守以下顺序。

SELECT XXX FROM XXX WHERE XXX GROUP BY XXX HAVING XXX ORDER BY XXX LIMIT XXX;

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容