Explain
模拟优化器执行SQL查询语句,从而知道MySQL是如何处理你的SQL语句的。分析你的查询语句或是表结构的性能瓶颈。
意义
|
字段含义
字段 | 意义 |
Id | Id相同由上向下执行,子查询id递增,id不同id值越大执行优先级越高 |
Select_type | SIMPLE 简单的select查询,查询中不包含子查询或者UNION
PRIMARY 查询中若包含任何复杂的子部分,最外层查询则被标记为PRIMARY
SUBQUERY 在SELECT或WHERE列表中包含了子查询 DERIVED 在FROM列表中包含的子查询被标记为DERIVED(衍生),MySQL会递归执行这些子查询,把结果放在临时表中 UNION 若第二个SELECT出现在UNION之后,则被标记为UNION:若UNION包含在FROM子句的子查询中,外层SELECT将被标记为:DERIVED UNION RESULT 从UNION表获取结果的SELECT |
Type | 执行类型及顺序:system > const > eq_ref > ref > range > index > all 一般来说,得保证查询至少达到range级别,最好能达到ref。
eq_ref 唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配。常见于主键或唯一索引扫描 ref 非唯一性索引扫描,返回匹配某个单独值的所有行,本质上也是一种索引访问,它返回所有匹配某个单独值的行,然而,它可能会找到多个符合条件的行,所以他应该属于查找和扫描的混合体。
|
possible_keys | 显示可能应用在这张表中的索引,一个或多个。查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询实际使用 |
Key | 实际使用的索引,如果为NULL,则没有使用索引 |
key_len | 表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度,在 |
ref | 显示索引的那一列被使用了,如果可能的话,最好是一个常数。哪些列或常量被用于查找索引列上的值 |
rows | 根据表统计信息及索引选用情况,大致估算出找到所需的记录所需要读取的行数,也就是说,用的越少越好 |
Extra | 包含不适合在其他列中显式但十分重要的额外信息 Using filesort:说明mysql会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行读取。MySQL中无法利用索引完成的排序操作称为“文件排序”。 Using temporary:使用了用临时表保存中间结果,MySQL在对查询结果排序时使用临时表。常见于排序order by和分组查询group by。 Using index:表示相应的select操作中使用了覆盖索引(Covering Index),避免访问了表的数据行,效率不错。如果同时出现using where,表明索引被用来执行索引键值的查找;如果没有同时出现using where,表明索引用来读取数据而非执行查找动作。 Using where: 表明使用了where过滤 Using join buffer: 表明使用了连接缓存,比如说在查询的时候,多表join的次数非常多,那么将配置文件中的缓冲区的join buffer调大一些。 impossible where: where子句的值
在没有GROUPBY子句的情况下,基于索引优化MIN/MAX操作或者对于MyISAM存储引擎优化COUNT(*)操作,不必等到执行阶段再进行计算,查询执行计划生成的阶段即完成优化。 Distinct:
|
索引
概念及分类
一般来说,索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储在磁盘上。我们平常所说的索引,如果没有特别指明,都是B树(多路搜索树,并不一定是二叉树)结构组织的索引。其中,聚集索引、复合索引、前缀索引、唯一索引默认都是使用B+树索引,统称索引。当然,除了B+树这种类型的索引之外,还有哈希索引(hash index)、全文索引(full-text)、R-Tree索引。
组合索引
指多个字段上创建的索引,只有在查询条件中使用了创建索引时的第一个字段,索引才会被使用。使用组合索引时遵循最左前缀集合
范例:ALTER TABLE `table` ADD INDEX name_city_age (name,city,age);
需要创建索引的情况
1.主键自动建立唯一索引
2.频繁作为查询条件的字段应该创建索引
3.查询中与其它表关联的字段,外键关系建立索引
4.频繁更新的字段不适合创建索引(因为每次更新都需要更新索引)
5.where条件里用不到的字段不创建索引
6.单键/组合索引的选择问题(在高并发下倾向创建组合索引)
7.查询中排序的字段,排序字段若通过索引去访问将大大提高排序速度
8.查询中统计或者分组字段
不要创建索引的情况
- 表记录太少
- 经常增删改的表
- 数据重复且分布平均的表字段,因此应该只为最经常查询和最经常排序的数据列建立索引(如sex)
JOIN语句的优化
1.尽可能减少join语句中的NestedLoop的循环总次数:“永远用小结果集驱动大的结果集”。
优先优化NestedLoop的内层循环
2.保证join语句中被驱动表上Join条件字段已经被索引
3.当无法保证被驱动表的JOIN条件字段被索引且内存资源充足的前提下,不要太吝啬JoinBuffer的设置。
索引优化之索引失效
ALTER TABLE staffs ADD INDEX idx_staffs_nameAgePos (NAME, age, pos);
1. 全值匹配指的是我要查询的语句的字段和顺序恰好和建立的索引的字段和顺序一致,否则索引失效
EXPLAIN SELECT * FROM staffs WHERE NAME = 'July' AND age = 25 AND pos = '实习生';(不失效)
EXPLAIN SELECT * FROM staffs WHERE age = 25 AND NAME = 'July' AND pos = '实习生';(失效)
2. 如果索引了多列,要遵守最左前缀法则,指的是查询从索引的最左前列开始并且不跳过索引中的列
EXPLAIN SELECT * FROM staffs WHERE NAME = 'July' AND pos = '实习生';(跳过age索引,索引pos索引失效)
3. 不在索引列上做任何操作这里的任何操作包括计算、函数、(自动or手动)类型转换,会导致索引失效而转向全表扫描
4. 存储引擎不能使用索引中的范围条件右边的列
EXPLAIN SELECT * FROM staffs WHERE NAME = 'July' AND age > 25 AND pos = '实习生';(age>25)导致age和pos索引失效
5. 尽量使用索引的查询即索引列和查询列一致,减少select *
EXPLAIN SELECT name,age,pos FROM staffs WHERE NAME = 'July' AND age = 25 AND pos = '实习生';
6. mysql在使用不等于(!=或者<>)的时候无法使用索引导致全表扫描
EXPLAIN SELECT * FROM staffs WHERE name != 'July';(name索引失效)
7. is null,is not null也无法使用索引
EXPLAIN SELECT * FROM staffs WHERE name is null;
8. like以通配符开头(’%abc…’)索引失效会变成全表扫描
EXPLAIN SELECT * FROM staffs WHERE name like '%July'(name索引失效)
通过覆盖索引解决:
EXPLAIN SELECT id,name,age FROM staffs WHERE name like '%July%'
9. 字符串不加单引号索引失效
SELECT * FROM staffs WHERE name = 200;(name为varchar,使用时先做类型转换,导致全表扫描,索引失效)
10. 少用or,用它来连接时会索引失效
EXPLAIN SELECT * FROM staffs WHERE name = 'July' or age = 2
索引优化口诀
全值匹配我最爱,最左前缀要遵守;
带头大哥不能死,中间兄弟不能断;
索引列上少计算,范围之后全失效;
LIKE百分写最右,覆盖索引不写星;
不能空值还有OR,索引失效要少用;
VAR引号不可丢,SQL高级也不难;
查询优化
- 永远小表驱动大表,exitst代替in
- order by子句,尽量使用index方式排序,避免使用filesort方式排序,增大sort_buffer-size参数的设置,增大max_length_for_sort_data参数的设置,为排序使用索引
- groupby 先排序后分组,遵循索引键的最佳左前缀原则,where高于having,能写在where限定的条件就不要去having限定了
- 设置慢查询日志分析慢查询语句,当然druid的数据库监控功能可替代此功能,结合使用explain进行sql优化
锁机制
锁的分类
- 从对数据库操作的类型分,分为读锁和写锁
读锁(共享锁):针对同一份数据,多个读操作可以同时进行而不会互相影响
写锁(排它锁):当前写操作没有完成前,它会阻断其他写锁和读锁
在MyISAM引擎下,读锁会阻塞写,但是不会阻塞读。而写锁则会把读和写都阻塞
- 从对数据操作的粒度分,分为表锁和行锁
表锁:
表锁偏向MyISAM存储引擎,开销小,加锁快,无思索,锁定粒度大,发生锁冲突的概率最高,并发度最低
行锁:
行锁偏向InnoDB存储引擎,开销大,加锁慢,会出现死锁,锁定粒度最小,发生锁冲突的概率最低,并发度也最高。InnoDB与MYISAM的最大不同有两点:一是支持事务(TRANSACTION);二是采用了行级锁。
行锁事物
✈ 更新丢失(Lost Update)
当两个或多个事务选择同一行,然后基于最初选定的值更新该行时,由于每个事务都不知道其他事务的存在,就会发生丢失更新问题–最后的更新覆盖了由其他事务所做的更新。
✈ 脏读(Dirty Reads)
一个事务正在对一条记录做修改,在这个事务完成并提交前,这条记录的数据就处于不一致的状态;这时,另一个事务也来读取同一条记录,如果不加控制,第二个事务读取了这些“脏”数据,并据此作进一步的处理,就会产生未提交的数据依赖关系。这种现象被形象的叫做“脏读”。
一句话:事务A读取到了事务B已经修改但尚未提交的数据,还在这个数据基础上做了操作。此时,如果B事务回滚,A读取的数据无效,不符合一致性要求。
✈ 不可重读(Non-Repeatable Reads)
一个事务在读取某些数据后的某个时间,再次读取以前读过的数据,却发现其读出的数据已经发生了改变、或某些记录已经被删除了!这种现象就叫做“不可重复读”。
一句话:事务A读取到了事务B已经提交的修改数据,不符合隔离性
✈ 幻读(Phantom Reads)
一个事务按相同的查询条件重新读取以前检索过的数据,却发现其他事务插入了满足其查询条件的新数据,这种现象就称为“幻读”。
一句话:事务A读取到了事务B提交的新增数据,不符合隔离性
脏读是事务B里面修改了数据
幻读是事务B里面新增了数据
事物隔离级别
锁优化建议
- 尽可能让所有数据检索都通过索引来完成,避免无索引行锁升级为表锁
- 合理设计索引,尽量缩小锁的范围
- 尽可能减少检索条件,避免间隙锁
- 尽量控制事务大小,减少锁定资源量和时间长度
- 尽可能低级别事务隔离
读写分离方案
Mycat