MySQL 多表查询优化问题

[复制链接]
查看11 | 回复4 | 2007-4-1 00:00:00 | 显示全部楼层 |阅读模式
现有表A,表B两张表,表A有几百条记录,表B有大约20万条记录。表B的superid字段外键关联到表A的ID字段。
假设表B的数据主要集中在2007.04.01到2007.04.09之间,对B表的time字段已经建立索引,两表的type字段也建立了索引。
现在要对表B进行查询,使用如下语句:
select b.id,b.time,b.data, a.name
from A a, B b
where b.type=1 and a.type=1
and b.time between '2007-04-01 00:00:00' and '2007-04-09 23:59:59'
and b.superid=a.id
order by b.time desc
#limit 0,100
和使用语句
selectb.id,b.time,b.data, a.name
from B b
join (
select id, name from A where type=1
) as a
on (a.id=b.superid)
where b.type=1
and b.time between '2007-04-01 00:00:00' and '2007-04-09 23:59:59'
order by b.time desc
#limit 0,100
两个语句查询结果为11万多条记录(注释掉limit)。
尝试使用以上两句查询语句后,使用MySQL-Front工具运行,提示前者需要8秒以上,第二句需要6秒左右时间。觉得表的总记录数只有20万,查询的结果为11万,就需要这么长时间,应该有很大的优化余地,但不知道从何优化起,请各位大侠指点,谢谢!!
回复

使用道具 举报

千问 | 2007-4-1 00:00:00 | 显示全部楼层
7.2.2. 估计查询性能
在大多数情况下,可以通过计算磁盘搜索来估计性能。对小的表,通常能在1次磁盘搜索中找到行(因为索引可能被缓存)。对更大的表,可以使用B-树索引进行估计,将需要log(row_count)/log(index_block_length/3 * 2/(index_length + data_pointer_length))+1次搜索才能找到行。
在MySQL中,索引块通常是1024个字节,数据指针通常是4个字节,这对于有一个长度为3(中等整数)的索引的500,000行的表,通过公式可以计算出log(500,000)/log(1024/3*2/(3+4))+1= 4次搜索。
上面的索引需要大约500,000 * 7 * 3/2 = 5.2MB,(假设典型情况下索引缓存区填充率为2/3),可以将大部分索引保存在内存中,仅需要1-2调用从OS读数据来找出行。
然而对于写,将需要4次搜索请求(如上)来找到在哪儿存放新索引,并且通常需要2次搜索来更新这个索引并且写入行。
注意,上述讨论并不意味着应用程序的性能将缓慢地以logN 退化!当表格变得更大时,所有内容缓存到OS或SQL服务器后,将仅仅或多或少地更慢。在数据变得太大不能缓存后,将逐渐变得更慢,直到应用程序只能进行磁盘搜索(以logN增加)。为了避免这个问题,随数据增加而增加 键高速缓冲区大小。对于MyISAM表, 由key_buffer_size系统变量控制 键高速缓冲区大小。参见7.5.2节,“调节服务器参数”。
7.2.3. SELECT查询的速度
总的来说,要想使一个较慢速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>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_nameWHERE key_part1=constant; SELECT ... FROM tbl_nameORDER BY key_part1,key_part2,... LIMIT 10; SELECT ... FROM tbl_nameORDER 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_nameWHERE key_part1=val1 AND key_part2=val2; SELECT key_part2 FROM tbl_name GROUP BY key_part1;下列查询使用索引按排序顺序检索行,不用另外的排序:
SELECT ... FROM tbl_nameORDER BY key_part1,key_part2,... ; SELECT ... FROM tbl_nameORDER 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索引,当使用>、=、,或者LIKE 'pattern'(其中 'pattern'不以通配符开始)操作符时,关键元素与常量值的比较关系对应一个范围条件。
· 对于所有类型的索引,多个范围条件结合OR或AND则产生一个范围条件。
前面描述的“常量值”系指:
· 查询字符串中的常量
· 同一联接中的const或system表中的列
· 无关联子查询的结果
· 完全从前面类型的子表达式组成的表达式
下面是一些WHERE子句中有范围条件的查询的例子:
SELECT * FROM t1 WHERE key_col > 1 AND key_col'z');key1的提取过程如下:
1.用原始WHERE子句开始:
2.(key1'z')
5.删除nonkey = 4和key1 LIKE '%b',因为它们不能用于范围扫描。删除它们的正确途径是用TRUE替换它们,以便进行范围扫描时不会丢失匹配的记录。用TRUE替换它们后,可以得到:
6.
(key1'z')9.取消总是为true或false的条件:
· (key1 LIKE 'abcde%' OR TRUE)总是true
· (key1'z')总是false
用常量替换这些条件,我们得到:
(key1 或者IS NULL比较操作符之一,条件包括所有索引部分。(也就是说,有N 个条件,每一个对应N-元素索引的每个部分)。
关于常量的定义,参见7.2.5.1节,“单元素索引的范围访问方法”。
例如,下面为三元素HASH索引的范围条件:
key_part1 = 1 AND key_part2 IS NULL AND key_part3 = 'foo'· 对于BTREE索引,区间可以对结合AND的条件有用,其中每个条件用一个常量值通过=、、IS NULL、>、=、、BETWEEN或者LIKE 'pattern' (其中'pattern'不以通配符开头)比较一个关键元素。区间可以足够长以确定一个包含所有匹配条件(或如果使用或!=,为两个区间)的记录的单一的关键元组。例如,对于条件:
·
key_part1 = 'foo' AND key_part2 >= 10 AND key_part3 > 10单一区间为:
('foo',10,10)5)区间为:
(1, -inf) = 1 AND key_part2 = 1 AND key_part2 IS NOT NULL7.2.5.1节,“单元素索引的范围访问方法”描述了如何进行优化以结合或删除单元素索引范围条件的区间。多元素索引范围条件的区间的步骤类似。
7.2.6. 索引合并优化
7.2.6.1. 索引合并交集访问算法
7.2.6.2. 索引合并并集访问算法
7.2.6.3. 索引合并排序并集访问算法
索引合并方法用于通过range扫描搜索行并将结果合成一个。合并会产生并集、交集或者正在进行的扫描的交集的并集。
在EXPLAIN输出中,该方法表现为type列内的index_merge。在这种情况下,key列包含一列使用的索引,key_len包含这些索引的最长的关键元素。
例如:
SELECT * FROM tbl_name WHERE key_part1 = 10 OR key_part2 = 20;

