一、索引是对DB优化最有效的方式html
varchar(10)定义的是字符的个数,若是是utf-8的话,最大是3X10个字节mysql
2、索引类型sql
一、MySql的索引是在存储引擎层实现的,各个存储引擎的的索引方式也是不一样的数据库
二、B-Tree索引api
MyISAM索引经过数据的物理位置引用被索引的行(数据存储位置变化时须要更新索引),INNODB则根据主键引用被索引的行(没有主键默则根据默认的策略生成主键)。缓存
B+树是平衡树性能优化
聚簇索引和非聚簇索引的分析:http://www.cnblogs.com/Arlen/articles/1605626.htmlbash
主键索引是聚簇索引,二级索引是非聚簇索引。服务器
INNODB索引的使用网络
一、全值匹配
二、匹配最左前缀
三、匹配列前缀
四、匹配范围值
五、精确匹配某一列并范围匹配另外一列
六、索引覆盖
七、由于索引树的节点是有序的,因此除了按值查找以外,索引还能用于order by和group by操做。
通常,若是B树能够按照某种方式找到值,那么也能按照这种方式排序。
限制:
一、必须匹配最左前缀
二、不能跳过索引中的列
三、若是某个列使用了范围查询,则其右边的全部列都有不能使用索引了。
四、order by 要么都是升序,要么都是降序
五、若是是多个表,只有所有是第一个表才能使用
三、Hash 索引
在MySql里,只有Memory引擎显式的支持hash索引。
哈希索引自身只存储对应的哈希值和行索引,而不存储字段值,因此不能使用索引的值来避免读取行。
哈希不支持部分索引列的查找,只支持所有匹配
哈希索引只支持列的全值匹配,只支持等值比较查询。
INNODB里面内置了自适应hash索引
四、全文索引
五、空间数据索引
六、在INNODB里面模拟hash索引来优化
如url列的内容很大,建立的索引占用的磁盘块数也很大。若是按照url列来等值查找数据(where url=www.yunzhu.com),则下面的优化方法较好:
加一列hash_url,该列存储的是url的MD5值。同时对hash_url建立索引,而后这样查找:where url=www.yunzhu.com and
hash_url=MD5(www.yunzhu.com)
则mysql会优先走hash_url索引,找到对应的行而后比较url是否相同。
原理:hash_url索引占的磁盘块数很小,并且是是整数比较(比字符串比较快不少),因此会很快。
3、高性能索引策略
一、不是独立的列(索引列是表达式或者函数的入参)
二、前缀索引(原理:给索引瘦身)
有时候某个列值是text或者varchar类型,并且值很大。这个时候若是直接对该列建立索引,磁盘块数将会很大,能够采起前缀索引进行优化。
前缀索引:只对某个列的前几位作索引,而不是全部位。没法使用前缀索引进行order by和group by
建立前缀索引语法:alter table mytable add key city(n) 则建立的索引对对city的前n个字符建立索引。
如何选择n
实验找出选择性较高的n。
不能进行索引排序
三、多列索引
问题:index_merge
在每一个列上都建立索引,并不能提升mysql的查询性能。
当出现服务器对多个索引作相交操做的时候(多个and条件),一般意味着须要一个包含全部相关列的索引,而不是多个独立的单列索引。
当服务器出现对多个索引作相交操做的时候(多个or条件),须要消耗大量的CPU资源去重,排序等操做上。
四、选择合适的索引列顺序(下面的两种条件都须要综合考虑)
一、在不考虑排序和分组的时候,将选择性较高的列放在前面一般是很好的。
考虑下面的一种极端的状况:
虽然列A的选择性很高,可是A的列有个值B重复率很大,若是where A=B来查询的话,则效率会很低。此时应该由上层的程序来控制不走这个索引。
极端的状况下B值的重复lv很高,高到和不走索引是同样的。
二、须要根据运行频率最高的查询来调整索引列的顺序。
3、聚簇索引
不是一种索引方式而是一种存储方式,表明主键和数据紧凑的存储在一块儿。思考下聚簇索引的优势和缺点:
优势:一、覆盖索引查询能够直接使用主键值
二、由于索引和数据存储在同一个树上,所以查找快。
三、聚簇
缺点:一、主键须要递增,不然会形成页分裂和碎片等。
MySql的数据文件就是索引文件,非叶子节点存储的是主键,叶子节点存储了所有的数据。
非聚簇索引也叫辅助索引。查询的时候通常是先根据辅助索引查询到主键以后再根据聚簇索引查询到全部的数据行。
若是没有定义主键,INNODB会选择一个惟一的非空索引代替。若是没有这样的索引,INNODB会隐式的定义一个主键做为聚簇索引。
聚簇索引的插入速度严重依赖插入顺序。
更新聚簇索引的成本好高,由于须要将索引移到新的位置。
基于聚簇索引的表在插入新行,或者主键被更新致使须要移动行的时候,可能致使列分裂的问题。
使用optimize table命令来重建表并进行优化页的填充。
INNODB应该尽量的按照主键的顺序插入数据,而且尽量的使用单调增长的聚簇值来插入新行,避免更新主键值。 (不然须要移动聚簇索引,同时形成页分裂,形成碎
片)。
4、索引覆盖
若是二级索引可以覆盖查询,则能够避免对主键索引的二次查询
在发起一个覆盖查询的时候,explain的extrea列能够看到using index的信息
不少的查询语句能够经过部分走索引覆盖而进行优化。
能够经过扩展索引来实现索引覆盖从而进行优化
5、使用索引排序
若是explain的type值为index,则说明mysql可能使用了索引扫描来排序。
MySql可使用同一个索引既知足排序,也能用于查找行。
只有当索引的列顺序和order by自居的顺序彻底一致,而且全部的列的排序方向都一致时,MySql才能使用索引来对结果进行排序。
若是查询须要关联多个表,只有当order by子句引用的字段所有为第一个表时(优化执行器选择执行的第一个表,不是sql的第一个select表),才能使用索引作排序。
order by子句和查找型查询的限制是同样的:须要知足索引的最左前缀要求,不然mysql都须要执行排序操做,而没法利用索引排序。
6、 kengyu索引
有时候坑与索引能在必定的程度上优化查询。
7、链接查询
Inner Join
Natural Join
Left Outer Join
Right Outer Join
Full Outer Join
Cross Join
8、查询性能优化
一、 主要是两个方面进行性能优化
一、客户端到服务器端之间的性能优化(减小数据包的传输等)
一、客户端是否请求了过多的行和列(列少的话可能会走覆盖索引,同时能减少数据包的大小)
二、客户端能够先查缓存减小查db的次数
二、mysql服务器端和存储引擎端的优化
主要是根据3个指标
一、响应时间(请求锁时间,排队时间,IO时间)
二、扫描的行数和返回的行数(通常这个比例是10:1甚至更大)
二、where条件的利用好坏,从好到坏排序
一、where用于索引
二、服务器层作过滤
三、切分查询(讲一个大的查询切分红小的)
为何切分查询
一、如今的网络带宽比较大,因此将一个查询切分红多个是可行的
二、一次执行大量数据的删除和查询,会一次占用不少的资源和锁,给DB形成很大的压力。这样作能够将DB的压力分散到不一样的时间。
三、将一个大的查询(如:链接查询)切分红小的查询能更好的利用缓存和操做缓存。
四、在应用层作关联,能够更好的对 数据库进行切分,更容易作到高性能和可扩展。
五、在应用层作关联查询,在一个事务中,能够把某个请求结果存储起来,而后再找个事务的执行过程当中就不断的重复去DB查了,这样就减小了请求DB的次数。
如:若是直接请求mysql执行一个个大sql,返回的结果是没有子查询的结果的,若是下次须要子查询的结果还须要如DB查。
四、客户端/服务器端的通讯协议
一、半双工,这致使两方对通信过程不能有很好的控制能力,在发送出去报文以后就只能干等着了。
二、客户端用一个单独的数据包将查询发送给服务器。max_allowed_packet就规定了这个数据包的大小,客户端发送完请求以后只能等着了。
三、服务器端的响应由多个数据包组成,客户端只有接收到完整的所有结果以后,服务器端才会释放资源。
四、客户端默认是先缓存所有的结果集,使得mysql服务器端能尽快的释放掉资源。客户端能够经过参数决定本身是否是缓存所有的结果集。jdbc也有api能设置。
五、服务器获得第一条数据的时候就能够返回数据了,这样客户端也能够同时处理数据了。服务器不是先查询到全部的数据而后才返回数据,是查询到第一条有效数据就返回数
据,这样能够避免即便服务器端须要返回大量的数据,也不会占用大量的内存。mysql会针对每一行使用通信协议进行包装而后写到socket,固然tcp可能会把多行以一个批次传输。问题:客户端能够指定是否缓存服务器端的数据,那么这个时候服务器端返回数据是否是就
受到限制了?
SQL_BUFFER_RESULT forces the result to be put into a temporary table. This helps MySQL free the table locks early and helps in cases where it takes a long time to send the result set to the client. This option can be used only for top-level SELECT statements, not for subqueries or following UNION. 这样能尽快的释放锁,可是会占用mysql的大量内存和资源 |
9、其余
一、mysql在生成执行计划的时候,5.6版本以前若是一个语句里面含有子查询时,子查询是须要执行的,可是5.6解除了这个限制。
二、当扫描大量的行数时,能够采起下面的措施(p201)
一、使用索引覆盖
二、使用汇总表
三、重写查询
三、查看各个链接的状态
show full processlist
在command列能看到各个查询如今的状态
一、sleep
二、query
三、locked
四、analysing and statistics
五、copying to tmp table[on disk]
group by,sort,union的时候会使用临时文件
六、sorting result
七、sending data
10、explain和explain extend
explain extand能额外的查到mysql执行优化器优化以后的sql语句
一、type(访问类型)
all 聚簇索引去扫描整个表(limit时不会扫描整个表),此时磁盘是顺序访问的。
一、只有聚簇索引 二、不能走索引覆盖
index 索引扫描整个表(使用索引排序的时候会走)
一、extra列里面有using index时只是磁盘顺序读取全部的索引,并不会再根据聚簇索引去磁盘随机读取其余列的数据
二、extra列里面没有using index时,此时还须要根据聚簇索引去随机读取其余列,比较好性能。
range
使用索引范围查询
IN,OR也是显示的range,可是和正常的>这种范围查询有区别
ref(非惟一性索引或者使用索引前缀查询)
索引等值查询,可是可能查询多多行
eq_ref(惟一性索引,例如主键id)
索引等值查询,可是只可能查询到一条语句。
const,system
不用执行能根据SQL和mysql统计信息就能获得结果,如:select id from XXX where id=1;
NULL
不用执行就能获得结果
分析:all和index?
all和index都是扫表。当是索引覆盖查询,或者能利用索引排序的时候才会走
二、rows
大概显示会扫描到多少行(若是索引覆盖查询的话,一个索引也算一行,因此这里rows指的不是扫描行同时获得全部的列)
rows也不能代表limit,如select * from XX limit 1,实际上mysql只会查询扫描一列就结束。
三、key_len(所使用索引的最大程度)
索引的长度,能根据这个值肯定最终sql在联合索引里面到底使用了前几个列走索引
四、possible_keys
可能走的索引,具体的例子见案例分析的第6个
五、key
优化器从possible_keys里最终选择的索引(根据IO次数选择合适的,具体的例子见案例分析的第6个)
六、id
根据sql里面的id的顺序由1递增排列
七、explain的执行结果顺序
explain的执行结果里面的id可能不是递增的,explain显示的列的顺序就是mysql的执行顺序。
八、select_type
一、simple
二、SUBQUERY(DEPENDENT_SUBQUERY)
select (select XX from XX) from XX
括号里面的是SUBQUERY
三、UNION
四、DERIVED
from (select XXX from XXX) 括号里面的是DERIVED
五、PRIMARY
最外层循环
九、table
一、DERIVED
二、真实表
三、UNION以后的表
<UNION 1,2>
十、extra
using index (说明只使用该索引,不用再根据索引得到的id去查询聚簇索引得到数据,也就是索引覆盖)
,using where (不带where的也可能出现using where,说明查询可收益于不一样的索引)
using tempory(临时表)
using filesore(多是在内存或者磁盘进行排序)
11、案例分析
一、status命令
能查看mysql的版本号和服务器的运行快照信息,以及mysql客户端的信息
二、show create table;
查看建表语句
三、索引覆盖案例
表结构:
CREATE TABLE `test` ( `id` int(11) NOT NULL, `name` varchar(10) DEFAULT NULL, PRIMARY KEY (`id`), KEY `name` (`name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8
select * from test;
执行结果以下:
能够看出执行优化器选择了索引覆盖查询。
注意:任何的索引默认主键都是索引的一部分
当sql没有指定order by的时候,查询出的结果是无序的。
select * from test order by id;
执行结果以下:
能够看出优化器选择了使用索引排序
四、聚簇索引和不是聚簇索引的选择
select count(*) from test;
能够看出count(*)也是扫描整个索引
count(*)由于只用统计数据,确定走索引覆盖(聚簇索引或者非聚簇索引)。
selct count(*)和select a,b基本相似,可是count(*)通常能够走索引覆盖,extra 确定包含using index。
CREATE TABLE `AA` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', `gmt_create` datetime NOT NULL COMMENT '建立时间', `gmt_modified` datetime NOT NULL COMMENT '修改时间', `appkey` varchar(32) NOT NULL COMMENT , `std_api_id` varchar(64) NOT NULL COMMENT , `cfg_h_group` varchar(128) NOT NULL COMMENT , `cfg_key` varchar(128) NOT NULL COMMENT , `cfg_value` varchar(300) NOT NULL COMMENT '配置项value', `creator` varchar(32) NOT NULL COMMENT '建立人', `modifier` varchar(32) NOT NULL COMMENT '修改人', `cfg_v_group` varchar(32) NOT NULL COMMENT , PRIMARY KEY (`id`), KEY `index_appkey_std_api_id` (`appkey`,`std_api_id`), KEY `index_appkey_h_group` (`appkey`,`cfg_h_group`) ) ENGINE=InnoDB AUTO_INCREMENT=490004 DEFAULT CHARSET=utf8 COMMENT=''
explain select count(*) from AA
该表存在聚簇索引还有两个联合索引,由于确定要所有扫描全部的行(但不必定是全部的列),优化执行器会比较怎么扫描读的磁盘块数(IO数)更少。
一、聚簇索引:叶子部分存储的是所有的列,因此pass。因此通常count(*)都不会走聚簇索引扫描
二、联合索引 选择联合索引数据量较小的,因此最后优化执行器选择了下面的执行计划
1
|
|
1 |
2
|
|
SIMPLE |
3
|
|
AA |
4
|
|
index |
5
|
|
6
|
|
index_appkey_std_api_id |
7
|
|
292 |
8
|
|
9
|
|
61181 |
10
|
|
Using index |
六、索引的选择
explain select count(*) from AA where appkey='aa';
从执行计划能够看出,优化执行器锁定两个索引(从possible_keys上看出),可是两个联合索引都只能选择使用第一列,因此在两个联合索引中选择索引占的数据较小的那个索
引,最终生成了下面的执行计划
1
|
|
1 |
2
|
|
SIMPLE |
3
|
|
AA |
4
|
|
ref |
5
|
|
index_appkey_std_api_id,index_appkey_h_group |
6
|
|
index_appkey_std_api_id |
7
|
|
98 |
8
|
|
const |
9
|
|
1 |
10
|
|
Using where; Using index |
七、 select (select XX from XX) from XX类型分析
返回的行数? 等于select count(*) from XX;
select (select id from s) from test;
本质上是个循环嵌套查询
八、select常量
返回多少行数? select count(*) from XX;
从执行计划能够看出,这种sql确定走索引覆盖(using index,由于查的是常量)。分析这种sql时和普通的sql同样对待,只是查的列比较特殊,确定走索引覆盖
九、链接查询(只支持左外链接,右外链接,内链接(等值链接))
虽然没有 join 关键字,可是下面的查询也是链接查询。
下面的查询没有where语句,实际是获得一个笛卡尔乘积,执行引擎在执行的时候是递归执行,随意因此外层循环的数量尽量少,因此查执行table s,而后再执行table
test。table S只有主键,因此type是all。table test能够进行索引覆盖,因此type是index。
十、虽然select多是所有数据,或者部分数据可是联合索引能作到覆盖查询,并且可使用索引排序。
十一、IN查询
注意:using where
十二、derived深刻分析
一、生成derived表的查询是能够走索引的
二、primary对derived的查询时不能走索引的,只能走where。
12、具体的优化
一、limit分页
limit 10000,10,此时mysql须要读取不少的行去得到偏移量为10000的数据,可是以前读取过的10000个数据是无用的。
方案1:
select film_id,description from sakila.film order by title limit 50,5
能够看出须要全表扫描,而后再根据title排序,而后在取偏移量为50的5条数据。性能弱爆了。
优化成:select film.film_id,film.dscription from sakila.film inner join (select film_id from sakila.film order by title limit 50,5)as lim using(fim_id);
mysql虽然查的前50个没有用,可是由于using index,只是浪费了50次查一个索引,没有查到一个索引获得主键再去查全部的数据。这里实际上用了延迟技术。
方案2:
想办法使得limit A,B中的A变小
二、UNION
UNION确定会使用临时表存放下来而后再发送数据给客户端,而其实是能够直接发送数据给客户端的。
通常尽可能使用UNION ALL,不然mysql须要对UNION的结果排除重复的
三、最大值最小值
优化方法:由于最大值和最小值只多是一个值,因此可使用limit1来进行优化。
极端的状况下会进行扫表而后才获得一个最大或者最小值,经过改写SQL能改变这种状况。
查索引上的最大值和最小值时,能够直接获得。
四、优化count查询
一、count(A),其中A能够是表达式,能够是列,也能够是*。*表明查询所有的行数,列表明查询非NULL值的个数,表达式表明有值的个数。
二、MYISAM在查询不带where条件的count(*)时是const类型,可是若是带where或者count(A)不能转换成count(*)和其余的引擎的执行同样。
在MYISAM:select count(*) from a where id>5;则执行时会扫描不少的行数。能够优化成:
select count(*) from a - select count(*) from a where id<=5;
三、使用近似值,汇总表等
五、优化链接查询
一、A和B链接时,只须要在一个表上创建索引就能够了。
二、确保group by 和order by 中的表达式只涉及到一个表中的列,只有这样mysql才能使用索引优化这个过程
六、优化group by和distinct
当没有办法使用索引的时候,mysql会使用临时表或者文件排序来分组。
使用分组的时候select的列尽可能是和group by 有关的字段。
十3、其余
一、建议执行优化器
SE INDEX
在你查询语句中表名的后面,添加 USE INDEX 来提供你但愿 MySQ 去参考的索引列
表,就可让 MySQL 再也不考虑其余可用的索引。
Eg:SELECT * FROM mytable USE INDEX (mod_time, name) ...
IGNORE INDEX
若是你只是单纯的想让 MySQL 忽略一个或者多个索引,可使用 IGNORE INDEX 做
为 Hint。
Eg:SELECT * FROM mytale IGNORE INDEX (priority) ...
FORCE INDEX为强制 MySQL 使用一个特定的索引,可在查询中使用 FORCE INDEX 做为 Hint。Eg:SELECT * FROM mytable FORCE INDEX (mod_time) ...