日期:2014-05-16  浏览次数:20742 次

MySQL查询的性能优化(查询缓存、排序和索引)

询是数据库技术中最常用的操作。查询操作的过程比较简单,首先从客户端发出查询的SQL语句,数据库服务端在接收到由客户端发来的SQL语句后,执行这条SQL语句,然后将查询到的结果返回给客户端。虽然过程很简单,但不同的查询方式和数据库设置,对查询的性能将会有很在的影响。?
因此,本文就在MySQL中常用的查询优化技术进行讨论。讨论的内容如:通过查询缓冲提高查询速度;MySQL对查询的自动优化;基于索引的排序;不可达查询的检测和使用各种查询选择来提高性能。?
一、?通过查询缓冲提高查询速度?
一般我们使用SQL语句进行查询时,数据库服务器每次在收到客户端发来SQL后,都会执行这条SQL语句。但当在一定间隔内(如1分钟内),接到完全一样的SQL语句,也同样执行它。虽然这样可以保证数据的实时性,但在大多数时候,数据并不要求完全的实时,也就是说可以有一定的延时。如果是这样的话,在短时间内执行完全一样的SQL就有些得不偿失。?
幸好MySQL为我们提供了查询缓冲的功能(只能在MySQL?4.0.1及以上版本使用查询缓冲)。我们可以通过查询缓冲在一定程度上提高查询性能。?
我们可以通过在MySQL安装目录中的my.ini文件设置查询缓冲。设置也非常简单,只需要将query_cache_type设为1即可。在设置了这个属性后,MySQL在执行任何SELECT语句之前,都会在它的缓冲区中查询是否在相同的SELECT语句被执行过,如果有,并且执行结果没有过期,那么就直接取查询结果返回给客户端。但在写SQL语句时注意,MySQL的查询缓冲是区分大小写的。如下列的两条SELECT语句:?

复制内容到剪贴板?

代码:?
SELECT?*?from?TABLE1?
SELECT?*?FROM?TABLE1?

上面的两条SQL语句对于查询缓冲是完全不同的SELECT。而且查询缓冲并不自动处理空格,因此,在写SQL语句时,应尽量减少空格的使用,尤其是在SQL首和尾的空格(因为,查询缓冲并不自动截取首尾空格)。?
虽然不设置查询缓冲,有时可能带来性能上的损失,但有一些SQL语句需要实时地查询数据,或者并不经常使用(可能一天就执行一两次)。这样就需要把缓冲关了。当然,这可以通过设置query_cache_type的值来关闭查询缓冲,但这就将查询缓冲永久地关闭了。在MySQL?5.0中提供了一种可以临时关闭查询缓冲的方法:?

复制内容到剪贴板?

代码:?
SELECT?SQL_NO_CACHE?field1,?field2?FROM?TABLE1?

以上的SQL语句由于使用了SQL_NO_CACHE,因此,不管这条SQL语句是否被执行过,服务器都不会在缓冲区中查找,每次都会执行它。?
我们还可以将my.ini中的query_cache_type设成2,这样只有在使用了SQL_CACHE后,才使用查询缓冲。?

复制内容到剪贴板?

代码:?
SELECT?SQL_CALHE?*?FROM?TABLE1?

二、MySQL对查询的自动优化?
索引对于数据库是非常重要的。在查询时可以通过索引来提高性能。但有时使用索引反而会降低性能。我们可以看如下的SALES表:?

复制内容到剪贴板?

代码:?
CREATE?TABLE?SALES?
(?
ID?INT(10)?UNSIGNED?NOT?NULL?AUTO_INCREMENT,?
NAME?VARCHAR(100)?NOT?NULL,?
PRICE?FLOAT?NOT?NULL,?
SALE_COUNT?INT?NOT?NULL,?
SALE_DATE?DATE?NOT?NULL,?
PRIMARY?KEY(ID),?
INDEX?(NAME),?
INDEX?(SALE_DATE)?
);?

假设这个表中保存了数百万条数据,而我们要查询商品号为1000的商品在2004年和2005年的平均价格。我们可以写如下的SQL语句:?
SELECT?AVG(PRICE)?FROM?SALES?
WHERE?ID?=?1000?AND?SALE_DATE?BETWEEN?‘2004-01-01′?AND?‘2005-12-31′;?
如果这种商品的数量非常多,差不多占了SALES表的记录的50%或更多。那么使用SALE_DATE字段上索引来计算平均数就有些慢。因为如果使用索引,就得对索引进行排序操作。当满足条件的记录非常多时(如占整个表的记录的50%或更多的比例),速度会变慢,这样还不如对整个表进行扫描。因此,MySQL会自动根据满足条件的数据占整个表的数据的比例自动决定是否使用索引进行查询。?
对于MySQL来说,上述的查询结果占整个表的记录的比例是30%左右时就不使用索引了,这个比例是MySQL的开发人员根据他们的经验得出的。然而,实际的比例值会根据所使用的数据库引擎不同而不同。?
三、?基于索引的排序?
MySQL的弱点之一是它的排序。虽然MySQL可以在1秒中查询大约15,000条记录,但由于MySQL在查询时最多只能使用一个索引。因此,如果WHERE条件已经占用了索引,那么在排序中就不使用索引了,这将大大降低查询的速度。我们可以看看如下的SQL语句:?

复制内容到剪贴板?

代码:?
SELECT?*?FROM?SALES?WHERE?NAME?=?“name”?ORDER?BY?SALE_DATE?DESC;?

在以上的SQL的WHERE子句中已经使用了NAME字段上的索引,因此,在对SALE_DATE进行排序时将不再使用索引。为了解决这个问题,我们可以对SALES表建立复合索引:?

复制内容到剪贴板?

代码:?
ALTER?TABLE?SALES?DROP?INDEX?NAME,?ADD?INDEX?(NAME,?SALE_DATE)?

这样再使用上述的SELECT语句进行查询时速度就会大副提升。但要注意,在使用这个方法时,要确保WHERE子句中没有排序字段,在上例中就是不能用SALE_DATE进行查询,否则虽然排序快了,但是SALE_DATE字段上没有单独的索引,因此查询又会慢下来。?
四、?不可达查询的检测?
在执行SQL语句时,难免会遇到一些必假的条件。所谓必假的条件是无论表中的数据如何变化,这个条件都为假。如WHERE?value?<?100?AND?value?>?200。我们永远无法找到一个既小于100又大于200的数。?
如果遇到这样的查询条件,再去执行这样的SQL语句就是多此一举。幸好MySQL可以自动检测这种情况。如我们可以看看如下的SQL语句:?

复制内容到剪贴板?

代码:?
SELECT?*?FROM?SALES?WHERE?NAME?=?“name1”?AND?NAME?=?“name2”?

以上的查询语句要查找NAME既等于name1又等于name2的记录。很明显,这是一