一 常见问题
系统性能下降,SQL变慢
1)查询语句写的烂
2)索引失效
3)关联查询太多join(设计缺陷或不得已的需求)
4)服务器调优,及各个参数设置(缓冲,线程数等)
二 常见的JOIN查询
2.1 SQL执行顺序
1)手写的SQL
select <select_list>
from <left_table>
<join_type> join <right_table>
on <join_condition>
where <where_condition>
group by <group_by_list>
having <having_condition>
order by <order_by_condition>
limit <start, size>
2)实际执行SQL
from <left_table>
on <join_condition>
<join_type> join <right_table>
where <where_condition>
group by <group_by_list>
having <having_condition>
select <select_list>
order by <order_by_condition>
limit <start, size>
2.2 七种JOIN用法
1)A和B的交集
select <select_list>
from A a
inner join B b on A.key = b.key
2)A的全集
select <select_list>
from A a
left join B b on a.key = b.key
3)B的全集
select <select_list>
from A a
right join B b on a.key = b.key
4)A减去AB之间的交集
select <select_list>
from A a
left join B b on a.key = b.key
where b.key is null
5)B减去AB的交集
select <select_list>
from A a
right join B b on a.key = b.key
where a.key is null
6)A和B相加的全集
select <select_list>
from A a
full out join B b on a.key = b.key # mysql不支持full out 这种语法
select <select_list> from A a left join B b on a.key = b.key
union
select <select_list> from A a right join B b on a.key = b.key
7)A加B减去AB之间的交集
select <select_list>
from A a
full out join B b on a.key = b.key
where a.key is null or b.key is null # full out 不支持语法
select <select_list> from A a left join B b on a.key = b.key where a.key is null
union
select <select_list> from A a right join B b on a.key = b.key where b.key is null
三 索引简介
3.1 概述
1)官方对索引的定义是:索引是可以帮助mysql高效获取数据的数据结构。
2)本质:数据结构,就是排好序的快速查找数据结构。
3)在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构上实现高级查找算法。这种数据结构,就是索引。
4)一般来说,索引也很大,不可能全部存储在内存里面,因此,索引往往以索引文件的形式存储在磁盘上面。
5)我们平常所说的索引,如果没有特别指明,都是指B树索引(多路搜索树,并不一定是二叉树)结构的索引。其中聚集索引,次要索引,覆盖索引,复合索引,前缀索引,唯一索引默认都是B+树索引,统称索引。当然除了B+树这种类型的索引,还有哈希索引等。
3.2 优缺点
1)优点
①提高数据检索的效率,降低数据库的IO成本。
②通过索引对数据进行排序,降低了数据排序的成本,降低了cpu的消耗。
2)缺点
①虽然索引大大提高了查询速度,同时却会降低更新表的速度。
②实际上索引也是一张表,该表保存了主键与索引字段,并指向实体表的记录,所以索引列也是要占用空间的。
3.3 索引分类
1)单值索引:即一个索引只包含单个列,一个表可以有多个单列索引
2)唯一索引:索引列的值必须唯一,但允许有空值
3)复合索引:即一个索引包含多个列
3.4 基本语法
1)创建
create [unique] index indexName on tableName(column(length))
alter tableName add [unique] index [indexName] on (column(length))
2)删除
drop index [indexName] on tableName
3)查看
show index from tabelName
4)使用alter 命令
# 该语句添加一个主键,意味着索引值必须是唯一的,且不能为NULL
alter table tableName add primary key (columnList)
# 这条语句创建索引的值必须是唯一的(除NULL以外,NULL还可能会出现多次)
alter table tableName add unique indexName (columnList)
# 添加普通索引,索引值可出现多次
alter table tableName add index indexName(columnList)
# 该语句指定了索引为FULLTEXT,用于全文索引
alter table tableName add fulltext indexName (columnList)
3.5 索引结构
3.5.1 B树索引
1)初始化
一颗b+树,浅蓝色的块我们称之为一个磁盘块,可以看到每个磁盘块包含几个数据项(深蓝色所示〉和指针(黄色所示),如磁盘块1包含数据项17和35,包含指针P1、P2、P3,P1表示小于17的磁盘块,P2表示在17和35之间的磁盘块,P3表示大于35的磁盘块。
真实的数据存在于叶子节点即3、5、9、10、13、15、28、29、36、60、75、79、90、99。
非叶子节点只不存储真实的数据,只存储指引搜索方向的数据项,如17、35并不真实存在于数据表中。
2)查找过程
如果要查找数据项29,那么首先会把磁盘块1由磁盘加载到内存,此时发生一次IO,在内存中用二分查找确定29在17和35之间,锁定磁盘块1的P2指针,内存时间因为非常短(相比磁盘的I0)可以忽略不计,通过磁盘块1的P2指针的磁盘地址把磁盘块3由磁盘加载到内存,发生第二次Il0,29在26和30之间,锁定磁盘块3的P2指针,通过指针加载磁盘块8到内存,发生第三次I0,同时内存中做二分查找找到29,结束查询,总计三次IO。
3)真实场景
真实的情况是,3层的b+树可以表示上百万的数据,如果上百万的数据查找只需要三次IO,性能提高将是巨大的,如果没有素引,每个数据项都要发生一次IO,那么总共需要百万次的IO,显然成本非常非常高。
3.5.2 其他索引
1)Hash索引
2)full-text全文索引
3)RTree索引
3.6 需要创建索引
①主键主动创建唯一索引
②频繁作为查询条件的字段应该创建索引
③查询中与其他表关联的字段,外键关系建立索引
④频繁更新的字段不适合创建索引:因为每次更新不单单更新了记录,还更新了索引
⑤where条件用不到的字段不创建索引
⑥单键/组合索引的选择问题:在高并发的场合下倾向于创建组合索引
⑦查询中排序的字段,排序字段若通过索引去访问,将大大提高排序速度
⑧查询中统计或分组的字段
3.7 不需要创建索引
①表记录太少
②经常增删改的表
③数据重复且分布平均的表字段,因此应该只为最经常查询和最经常排序的数据列建立索引。注意,如果某个数据列包含许多重复的内容,为它创建索引就没有太大的实际效果
假如一个表有10万行记录,有一个字段A只有T和F两种值,且每个值的分希概率大约为50%,那么对这种表A字段建索引一般不会提高数据库的查询速度。索引的选择性是指索引列中不同值的数目与表中记录数的比。如果一个表中有2000条记录,表索引列有1980个不同的值,那么这个索引的选择性就是1980/2000=0.99。一个索引的选择性越接近于1,这个索引的效率就越高。
四 性能分析
4.1 MySQLQueryOptimizer
1)MySQL中有专门负责优化SELECT语句的优化器模块,主要功能:通过计算分析系统中收集到的统计信息,为客户端请求的Query提供他认为最优的执行计划(他认为最优的数据检索方式,但不见得是DBA认为是最优的,这部分最耗费时间)
2)当客户端向MySQL请求一条Query,命令解析器模块完成请求分类,区别出是SELECT并转发给MySQL Query Optimizer时,MySQL Query Optimizer首先会对整条Query进行优化,处理掉一些常量表达式的预算,直接换算成常量值。并对Query中的查询条件进行简化和转换,如去掉一些无用或显而易见的条件、结构调整等。然后分析Query中的Hint信息(如果有),看显示Hint信息是否可以完全确定该Query的执行计划。如果没有Hint或Hint信息还不足以完全确定执行计划,则会读取所涉及对象的统计信息,根据Query进行写相应的计算分析,然后再得出最后的执行计划。4.2 常见瓶颈
1)CPU:CPU饱和的时候一般发生在数据装入内存,或从磁盘上读取数据的时候
2)IO:磁盘IO瓶颈发生在装入数据远大于内存容量的时候
3)服务器硬件的瓶颈:top,free,iostat,vmstat来查看系统的性能状态4.3 Explain
4.3.1 Explain的作用
1)使用explain关键字可以模拟优化器执行SQL查询语句,从而知道MySQL是如何处理SQL语句的。分析查询语句或者是表结构的性能瓶颈。
2)用处查看表的读取顺序
- 查看数据读取操作的操作类型
- 哪些索引可以被使用
- 哪些索引被实际使用
- 表之间的引用
-
4.3.2 使用方式
1)语法:explain + sql语句
2)执行计划包含的信息id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra
4.3.3 各个字段解析
4.3.3.1 id
1)select查询的序列号,包含一组数字,表示查询中执行的select子句或操作表的顺序
2)id相同,执行顺序由上而下
3)id不同,如果是子查询,id的序号会递增,id值越大优先级越高,越先被执行
4)id相同不同,同时存在4.3.3.2 select_type
1)表示查询的类型,主要用于区别普通查询,联合查询,子查询等复杂查询
2)取值主要有6种:SIMPLE,PRIMARY,SUBQUERY,DEDRIVED,UNION,UNION RESULT
3)SIMPLE:简单的select查询,查询中不包含子查询或者UNION
4)PRIMARY:查询中若包含任何复杂的子查询,最外层的查询则被标记为PRIMARY
5)SUBQUERY:在select 或 where 列表中包含了子查询
6)DERIVED:在from列表中包含了的子查询会被标记为DERIVED(衍生),MySQL会递归执行这些子查询,把结果放在临时表
7)UNION:若在第二个select出现在union之后,则被标记为UNION;若UNION包含在from子句中,外层的select会被标记为DERIVED。
8)UNION RESULT:从UNION表获取结果的select4.3.3.3 table
4.3.3.4 type
1)type显示的是访问类型,是较为重要的一个指标
2)取值为:ALL,index,range,ref,eq_ref,const,system,NULL
3)访问类型排列:system > const > eq_ref > ref > range > index > ALL(从最好到最差)
4)一般来说,得保证查询至少达到 range 级别,最好能达到ref
5)system:表只有一行记录(等于系统表),这个const类型得特列,平时不会出现,这个也可以忽略不记
6)const:表示通过索引一次就找到了,const用于比较primary key 或者 unique 索引。因为只匹配一行数据,所以很快。如将主键置于where列表中,MySQL就能将该查询转换为一个常量
7)eq_ref:唯一性索引扫描,对于每一个索引键,表中只有一条记录与之匹配,常见于主键和唯一索引扫描
8)ref:非唯一性索引扫描,返回匹配某个单独值得所有行,本质上也是一种索引访问,它返回得所有匹配某个单独值的行,然而,它可能会找到多个符合条件的行,所以他属于查找和扫描的混合体
9)range:只检索给定范围的行,使用一个索引来选择行,key列显示使用了哪个索引,一般就是在你的where语句中出现了between,>,<,in等的查询
10)index:Full Index Scan,index 和 ALL 的区别为 index 类型只遍历索引树,这通常比ALL 快,因为索引文件通常比数据文件小。(也就是说虽然index 和 ALL 都是读全表,但是index 是从索引读取的,而ALL 是从硬盘读取的)
11)all:全表扫描以找到匹配的行4.3.3.5 possible_keys
1)显示可能应用到这张表的索引,一个或者多个
2)查询涉及到的字段若存在索引,则该索引被列出,但不一定被查询实际使用4.3.3.6 key
1)实际使用到的索引,如果为NULL,则没有使用索引
2)查询中若使用了覆盖索引,则该索引仅出现在key 列表中4.3.3.7 key_len
1)表示索引中使用的字节数,可通过该列计算查询中使用的索引长度。在不损失精确性的情况下,长度越短越好
2)key_len 显示的值为索引字段的最大可能长度,并非实际长度,即key_len 是根据表定义计算而得,不是通过表内检索出的。4.3.3.8 ref
1)显示索引的哪一列被使用了,如果可能的话,是一个常数,哪些列或常量被用于查找索引列上的值
4.3.3.9 rows
根据表统计信息及索引选用情况,大致估算出找到所需的记录所要读取的行数
4.3.3.10 Extra
1)包含不适合在其他列显示但是很重要的信息
2)Using filesort:说明MySQL会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行读取。MySQL中无法利用索引完成的排序操作称为“文件排序”
3)Using temporay:使用了临时表保存中间结果,MySQL对查询结果排序时使用了临时表。常见于order by 和分组查询 group by
4)Using index:表示相应的select操作使用了覆盖索引,避免访问了表的数据行,效率不错。如果同时出现了using where,表明索引被用来执行索引键值的查找。如果没有同时出现using where ,表明索引用来读取数据而非执行查找动作
4.3.4 索引优化
4.3.4.1 索引分析
1)单表
- 建表语句
``sql CREATE TABLE IF NOT EXISTS
article(
idINT(10) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
author_idINT(10) UNSIGNED NOT NULL,
category_idINT(10) UNSIGNED NOT NULL,
viewsINT(10) UNSIGNED NOT NULL,
commentsINT(10) UNSIGNED NOT NULL,
titleVARBINARY(255) NOT NULL,
content` TEXT NOT NULL );
INSERT INTO article
(author_id
, category_id
, views
, comments
, title
, content
) VALUES
(1, 1, 1, 1, ‘1’, ‘1’),
(2, 2, 2, 2, ‘2’, ‘2’),
(1, 1, 3, 3, ‘3’, ‘3’);
- 案例分析
```sql
# 查询 category_id 为 1 且 comments 大于 1 的情况下,views 最多的 article_id。
EXPLAIN SELECT id,author_id FROM article WHERE category_id = 1 AND comments > 1 ORDER BY views DESC LIMIT 1;
# 结论:很显然,type 是 ALL,即最坏的情况。Extra 里还出现了 Using filesort,也是最坏的情况。优化是必须的。
# 开始优化:
# 1.1、新建索引 + 删除索引
# ALTER TABLE `article` ADD INDEX idx_article_ccv ( `category_id` , `comments`, `views` );
create index idx_article_ccv on article(category_id,comments,views);
DROP INDEX idx_article_ccv ON article
# 1.2、第2次EXPLAIN
EXPLAIN SELECT id,author_id FROM `article` WHERE category_id = 1 AND comments >1 ORDER BY views DESC LIMIT 1;
EXPLAIN SELECT id,author_id FROM `article` WHERE category_id = 1 AND comments =3 ORDER BY views DESC LIMIT 1
#结论:
#type 变成了 range,这是可以忍受的。但是 extra 里使用 Using filesort 仍是无法接受的。
#但是我们已经建立了索引,为啥没用呢?
#这是因为按照 BTree 索引的工作原理,
# 先排序 category_id,
# 如果遇到相同的 category_id 则再排序 comments,如果遇到相同的 comments 则再排序 views。
#当 comments 字段在联合索引里处于中间位置时,
#因comments > 1 条件是一个范围值(所谓 range),
#MySQL 无法利用索引再对后面的 views 部分进行检索,即 range 类型查询字段后面的索引无效。
# 1.3、删除第一次建立的索引
DROP INDEX idx_article_ccv ON article;
# 1.4、第2次新建索引
#ALTER TABLE `article` ADD INDEX idx_article_cv ( `category_id` , `views` ) ;
create index idx_article_cv on article(category_id,views);
# 1.5、第3次EXPLAIN
EXPLAIN SELECT id,author_id FROM article WHERE category_id = 1 AND comments > 1 ORDER BY views DESC LIMIT 1;
#结论:可以看到,type 变为了 ref,Extra 中的 Using filesort 也消失了,结果非常理想。
DROP INDEX idx_article_cv ON article;
2)两表
- 建表语句
``sql CREATE TABLE IF NOT EXISTS
class(
idINT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
cardINT(10) UNSIGNED NOT NULL, PRIMARY KEY (
id) ); CREATE TABLE IF NOT EXISTS
book(
bookidINT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
cardINT(10) UNSIGNED NOT NULL, PRIMARY KEY (
bookid`) );
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() 20)));
- 案例分析
```sql
# 下面开始explain分析
EXPLAIN SELECT * FROM class LEFT JOIN book ON class.card = book.card;
# 结论:type 有All
# 添加索引优化
ALTER TABLE `book` ADD INDEX Y ( `card`);
# 第2次explain
EXPLAIN SELECT * FROM class LEFT JOIN book ON class.card = book.card;
# 可以看到第二行的 type 变为了 ref,rows 也变成了优化比较明显。
# 这是由左连接特性决定的。LEFT JOIN 条件用于确定如何从右表搜索行,左边一定都有,
# 所以右边是我们的关键点,一定需要建立索引。
# 删除旧索引 + 新建 + 第3次explain
DROP INDEX Y ON book;
ALTER TABLE class ADD INDEX X (card);
EXPLAIN SELECT * FROM class LEFT JOIN book ON class.card = book.card;
# 然后来看一个右连接查询:
# 优化较明显。这是因为 RIGHT JOIN 条件用于确定如何从左表搜索行,右边一定都有,所以左边是我们的关键点,一定需要建立索引。
EXPLAIN SELECT * FROM class RIGHT JOIN book ON class.card = book.card;
DROP INDEX X ON class;
ALTER TABLE book ADD INDEX Y (card);
# 右连接,基本无变化
EXPLAIN SELECT * FROM class RIGHT JOIN book ON class.card = book.card;
3)三表
- 建表语句
``sql CREATE TABLE IF NOT EXISTS
phone(
phoneidINT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
cardINT(10) UNSIGNED NOT NULL, PRIMARY KEY (
phoneid`) ) ENGINE = INNODB;
INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() 20))); INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() 20))); INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() 20))); INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() 20))); INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() 20))); INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() 20))); INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() 20))); INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() 20))); INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() 20))); INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() 20))); INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() 20))); INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() 20))); INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() 20))); INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() 20))); INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() 20))); INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() 20))); INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() 20))); INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() 20))); INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() 20))); INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() 20)));
- 案例分析
```sql
ALTER TABLE `phone` ADD INDEX z ( `card`);
ALTER TABLE `book` ADD INDEX Y ( `card`);#上一个case建过一个同样的
EXPLAIN SELECT * FROM class LEFT JOIN book ON class.card=book.card LEFT JOIN phone ON book.card = phone.card;
# 后 2 行的 type 都是 ref 且总 rows 优化很好,效果不错。因此索引最好设置在需要经常查询的字段中。
==================================================================================
【结论】
Join语句的优化
尽可能减少Join语句中的NestedLoop的循环总次数;“永远用小结果集驱动大的结果集”。
优先优化NestedLoop的内层循环;
保证Join语句中被驱动表上Join条件字段已经被索引;
当无法保证被驱动表的Join条件字段被索引且内存资源充足的前提下,不要太吝惜JoinBuffer的设置;
4.3.4.2 索引失效
1)建表语句
CREATE TABLE staffs (
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR (24) NOT NULL DEFAULT '' COMMENT '姓名',
age INT NOT NULL DEFAULT 0 COMMENT '年龄',
pos VARCHAR (20) NOT NULL DEFAULT '' COMMENT '职位',
add_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '入职时间'
) CHARSET utf8 COMMENT '员工记录表' ;
INSERT INTO staffs(NAME,age,pos,add_time) VALUES('z3',22,'manager',NOW());
INSERT INTO staffs(NAME,age,pos,add_time) VALUES('July',23,'dev',NOW());
SELECT * FROM staffs;
ALTER TABLE staffs ADD INDEX idx_staffs_nameAgePos(name, age, pos);
2)案例分析
(1)全值匹配我最爱
(2)最佳最前缀法则:如果索引了多列,那么要遵守最左前缀法则。指的是查询从索引的最左列开始并且不跳过索引中的列。
(3)不在索引列上做任何操作(计算,函数,类型转换),会导致索引失效而转向全表扫描
(4)存储引擎不能使用索引中范围条件右边的列
(5)尽量使用覆盖索引(只访问索引的查询),减少 select *
(6)MySQL 在使用不等于(!=,<>)的时候,无法使用索引会导致全表扫描
(7)is null,is not null 也无法使用索引
(8)like 以通配符开头,MySQL索引失效会导致全表扫描
(9)字符串不加单引号索引失效
(10)少用 or,使用它连接会导致索引失效
总结:索引 index(a,b,c)
Where语句 | 索引是否被使用 |
---|---|
where a = 3 | Y,使用到a |
where a = 3 and b = 5 | Y,使用到a,b |
where a = 3 and b = 5 and c = 4 | Y,使用到a,b,c |
where b = 3 | where b = 3 and c = 4 | where c = 4 | N |
where a = 3 and c = 5 | 使用到a, 但是C不可以,中间断了 |
where a = 3 and b > 4 and c = 7 | 使用到a和b, c在范围之后,断了 |
where a = 3 and b like ‘kk%’ and c = 4 | 同上 |