SELECT * FROM tbl_name
WHERE (key_part1 = 10 OR key_part2 = 20) AND non_key_part=30;

SELECT * FROM t1, t2
WHERE (t1.key1 IN (1,2) OR t1.key2 LIKE 'value%')
AND t2.key1=t1.some_col;

SELECT * FROM t1, t2
WHERE t1.key1=1
AND (t2.key1=t1.some_col OR t2.key2=t1.some_col2);

索引合并方法有几种访问算法 (参见EXPLAIN输出的Extra字段):
· 交集
· 联合
· 排序并集
后面几节更加详细地描述了这些方法。
注释:索引合并优化算法具有以下几个已知缺陷:
· 如果可以对某些关键字进行范围扫描,则不考虑索引合并。例如,下面的查询:
·
SELECT * FROM t1 WHERE (goodkey110 OR key_col2 = 20) AND nonkey_col=30;排序联合算法和联合算法的区别是排序联合算法必须先索取所有记录的行ID,然后在返回记录前对它们进行排序。
回复

使用道具 举报

千问 | 2007-4-1 00:00:00 | 显示全部楼层
学习!
回复

使用道具 举报

千问 | 2007-4-1 00:00:00 | 显示全部楼层
都是大侠啊,好久没学习了,都快忘了基本语法!以前学的是sql, my sql应该基本的语法是相同的吧~
回复

使用道具 举报

千问 | 2007-4-1 00:00:00 | 显示全部楼层
个人见解:
1、在A表对id建索引,在B表对superid建索引
2、B表对type建的索引效果不大,20W的数据,type=1的就有11W.
3、where b.type = 1 and a.type =1改写成 where b.type = a.type and b.type = 1
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

主题

0

回帖

4882万

积分

论坛元老

Rank: 8Rank: 8

积分
48824836
热门排行