39. 3. 充分利用 EXPLAIN 和
PROFILING• select_type: SELECT 类型,有以下几种不同的类型
• (1).SIMPLE: 简单的 SELECT (不使用 UNION 或子查询)
• (2).PRIMARY: 最外面的 SELECT ,如果我们使用 UNION 或子查询,第
一个查询将会是这个类型
• (3).UNION: 使用 UNION 查询时,除第一个语句外的所有语句会返回这个
类型
• (4).DEPENDENT UNION: UNION 中的第二个或后面的 SELECT 语句
,取决于外面的查询。
• (5).UNION RESULT: UNION 的结果。
• (6).SUBQUERY: 子查询中的第一个 SELECT 。
• (7).DEPENDENT SUBQUERY: 子查询中的第一个 SELECT ,取决于外
面的查询。
• (8).DERIVED: 衍生表会返回这个类型。如: select * from (select * from
jos_content) as A; 。
• table: 输出引用的表。
40. 3. 充分利用 EXPLAIN 和
PROFILING
• type: 联接类型,从这个选项我们可以初步判断查询效率
,有以下几种不同的类型(按从最佳到最坏排序):
• (1).system: 表中仅有一行记录,这是 const 的一个特例。
• (2).const: 表中最多有一行符合查询条件,它在查询开始时被读取。因为只有一行,这
行的列值可被优化器剩余部分认为是常数。 const 表很快,因为它们只被读取一次!(如
上面的查询)
• (3).eq_ref: 对于每个来自于前面的表的行组合,从该表中读取一行。例如: select * from
A,B where A.id=B.id ,如果 id 在 B 表中是 unique 或 primary key ,会返回这个类型。它是
说对于 A 表中的每一行,在 B 表中读取符合记录的一行。除了 const 之外,这是最好的联接
类型。
• (4).ref: 这个类型跟 eq_ref 类似,不同的是 eq_ref 能根据 unique 或主键在后面的表中选
择出唯一的行,而不能确定唯一行,则使用这个类型。
• (5).ref_or_null: 该联接类型如同 ref ,但是添加了 MySQL 可以专门搜索包含 NULL 值的
行。在解决子查询中经常使用该联接类型的优化。
• (6).index_merge: 索引合并方法用于通过 range 扫描搜索行并将结果合成一个。合并
会产生并集、交集或者正在进行的扫描的交集的并集。在 EXPLAIN 输出中,该方法表现为
type 列内的 index_merge 。在这种情况下, key 列包含一列使用的索引, key_len 包含这些
索引的最长的关键元素。
41. 3. 充分利用 EXPLAIN 和
PROFILING• (7).unique_subquery: unique_subquery 是一个索引查找函数,可以完全替换子查询,
效率更高。 explain select * from jos_content where id in (select id from
jos_categories); 会使用这个类型。
• (8).index_subquery: 该联接类型类似于 unique_subquery 。可以替换 IN 子查询,
但只适合子查询中的非唯一索引。
• (9).range: 只检索给定范围的行,使用一个索引来选择行。 key 列显示使用了哪个
索引。 key_len 包含所使用索引的最长关键元素。在该类型中 ref 列为 NULL 。当使用
= 、 <> 、 > 、 >= 、 < 、 <= 、 IS NULL 、 <=> 、 BETWEEN 或者 IN 操作符,用常
量比较关键字列时,可以使用这个类型。
• (10).index: 这与 ALL 相同,除了只有索引树被扫描。这通常比 ALL 快,因为索引文
件通常比数据文件小。
• (11).all: 对于每个来自于先前的表的行组合,将要做一个完整的表扫描。如果表格是
第一个没标记 const 的表,效果不是很好,并且在所有的其他情况下很差。你可以通过
增加更多的索引来避免 ALL ,使得行能从早先的表中基于常数值或列值被检索出来。
42. 3. 充分利用 EXPLAIN 和
PROFILING
• possible_keys: possible_keys 列指出 MySQL 能使用哪个索引在该表中
找到行。注意,该列完全独立于 EXPLAIN 输出所示的表的次序。这意味着在
possible_keys 中的某些键实际上不能按生成的表次序使用。
• 如果该列是 NULL ,则没有相关的索引。在这种情况下,可以通过检查 WHERE 子句
看是否它引用某些列或适合索引的列来提高你的查询性能。如果是这样,创造一个适当
的索引并且再次用 EXPLAIN 检查查询。
• key: key 列显示 MySQL 实际决定使用的键(索引)。如果没有选择索引,
键是 NULL 。要想强制 MySQL 使用或忽视 possible_keys 列中的索引,在查询中使用
FORCE INDEX 、 USE INDEX 或者 IGNORE INDEX 。对于 MyISAM 和 BDB 表,
运行 ANALYZE TABLE 可以帮助优化器选择更好的索引。对于 MyISAM 表,可以使
用 myisamchk –analyze 。
• key_len: 此列显示 MySQL 决定使用的键长度。如果键是 NULL ,则
长度为 NULL 。注意通过 key_len 值我们可以确定 MySQL 将实际使用一个多部关键字
的几个部分。在不损失精确性的情况下,长度越短越好。
• ref: 此列显示使用哪个列或常数与 key 一起从表中选择行。
• rows: 此列显示了 MySQL 认为它执行查询时必须检查的行数
43. 3. 充分利用 EXPLAIN 和
PROFILING• Extra: 该列包含 MySQL 解决查询的详细信息。
• (1).Distinct: 一旦 MYSQL 找到了与行相联合匹配的行,就不再搜索了。
• (2).Not exists: MYSQL 优化了 LEFT JOIN ,一旦它找到了匹配 LEFT JOIN 标准的
行,就不再搜索了。
• (3).Range checked for each: Record ( index map:# )没有找到理想的索引,因此对于
从前面表中来的每一个行组合, MYSQL 检查使用哪个索引,并用它来从表中返回行。这是
使用索引的最慢的连接之一。
• (4).Using filesort: MYSQL 需要进行额外的步骤来发现如何对返回的行排序。它根据连接
类型以及存储排序键值和匹配条件的全部行的行指针来排序全部行。
• (5).Using index: 列数据是从仅仅使用了索引中的信息而没有读取实际的行动的表返回的
,这发生在对表的全部的请求列都是同一个索引的部分的时候。
• (6).Using temporary: 看到这个的时候,查询需要优化了。这里, MYSQL 需要创建一个
临时表来存储结果,这通常发生在对不同的列集进行 ORDER BY 上,而不是 GROUP BY
上。
• (7).Using where: 使用了 WHERE 从句来限制哪些行将与下一张表匹配或者是返回给用
户。如果不想返回表中的全部行,并且连接类型 ALL 或 index ,这就会发生,或者是查询
有问题。
44. Profiling 的使用
• 要想优化一条 Query ,就须要清楚这条 Query 的性
能瓶颈到底在哪里,是消耗的 CPU 计算太多,还是
需要的 IO 操作太多?要想能够清楚地了解这些信息,在 MySQL
5.0 和 MySQL 5.1 正式版中已经非常容易做到,即通过 Query Profiler 功
能。
• MySQL 的 Query Profiler 是一个使用非常方便的 Query 诊断分析工具,
通过该工具可以获取一条 Query 在整个执行过程中多种资源的消耗情况,
如 CPU 、 IO 、 IPC 、 SWAP 等,以及发生的 PAGE
FAULTS 、 CONTEXT SWITCHE 等,同时还能得到该 Query 执行过程中
MySQL 所调用的各个函数在源文件中的位置。下面看看 Query
Profiler 的具体用法。
55. MYSQL 的索引限制
• 在使用索引的同时,还应该了解 MySQL 中索引存在的限制,以
便在索引应用中尽可能地避开限制所带来的问题。下面列出了
目前 MySQL 中与索引使用相关的限制。
• ( 1 ) MyISAM 存储引擎索引键长度的总和不能超过 1000 字节
;
• ( 2 ) BLOB 和 TEXT 类型的列只能创建前缀索引;
• ( 3 ) MySQL 目前不支持函数索引;
• ( 4 )使用不等于( != 或者 <> )的时候, MySQL 无法使用
索引;
• ( 5 )过滤字段使用了函数运算(如 abs ( column ))
后, MySQL 无法使用索引;
• ( 6 ) Join 语句中 Join 条件字段类型不一致的时候, MySQL
无法使用索引;
• ( 7 )使用 LIKE 操作的时候如果条件以通配符开始
(如 '%abc...' )时, MySQL 无法使用索引;
• ( 8 )使用非等值查询的时候, MySQL 无法使用 Hash 索引
56. 6.ORDER BY 、 GROUP BY 和
DISTINCT 优化
• 1.Order by 的实现与优化:
• 2.Group by 的实现与优化
• 3.Distinct 的实现与优化
57. Order by 的实现与优化:
•
在 MySQL 中, Order by 的实现有两种,一是通过有序的索引直接取得有序的数据
直接返回客户端;二是通过 MySQL 的排序算法进行排序后在将排序后的数据返回到
客户端。
• 经实践证明利用索引实现数据排序的方法是 MySQL 中实现结果集排序的最佳
做法,可以完全避免因为排序计算所带来的资源消耗。所以,在我们优化 Query 语
句中的 ORDER BY 的时候,尽可能利用已有的索引来避免实际的排序计算,可以
很大幅度的提升 ORDER BY 操作的性能 。
• 对于采取排序算法 ,MySQL 有两种方式能够实现
• 1. 取出满足过滤条件的用于排序条件的字段以及可以直接定位到行数据的行
指针信息,在 SortBuffer 中进行实际的排序操作,然后利用排好序之后的数
据根据行指针信息返回表中取得客户端请求的其他字段的数据,再返回给客
户端;
2. 根据过滤条件一次取出排序字段以及客户端请求的所有其他字段的数据,
并将不需要排序的字段存放在一块内存区域中,然后在 Sort Buffer 中将排序
字段和行指针信息进行排序,最后再利用排序后的行指针与存放在内存区域
中和其他字段一起的行指针信息进行匹配合并结果集,再按照顺序返回给客
户端。
• 这两种方式略有不同,能够看出来第二种方式优于第一种方式,第二种方式是
典型的以内存为代价换取效率的提升。
58. Group by 的实现与优化
• 由于 GROUP BY 实际上也同样需要进行排序操作,
而且与 ORDER BY 相比, GROUP BY 主要只是多了
排序之后的分组操作。当然,如果在分组的时候还使
用了其他的一些聚合函数,那么还需要一些聚合函数
的计算。所以,在 GROUP BY 的实现过程中,与
ORDER BY 一样也可以利用到索引。
• 在 MySQL 中, GROUP BY 的实现同样有多种
(三种)方式,其中有两种方式会利用现有的索引信
息来完成 GROUP BY ,另外一种为完全无法使用索
引的场景下使用。下面我们分别针对这三种实现方式
做一个分析。
• 1. 使用松散( Loose )索引扫描实现 GROUP BY
• 2. 使用紧凑( Tight )索引扫描实现 GROUP BY
• 3. 使用临时表实现 GROUP BY
59. Distinct 的实现与优化
•
Distinct 实际上和 Group by 的操作非常相似,只不过是在 Group by 之后
的每组中只取出一条记录而已。所以, Distinct 的实现和 Group by 的实现也
基本差不多,没有太大的区别。同样可以通过松散索引扫描或者是紧凑索引
扫描来实现,当然,在无法仅仅使用索引即能完成
Distinct 的时候, MySQL 只能通过临时表来完成。但
是,和 Group by 有一点差别的是, Distinct 并不需
要进行排序。也就是说,在仅仅只是 Distinct 操作的
Query 如果无法仅仅利用索引完成操作的时
候, MySQL 会利用临时表来做一次数据的“缓”,但
是不会对临时表中的数据进行 filesort 操作。当然,
如果我们在进行 Distinct 的时候还使用了 Group by
并进行了分组,并使用了类似于 max 之类的聚合函数
操作,就无法避免 filesort 了
61. 1. 网络连接与连接线程
• 查看连接线程相关的系统变量的设置值
• MYSQL>show variables like ‘thread%’
• 系统被连接的次数及当前系统中连接线
程的状态值
• Mysql>show status like ‘connections’
• mysql>show status like ‘%thread’
62. 2.TABLE CACHE 的相关优化
• 查看表 CACHE 的设置和当前系统中的
使用状况
• Mysql>show variables like ‘table_cache’
• Mysql>show status like ‘open_tables’
70. QUERY CACHE 优化
• MySQL Cluster 是否可以使用 Query Cache ?可以
使用
其实在我们的生产环境中也没有使用 MySQL Cluster ,
所以我也没有在 MySQL Cluster 环境中使用 Query
Cache 的实际经验,只是 MySQL 文档中说明确实可以
在 MySQL Cluster 中使用 Query Cache 。从 MySQL
Cluster 的原理来分析,也觉得应该可以使用,毕竟
SQL 节点和数据节点比较独立,各司其职,只是 Cache
的失效机制会要稍微复杂一点。
71. MySQL 中优化 sql 语句查询
常用的 30 种方法
• 1. 对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及
order by 涉及的列上建立索引。
2. 应尽量避免在 where 子句中使用 != 或 <> 操作符,否则将引擎放弃
使用索引而进行全表扫描。
3. 应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致
引擎放弃使用索引而进行全表扫描,如:
select id from t where num is null
可以在 num 上设置默认值 0 ,确保表中 num 列没有 null 值,然后这样
查询:
select id from t where num=0
4. 应尽量避免在 where 子句中使用 or 来连接条件,否则将导致引擎
放弃使用索引而进行全表扫描,如:
select id from t where num=10 or num=20
可以这样查询:
select id from t where num=10
union all
select id from t where num=20
72. MySQL 中优化 sql 语句查询
常用的 30 种方法• 5. 下面的查询也将导致全表扫描:
select id from t where name like '%abc%'
若要提高效率,可以考虑全文检索。
6.in 和 not in 也要慎用,否则会导致全表扫描,如:
select id from t where num in(1,2,3)
对于连续的数值,能用 between 就不要用 in 了:
select id from t where num between 1 and 3
7. 如果在 where 子句中使用参数,也会导致全表扫描。因为 SQL 只有在运
行时才会解析局部变量,但优化程序不能将访问计划的选择推迟到运行时;
它必须在编译时进行选择。然而,如果在编译时建立访问计划,变量的值还
是未知的,因而无法作为索引选择的输入项。如下面语句将进行全表扫描:
select id from t where num=@num
可以改为强制查询使用索引:
select id from t with(index( 索引名 )) where num=@num
8. 应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使
用索引而进行全表扫描。如:
select id from t where num/2=100
应改为 :
select id from t where num=100*2
73. MySQL 中优化 sql 语句查询
常用的 30 种方法9. 应尽量避免在 where 子句中对字段进行函数操作,这将导致引擎放弃使用索
引而进行全表扫描。如:
select id from t where substring(name,1,3)='abc'--name 以 abc 开头的 id
select id from t where datediff(day,createdate,'2005-11-30')=0--'2005-11-30' 生成
的 id
应改为 :
select id from t where name like 'abc%'
select id from t where createdate>='2005-11-30' and createdate<'2005-12-1'
10. 不要在 where 子句中的“ =” 左边进行函数、算术运算或其他表达式运算,
否则系统将可能无法正确使用索引。
11. 在使用索引字段作为条件时,如果该索引是复合索引,那么必须使用到该索
引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使
用,并且应尽可能的让字段顺序与索引顺序相一致。
12. 不要写一些没有意义的查询,如需要生成一个空表结构:
select col1,col2 into #t from t where 1=0
这类代码不会返回任何结果集,但是会消耗系统资源的,应改成这样:
create table #t(...)
13. 很多时候用 exists 代替 in 是一个好的选择:
select num from a where num in(select num from b)
用下面的语句替换:
select num from a where exists(select 1 from b where num=a.num)
88. 多表连接
• select document.ID, document.SEC_ID, document.IMP_ID, document.PER_ID,
document.title, document.CREATEDATE, document.STATE, document.TIMENESS,
document.SORT, document.word_no, document.sendunit, document.urgentlevel,
document.word_in_no, document.f?,document.f?, document.sum_i?,
document.sum_i?,document.sum_i?, document.sum_i?, document.templetid, flownode.ID,
flownode.DISCRIPTION, flownode.URGENT,flownode_parent.WORKDATE,
flownode_member.ID, flownode_member.ENTITY, flownode_member.WORKFLAG,
flownode_member.DELFLAG, flownode_member.TRACKFLAG, flownode_member.URGENT,
assess_member.ID,assess_member.prestartdate,assess_member.preenddate,assess_memb
er.relstartdate, assess_member.relenddate, assess_date_type.typename from
flownode_member left join flownode on (flownode.id=flownode_member.flo_id) left join
document on (document.id=flownode_member.doc_id) left join flownode flownode_parent on
(flownode_parent.id=flownode.parent_ID ) left join assess_node on
(assess_node.flownode_id=flownode.ID) left join assess_date_type on
(assess_date_type.id=assess_node.datatype_id) left join assess_member on
(assess_member.flownodemember_id=flownode_member.id ) where
flownode_member.ENTITY in(?,-?) and flownode_member.delflag=? and
flownode_member.workFlag in (?,?,?) and (flownode_member.orgcode=? or
flownode_member.orgcode=? or flownode_member.orgcode=?) and
( (flownode_parent.workFlag=? or flownode.parent_ID=? or (flownode_parent.workFlag=?
and (flownode.isreturn=? or flownode_parent.isreturn=?) ) ) or (flownode.workFlag=? and
flownode_member.workFlag=?) ) and document.SORT=? and (document.state in (?,?) and
sum_i? =? ) and flownode.chooseflag<>? and flownode_member.isdelperson = ? order by