总的来说,要想使一个较慢速select ... where更快,应首先检查是否能增加一个索引。不同表之间的引用通常通过索引来完成。你可以使用explain语句来确定select语句使用哪些索引。参见7.4.5节,"mysql教程如何使用索引"和7.2.1节,"explain语法(获取关于select的信息)"。
下面是一些加速对myisam表的查询的一般建议:
· 为了帮助mysql更好地优化查询,在一个装载数据后的表上运行analyze table或myisamchk --analyze。这样为每一个索引更新指出有相同值的行的平均行数的值(当然,如果只有一个索引,这总是1。)mysql使用该方法来决定当你联接两个基于非常量表达式的表时选择哪个索引。你可以使用show index from tbl_name并检查cardinality值来检查表分析结果。myisamchk --description --verbose可以显示索引分布信息。
· 要想根据一个索引排序一个索引和数据,使用myisamchk --sort-index --sort-records=1(如果你想要在索引1上排序)。如果只有一个索引,想要根据该索引的次序读取所有的记录,这是使查询更快的一个好方法。但是请注意,第一次对一个大表按照这种方法排序时将花很长时间!
7.2.4. mysql怎样优化where子句
该节讨论为处理where子句而进行的优化。例子中使用了select语句,但相同的优化也适用delete和update语句中的where子句。
请注意对mysql优化器的工作在不断进行中,因此该节并不完善。mysql执行了大量的优化,本文中所列的并不详尽。
下面列出了mysql执行的部分优化:
· 去除不必要的括号:
· ((a and b) and c or (((a and b) and (c and d))))· -> (a and b and c) or (a and b and c and d)· 常量重叠:
· (a<b and b=c) and a=5· -> b>5 and b=c and a=5· 去除常量条件(由于常量重叠需要):
· (b>=5 and b=5) or (b=6 and 5=5) or (b=7 and 5=6)· -> b=5 or b=6· 索引使用的常数表达式仅计算一次。
对于myisam和heap表,在一个单个表上的没有一个where的count(*)直接从表中检索信息。当仅使用一个表时,对not null表达式也这样做。
无效常数表达式的早期检测。mysql快速检测某些select语句是不可能的并且不返回行。
如果不使用group by或分组函数(count()、min()……),having与where合并。
对于联接内的每个表,构造一个更简单的where以便更快地对表进行where计算并且也尽快跳过记录。
所有常数的表在查询中比其它表先读出。常数表为:
空表或只有1行的表。
与在一个primary key或unique索引的where子句一起使用的表,这里所有的索引部分使用常数表达式并且索引部分被定义为not null。
下列的所有表用作常数表:
mysql> select * from t where primary_key=1;mysql> select * from t1,t2 where t1.primary_key=1 and t2.primary_key=t1.id;尝试所有可能性便可以找到表联接的最好联接组合。如果所有在order by和group by的列来自同一个表,那么当联接时,该表首先被选中。
如果有一个order by子句和不同的group by子句,或如果order by或group by包含联接队列中的第一个表之外的其它表的列,则创建一个临时表。
如果使用sql_small_result,mysql使用内存中的一个临时表。
每个表的索引被查询,并且使用最好的索引,除非优化器认为使用表扫描更有效。是否使用扫描取决于是否最好的索引跨越超过30%的表。优化器更加复杂,其估计基于其它因素,例如表大小、行数和i/o块大小,因此固定比例不再决定选择使用索引还是扫描。
在一些情况下,mysql能从索引中读出行,甚至不查询数据文件。如果索引使用的所有列是数值类,那么只使用索引树来进行查询。
输出每个记录前,跳过不匹配having子句的行。
下面是一些快速查询的例子:
select count(*) from tbl_name; select min(key_part1),max(key_part1) from tbl_name; select max(key_part2) from tbl_name where key_part1=constant; select ... from tbl_name order by key_part1,key_part2,... limit 10; select ... from tbl_name order by key_part1 desc, key_part2 desc, ... limit 10;下列查询仅使用索引树就可以解决(假设索引的列为数值型):
select key_part1,key_part2 from tbl_name where key_part1=val; select count(*) from tbl_name where key_part1=val1 and key_part2=val2; select key_part2 from tbl_name group by key_part1;下列查询使用索引按排序顺序检索行,不用另外的排序:
select ... from tbl_name order by key_part1,key_part2,... ; select ... from tbl_name order by key_part1 desc, key_part2 desc, ... ;7.2.5. 范围优化
7.2.5.1. 单元素索引的范围访问方法
7.2.5.2. 多元素索引的范围访问方法
range访问方法使用单一索引来搜索包含在一个或几个索引值距离内的表记录的子集。可以用于单部分或多元素索引。后面的章节将详细描述如何从where子句提取区间。
7.2.5.1. 单元素索引的范围访问方法
对于单元素索引,可以用where子句中的相应条件很方便地表示索引值区间,因此我们称为范围条件而不是"区间"。
单元素索引范围条件的定义如下:
· 对于btree和hash索引,当使用=、<=>、in、is null或者is not null操作符时,关键元素与常量值的比较关系对应一个范围条件。
· 对于btree索引,当使用>、<、>=、<=、between、!=或者<>,或者like 'pattern'(其中 'pattern'不以通配符开始)操作符时,关键元素与常量值的比较关系对应一个范围条件。
· 对于所有类型的索引,多个范围条件结合or或and则产生一个范围条件。
前面描述的"常量值"系指:
· 查询字符串中的常量
· 同一联接中的const或system表中的列
· 无关联子查询的结果
· 完全从前面类型的子表达式组成的表达式
下面是一些where子句中有范围条件的查询的例子:
select * from t1 where key_col > 1 and key_col < 10; select * from t1 where key_col = 1 or key_col in (15,18,20); select * from t1 where key_col like 'ab%' or key_col between 'bar' and 'foo'; 请注意在常量传播阶段部分非常量值可以转换为常数。
mysql尝试为每个可能的索引从where子句提取范围条件。在提取过程中,不能用于构成范围条件的条件被放弃,产生重叠范围的条件组合到一起,并且产生空范围的条件被删除。
例如,考虑下面的语句,其中key1是有索引的列,nonkey没有索引:
select * from t1 where (key1 < 'abc' and (key1 like 'abcde%' or key1 like '%b')) or (key1 < 'bar' and nonkey = 4) or (key1 < 'uux' and key1 > 'z');key1的提取过程如下:
1. 用原始where子句开始:
2. (key1 < 'abc' and (key1 like 'abcde%' or key1 like '%b')) or
3. (key1 < 'bar' and nonkey = 4) or
4. (key1 < 'uux' and key1 > 'z')
5. 删除nonkey = 4和key1 like '%b',因为它们不能用于范围扫描。删除它们的正确途径是用true替换它们,以便进行范围扫描时不会丢失匹配的记录。用true替换它们后,可以得到:
6. (key1 < 'abc' and (key1 like 'abcde%' or true)) or7. (key1 < 'bar' and true) or8. (key1 < 'uux' and key1 > 'z')9. 取消总是为true或false的条件:
· (key1 like 'abcde%' or true)总是true
· (key1 < 'uux' and key1 > 'z')总是false
用常量替换这些条件,我们得到:
(key1 < 'abc' and true) or (key1 < 'bar' and true) or (false)删除不必要的true和false常量,我们得到
(key1 < 'abc') or (key1 < 'bar')10.将重叠区间组合成一个产生用于范围扫描的最终条件:
11. (key1 < 'bar')总的来说(如前面的例子所述),用于范围扫描的条件比where子句限制少。mysql再执行检查以过滤掉满足范围条件但不完全满足where子句的行。
范围条件提取算法可以处理嵌套的任意深度的and/or结构,并且其输出不依赖条件在where子句中出现的顺序