本文由云+社区发表
文章《MySQL查询分析》讲述了使用MySQL慢查询和explain命令来定位mysql性能瓶颈的方法,定位出性能瓶颈的sql语句后,则须要对低效的sql语句进行优化。本文主要讨论MySQL索引原理及经常使用的sql查询优化。javascript
前面的案例中,c2c_zwdb.t_file_count表只有一个自增id,FFileName字段未加索引的sql执行状况以下:前端
在上图中,type=all,key=null,rows=33777。该sql未使用索引,是一个效率很是低的全表扫描。若是加上联合查询和其余一些约束条件,数据库会疯狂的消耗内存,而且会影响前端程序的执行。java
这时给FFileName字段添加一个索引:mysql
alter table c2c_zwdb.t_file_count add index index_title(FFileName);算法
再次执行上述查询语句,其对比很明显:sql
在该图中,type=ref,key=索引名(index_title),rows=1。该sql使用了索引index_title,且是一个常数扫描,根据索引只扫描了一行。数据库
比起未加索引的状况,加了索引后,查询效率对比很是明显。数据结构
经过上面的对比测试能够看出,索引是快速搜索的关键。MySQL索引的创建对于MySQL的高效运行是很重要的。对于少许的数据,没有合适的索引影响不是很大,可是,当随着数据量的增长,性能会急剧降低。若是对多列进行索引(组合索引),列的顺序很是重要,MySQL仅能对索引最左边的前缀进行有效的查找。函数
下面介绍几种常见的MySQL索引类型。性能
索引分单列索引和组合索引。单列索引,即一个索引只包含单个列,一个表能够有多个单列索引,但这不是组合索引。组合索引,即一个索引包含多个列。
(1) 主键索引 PRIMARY KEY
它是一种特殊的惟一索引,不容许有空值。通常是在建表的时候同时建立主键索引。
固然也能够用 ALTER 命令。记住:一个表只能有一个主键。
(2) 惟一索引 UNIQUE
惟一索引列的值必须惟一,但容许有空值。若是是组合索引,则列值的组合必须惟一。能够在建立表的时候指定,也能够修改表结构,如:
ALTER TABLE table_name
ADD UNIQUE (column
)
(3) 普通索引 INDEX
这是最基本的索引,它没有任何限制。能够在建立表的时候指定,也能够修改表结构,如:
ALTER TABLE table_name
ADD INDEX index_name (column
)
(4) 组合索引 INDEX
组合索引,即一个索引包含多个列。能够在建立表的时候指定,也能够修改表结构,如:
ALTER TABLE table_name
ADD INDEX index_name(column1
, column2
, column3
)
(5) 全文索引 FULLTEXT
全文索引(也称全文检索)是目前搜索引擎使用的一种关键技术。它可以利用分词技术等多种算法智能分析出文本文字中关键字词的频率及重要性,而后按照必定的算法规则智能地筛选出咱们想要的搜索结果。
能够在建立表的时候指定,也能够修改表结构,如:
ALTER TABLE table_name
ADD FULLTEXT (column
)
mysql中广泛使用B+Tree作索引,但在实现上又根据聚簇索引和非聚簇索引而不一样,本文暂不讨论这点。
b+树介绍
下面这张b+树的图片在不少地方能够看到,之因此在这里也选取这张,是由于以为这张图片能够很好的诠释索引的查找过程。
如上图,是一颗b+树。浅蓝色的块咱们称之为一个磁盘块,能够看到每一个磁盘块包含几个数据项(深蓝色所示)和指针(黄色所示),如磁盘块1包含数据项17和35,包含指针P一、P二、P3,P1表示小于17的磁盘块,P2表示在17和35之间的磁盘块,P3表示大于35的磁盘块。
真实的数据存在于叶子节点,即三、五、九、十、1三、1五、2八、2九、3六、60、7五、7九、90、99。非叶子节点不存储真实的数据,只存储指引搜索方向的数据项,如1七、35并不真实存在于数据表中。
查找过程
在上图中,若是要查找数据项29,那么首先会把磁盘块1由磁盘加载到内存,此时发生一次IO,在内存中用二分查找肯定29在17和35之间,锁定磁盘块1的P2指针,内存时间由于很是短(相比磁盘的IO)能够忽略不计,经过磁盘块1的P2指针的磁盘地址把磁盘块3由磁盘加载到内存,发生第二次IO,29在26和30之间,锁定磁盘块3的P2指针,经过指针加载磁盘块8到内存,发生第三次IO,同时内存中作二分查找找到29,结束查询,总计三次IO。真实的状况是,3层的b+树能够表示上百万的数据,若是上百万的数据查找只须要三次IO,性能提升将是巨大的,若是没有索引,每一个数据项都要发生一次IO,那么总共须要百万次的IO,显然成本很是很是高。
性质
(1) 索引字段要尽可能的小。
经过上面b+树的查找过程,或者经过真实的数据存在于叶子节点这个事实可知,IO次数取决于b+数的高度h。
假设当前数据表的数据量为N,每一个磁盘块的数据项的数量是m,则树高h=㏒(m+1)N,当数据量N必定的状况下,m越大,h越小;
而m = 磁盘块的大小/数据项的大小,磁盘块的大小也就是一个数据页的大小,是固定的;若是数据项占的空间越小,数据项的数量m越多,树的高度h越低。这就是为何每一个数据项,即索引字段要尽可能的小,好比int占4字节,要比bigint8字节少一半。
(2) 索引的最左匹配特性。
当b+树的数据项是复合的数据结构,好比(name,age,sex)的时候,b+数是按照从左到右的顺序来创建搜索树的,好比当(张三,20,F)这样的数据来检索的时候,b+树会优先比较name来肯定下一步的所搜方向,若是name相同再依次比较age和sex,最后获得检索的数据;但当(20,F)这样的没有name的数据来的时候,b+树就不知道下一步该查哪一个节点,由于创建搜索树的时候name就是第一个比较因子,必需要先根据name来搜索才能知道下一步去哪里查询。好比当(张三,F)这样的数据来检索时,b+树能够用name来指定搜索方向,但下一个字段age的缺失,因此只能把名字等于张三的数据都找到,而后再匹配性别是F的数据了, 这个是很是重要的性质,即索引的最左匹配特性。
建索引的几大原则
(1) 最左前缀匹配原则
对于多列索引,老是从索引的最前面字段开始,接着日后,中间不能跳过。好比建立了多列索引(name,age,sex),会先匹配name字段,再匹配age字段,再匹配sex字段的,中间不能跳过。mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就中止匹配。
通常,在建立多列索引时,where子句中使用最频繁的一列放在最左边。
看一个补符合最左前缀匹配原则和符合该原则的对比例子。
实例:表c2c_db.t_credit_detail建有索引(Flistid
,Fbank_listid
)
不符合最左前缀匹配原则的sql语句:
select * from t_credit_detail where Fbank_listid='201108010000199'G
该sql直接用了第二个索引字段Fbank_listid,跳过了第一个索引字段Flistid,不符合最左前缀匹配原则。用explain命令查看sql语句的执行计划,以下图:
从上图能够看出,该sql未使用索引,是一个低效的全表扫描。
符合最左前缀匹配原则的sql语句:
select * from t_credit_detail where Flistid='2000000608201108010831508721' and Fbank_listid='201108010000199'G
该sql先使用了索引的第一个字段Flistid,再使用索引的第二个字段Fbank_listid,中间没有跳过,符合最左前缀匹配原则。用explain命令查看sql语句的执行计划,以下图:
从上图能够看出,该sql使用了索引,仅扫描了一行。
对比可知,符合最左前缀匹配原则的sql语句比不符合该原则的sql语句效率有极大提升,从全表扫描上升到了常数扫描。
(2) 尽可能选择区分度高的列做为索引。
好比,咱们会选择学号作索引,而不会选择性别来作索引。
(3) =和in能够乱序
好比a = 1 and b = 2 and c = 3,创建(a,b,c)索引能够任意顺序,mysql的查询优化器会帮你优化成索引能够识别的形式。
(4) 索引列不能参与计算,保持列“干净”
好比:Flistid+1>‘2000000608201108010831508721‘。缘由很简单,假如索引列参与计算的话,那每次检索时,都会先将索引计算一次,再作比较,显然成本太大。
(5) 尽可能的扩展索引,不要新建索引。
好比表中已经有a的索引,如今要加(a,b)的索引,那么只须要修改原来的索引便可。
索引的不足
虽然索引能够提升查询效率,但索引也有本身的不足之处。
索引的额外开销:
(1) 空间:索引须要占用空间;
(2) 时间:查询索引须要时间;
(3) 维护:索引需要维护(数据变动时);
不建议使用索引的状况:
(1) 数据量很小的表
(2) 空间紧张
优化语句不少,须要注意的也不少,针对平时的状况总结一下几点:
(1) Like的参数以通配符开头时
尽可能避免Like的参数以通配符开头,不然数据库引擎会放弃使用索引而进行全表扫描。
以通配符开头的sql语句,例如:select * from t_credit_detail where Flistid like '%0'G
这是全表扫描,没有使用到索引,不建议使用。
不以通配符开头的sql语句,例如:select * from t_credit_detail where Flistid like '2%'G
很明显,这使用到了索引,是有范围的查找了,比以通配符开头的sql语句效率提升很多。
(2) where条件不符合最左前缀原则时
例子已在最左前缀匹配原则的内容中有举例。
(3) 使用!= 或 <> 操做符时
尽可能避免使用!= 或 <>操做符,不然数据库引擎会放弃使用索引而进行全表扫描。使用>或<会比较高效。
select * from t_credit_detail where Flistid != '2000000608201108010831508721'G
(4) 索引列参与计算
应尽可能避免在 where 子句中对字段进行表达式操做,这将致使引擎放弃使用索引而进行全表扫描。
select * from t_credit_detail where Flistid +1 > '2000000608201108010831508722'G
(5) 对字段进行null值判断
应尽可能避免在where子句中对字段进行null值判断,不然将致使引擎放弃使用索引而进行全表扫描,如: 低效:select * from t_credit_detail where Flistid is null ;
能够在Flistid上设置默认值0,确保表中Flistid列没有null值,而后这样查询: 高效:select * from t_credit_detail where Flistid =0;
(6) 使用or来链接条件
应尽可能避免在where子句中使用or来链接条件,不然将致使引擎放弃使用索引而进行全表扫描,如: 低效:select * from t_credit_detail where Flistid = '2000000608201108010831508721' or Flistid = '10000200001';
能够用下面这样的查询代替上面的 or 查询: 高效:select from t_credit_detail where Flistid = '2000000608201108010831508721' union all select from t_credit_detail where Flistid = '10000200001';
在解析的过程当中,会将'*' 依次转换成全部的列名,这个工做是经过查询数据字典完成的,这意味着将耗费更多的时间。
因此,应该养成一个须要什么就取什么的好习惯。
任何在Order by语句的非索引项或者有计算表达式都将下降查询速度。
方法:1.重写order by语句以使用索引;
2.为所使用的列创建另一个索引 3.绝对避免在order by子句中使用表达式。
提升GROUP BY 语句的效率, 能够经过将不须要的记录在GROUP BY 以前过滤掉
低效:
SELECT JOB , AVG(SAL)
FROM EMP
GROUP by JOB
HAVING JOB = ‘PRESIDENT'
OR JOB = ‘MANAGER'
高效:
SELECT JOB , AVG(SAL)
FROM EMP
WHERE JOB = ‘PRESIDENT'
OR JOB = ‘MANAGER'
GROUP by JOB
不少时候用 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)
尽量的使用 varchar/nvarchar 代替 char/nchar ,由于首先变长字段存储空间小,能够节省存储空间,其次对于查询来讲,在一个相对较小的字段内搜索效率显然要高些。
SELECT OrderID FROM Details WHERE UnitPrice > 10 GROUP BY OrderID
可改成:
SELECT DISTINCT OrderID FROM Details WHERE UnitPrice > 10
UNION ALL不执行SELECT DISTINCT函数,这样就会减小不少没必要要的资源。
若是应用程序有不少JOIN 查询,你应该确认两个表中Join的字段是被建过索引的。这样,MySQL内部会启动为你优化Join的SQL语句的机制。
并且,这些被用来Join的字段,应该是相同的类型的。例如:若是你要把 DECIMAL 字段和一个 INT 字段Join在一块儿,MySQL就没法使用它们的索引。对于那些STRING类型,还须要有相同的字符集才行。(两个表的字符集有可能不同)
此文已由做者受权腾讯云+社区在各渠道发布
获取更多新鲜技术干货,能够关注咱们腾讯云技术社区-云加社区官方号及知乎机构号