一 常见问题

系统性能下降,SQL变慢

1)查询语句写的烂
2)索引失效
3)关联查询太多join(设计缺陷或不得已的需求)
4)服务器调优,及各个参数设置(缓冲,线程数等)

二 常见的JOIN查询

2.1 SQL执行顺序

1)手写的SQL

  1. select <select_list>
  2. from <left_table>
  3. <join_type> join <right_table>
  4. on <join_condition>
  5. where <where_condition>
  6. group by <group_by_list>
  7. having <having_condition>
  8. order by <order_by_condition>
  9. limit <start, size>

2)实际执行SQL

  1. from <left_table>
  2. on <join_condition>
  3. <join_type> join <right_table>
  4. where <where_condition>
  5. group by <group_by_list>
  6. having <having_condition>
  7. select <select_list>
  8. order by <order_by_condition>
  9. limit <start, size>

2.2 七种JOIN用法

1)A和B的交集

  1. select <select_list>
  2. from A a
  3. inner join B b on A.key = b.key

2)A的全集

  1. select <select_list>
  2. from A a
  3. left join B b on a.key = b.key

3)B的全集

  1. select <select_list>
  2. from A a
  3. right join B b on a.key = b.key

4)A减去AB之间的交集

  1. select <select_list>
  2. from A a
  3. left join B b on a.key = b.key
  4. where b.key is null

5)B减去AB的交集

  1. select <select_list>
  2. from A a
  3. right join B b on a.key = b.key
  4. where a.key is null

6)A和B相加的全集

  1. select <select_list>
  2. from A a
  3. full out join B b on a.key = b.key # mysql不支持full out 这种语法
  1. select <select_list> from A a left join B b on a.key = b.key
  2. union
  3. select <select_list> from A a right join B b on a.key = b.key

7)A加B减去AB之间的交集

  1. select <select_list>
  2. from A a
  3. full out join B b on a.key = b.key
  4. where a.key is null or b.key is null # full out 不支持语法
  1. select <select_list> from A a left join B b on a.key = b.key where a.key is null
  2. union
  3. 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)创建

  1. create [unique] index indexName on tableName(column(length))
  2. alter tableName add [unique] index [indexName] on (column(length))

2)删除

  1. drop index [indexName] on tableName

3)查看

  1. show index from tabelName

4)使用alter 命令

  1. # 该语句添加一个主键,意味着索引值必须是唯一的,且不能为NULL
  2. alter table tableName add primary key (columnList)
  3. # 这条语句创建索引的值必须是唯一的(除NULL以外,NULL还可能会出现多次)
  4. alter table tableName add unique indexName (columnList)
  5. # 添加普通索引,索引值可出现多次
  6. alter table tableName add index indexName(columnList)
  7. # 该语句指定了索引为FULLTEXT,用于全文索引
  8. 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)执行计划包含的信息

    1. 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表获取结果的select

    4.3.3.3 table

    1)显示这一行数据是属于哪一张表的

    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 EXISTSarticle(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’);

  1. - 案例分析
  2. ```sql
  3. # 查询 category_id 为 1 且 comments 大于 1 的情况下,views 最多的 article_id。
  4. EXPLAIN SELECT id,author_id FROM article WHERE category_id = 1 AND comments > 1 ORDER BY views DESC LIMIT 1;
  5. # 结论:很显然,type 是 ALL,即最坏的情况。Extra 里还出现了 Using filesort,也是最坏的情况。优化是必须的。
  6. # 开始优化:
  7. # 1.1、新建索引 + 删除索引
  8. # ALTER TABLE `article` ADD INDEX idx_article_ccv ( `category_id` , `comments`, `views` );
  9. create index idx_article_ccv on article(category_id,comments,views);
  10. DROP INDEX idx_article_ccv ON article
  11. # 1.2、第2次EXPLAIN
  12. EXPLAIN SELECT id,author_id FROM `article` WHERE category_id = 1 AND comments >1 ORDER BY views DESC LIMIT 1;
  13. EXPLAIN SELECT id,author_id FROM `article` WHERE category_id = 1 AND comments =3 ORDER BY views DESC LIMIT 1
  14. #结论:
  15. #type 变成了 range,这是可以忍受的。但是 extra 里使用 Using filesort 仍是无法接受的。
  16. #但是我们已经建立了索引,为啥没用呢?
  17. #这是因为按照 BTree 索引的工作原理,
  18. # 先排序 category_id,
  19. # 如果遇到相同的 category_id 则再排序 comments,如果遇到相同的 comments 则再排序 views。
  20. #当 comments 字段在联合索引里处于中间位置时,
  21. #因comments > 1 条件是一个范围值(所谓 range),
  22. #MySQL 无法利用索引再对后面的 views 部分进行检索,即 range 类型查询字段后面的索引无效。
  23. # 1.3、删除第一次建立的索引
  24. DROP INDEX idx_article_ccv ON article;
  25. # 1.4、第2次新建索引
  26. #ALTER TABLE `article` ADD INDEX idx_article_cv ( `category_id` , `views` ) ;
  27. create index idx_article_cv on article(category_id,views);
  28. # 1.5、第3次EXPLAIN
  29. EXPLAIN SELECT id,author_id FROM article WHERE category_id = 1 AND comments > 1 ORDER BY views DESC LIMIT 1;
  30. #结论:可以看到,type 变为了 ref,Extra 中的 Using filesort 也消失了,结果非常理想。
  31. DROP INDEX idx_article_cv ON article;

2)两表
  • 建表语句 ``sql CREATE TABLE IF NOT EXISTSclass(idINT(10) UNSIGNED NOT NULL AUTO_INCREMENT,cardINT(10) UNSIGNED NOT NULL, PRIMARY KEY (id) ); CREATE TABLE IF NOT EXISTSbook(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)));

  1. - 案例分析
  2. ```sql
  3. # 下面开始explain分析
  4. EXPLAIN SELECT * FROM class LEFT JOIN book ON class.card = book.card;
  5. # 结论:type 有All
  6. # 添加索引优化
  7. ALTER TABLE `book` ADD INDEX Y ( `card`);
  8. # 第2次explain
  9. EXPLAIN SELECT * FROM class LEFT JOIN book ON class.card = book.card;
  10. # 可以看到第二行的 type 变为了 ref,rows 也变成了优化比较明显。
  11. # 这是由左连接特性决定的。LEFT JOIN 条件用于确定如何从右表搜索行,左边一定都有,
  12. # 所以右边是我们的关键点,一定需要建立索引。
  13. # 删除旧索引 + 新建 + 第3次explain
  14. DROP INDEX Y ON book;
  15. ALTER TABLE class ADD INDEX X (card);
  16. EXPLAIN SELECT * FROM class LEFT JOIN book ON class.card = book.card;
  17. # 然后来看一个右连接查询:
  18. # 优化较明显。这是因为 RIGHT JOIN 条件用于确定如何从左表搜索行,右边一定都有,所以左边是我们的关键点,一定需要建立索引。
  19. EXPLAIN SELECT * FROM class RIGHT JOIN book ON class.card = book.card;
  20. DROP INDEX X ON class;
  21. ALTER TABLE book ADD INDEX Y (card);
  22. # 右连接,基本无变化
  23. EXPLAIN SELECT * FROM class RIGHT JOIN book ON class.card = book.card;

3)三表
  • 建表语句 ``sql CREATE TABLE IF NOT EXISTSphone(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)));

  1. - 案例分析
  2. ```sql
  3. ALTER TABLE `phone` ADD INDEX z ( `card`);
  4. ALTER TABLE `book` ADD INDEX Y ( `card`);#上一个case建过一个同样的
  5. EXPLAIN SELECT * FROM class LEFT JOIN book ON class.card=book.card LEFT JOIN phone ON book.card = phone.card;
  6. # 后 2 行的 type 都是 ref 且总 rows 优化很好,效果不错。因此索引最好设置在需要经常查询的字段中。
  7. ==================================================================================
  8. 【结论】
  9. Join语句的优化
  10. 尽可能减少Join语句中的NestedLoop的循环总次数;“永远用小结果集驱动大的结果集”。
  11. 优先优化NestedLoop的内层循环;
  12. 保证Join语句中被驱动表上Join条件字段已经被索引;
  13. 当无法保证被驱动表的Join条件字段被索引且内存资源充足的前提下,不要太吝惜JoinBuffer的设置;

4.3.4.2 索引失效

1)建表语句

  1. CREATE TABLE staffs (
  2. id INT PRIMARY KEY AUTO_INCREMENT,
  3. NAME VARCHAR (24) NOT NULL DEFAULT '' COMMENT '姓名',
  4. age INT NOT NULL DEFAULT 0 COMMENT '年龄',
  5. pos VARCHAR (20) NOT NULL DEFAULT '' COMMENT '职位',
  6. add_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '入职时间'
  7. ) CHARSET utf8 COMMENT '员工记录表' ;
  8. INSERT INTO staffs(NAME,age,pos,add_time) VALUES('z3',22,'manager',NOW());
  9. INSERT INTO staffs(NAME,age,pos,add_time) VALUES('July',23,'dev',NOW());
  10. SELECT * FROM staffs;
  11. 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 同上