索引(index)是帮助MySQL高效获取数据的数据结构->本质:索引是数据结构 MySQL默认存储引擎innodb只显示支持B-Tree(从技术上来说是B+Tree)索引
索引的分类:
普通索引:即一个索引只包含单个列,一个表可以有多个单列索引。唯一索引:索引列必须唯一,但允许有空值。复合索引:即一个索引包含多个列.聚簇索引:聚集索引:并不是一种单独的索引类型,而是一种数据存储方式。具体细节取决于不同的实现,innodb的聚簇索引其实就是在同一个结构中保存了B-Tree索引(技术上是B+Tree)和数据行。非聚簇索引:不是聚簇索引就是非聚簇索引 -- 查看索引 show index from account; -- 创建索引 create [unique] index indexName on tablename(columnname(length)) alter table tablename add [unique] index indexname on (columnname(length)) -- 删除索引 drop index indexname on tablename;使用explain关键字可以模拟优化器执行SQL查询语句,从而知道MySQL是如何处理你的SQL语句的,分析你的查询语句或是表结构的性能瓶颈。 explain +SQL语句
执行计划的作用:
表的读取顺序数据读取操作的操作类型哪些索引可以使用哪些索引被实际使用表之间的引用每张表有多少行被优化器查询select查询的序列号,包含一组数字,表示查询中执行select子句或操作表的顺序
id相同,执行顺序由上而下id不同,如果是子查询,id的序号会递增,id越大优先级越高,越先被查询。id相同不同,同时存在查询的类别,主要用来区别普通查询、联合查询、子查询等复杂查询
SIMPLE: 简单的select查询,查询中不包含子查询或者UNIONPRIMARY:查询中若包含任何复杂的子部分,最外层查询则被标记为SUBQUERY:在select或where列表中包含了子查询DERIVED:在from列表中包含的子查询被标记为DERIVED(衍生),MySQL会递归执行这些子查询,把结果放到临时表UNION:若第二个SELECT出现在UNION之后,则被标记为UNION;若UNION包含在form子句的子查询中,外层select被标记为DerivedUNION RESULT: 从UNION表获取结果的select -- SIMPLE explain select * from t1 -- PRIMARY SUBQUERY explain selectt t1.* ,(select t2.id from t2 where t2.id=1) from t1 -- DERIVED select t1.* from t1,(select t2.* from t2 where t2.id =1) s2 where t1.id=t2.id -- UNION RESULT UNION explain select * from t2 union select * from t2显示这一行的数据是关于哪张表的。
type显示的是访问类型,是较为重要的一个指标,结果值从最好到最坏依次是: system -> const -> eq_ref -> fulltext -> ref_or_null -> index_merge->unique_subquery -> index_subquery -> range -> ALL 比较重要的: system -> const -> eq_ref -> ref -> range -> index -> ALL
System:表中只有一条记录(等于系统表),是construction类型的特例平常不会出现,这个可以忽略不计 Const:表示通过索引依次就找到了 const用于比较primary key 或者 unique索引。因为只匹配一行数据,所以很快。
-- 将主键置于where列表中,MySQL就能将查询转换为一个常量 const explain select * from (select * from test_demo where id =1 ) d唯一性索引扫描,对于每个索引建,表中只有一条记录与之匹配。常用语主键或唯一索引扫描
-- id是account和test_demo 的主键 account-all test_demo eq_ref explain select * from account,test_demo where account.id=test_demo.id非唯一性索引扫描,返回匹配某个单独值的所有行 本质上也是一种索引访问,它返回所有匹配某个单独值的行,然而,它可能会找到多个符合条件的行,所以它应该属于查找和扫描的混合体
-- balance 是非主键索引 type ref explain select balance from account where balance =1只检索给定范围的行,使用一个索引来选择行。key列显示使用了哪个索引,一般就是在你的where语句中出现了between、<、>、in等查询,这种范围扫描索引比全表扫描要好,因为它只需要开始于索引某一点而结束于另一点,不用扫描全部索引。
-- 范围查找 range explain select * from account where id between 1 and 3当查询的结果全为索引列的时候,虽然也是全部扫描,但只查询了索引库,而没有查询数据
-- id和balance 均为索引 index explain select id,balance from accountfull table scan,将遍历全表以找到匹配的行
-- 遍历全表 all explain select * from accountpossible_key:可能使用的key Key:实际使用的索引,如果为null,则没有使用索引。
查询中若使用了覆盖索引,则该索引和查询的select字段重叠
-- extra= using index type = index explain select balance from account其中 key 和 possible_keys 都可能出现null的情况
key_len 表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度。在不损失精确性的情况下,长度越短越好 key_len 显示的值为索引字段的最大可能长度,并非实际使用长度,即key_len是根据表定义计算而得,不是通过表内检索出的
key_len 表示索引使用的字节数根据这个值,就可以判断索引使用情况,特别是在组合索引的时候,判断所有的索引字段是否都被查询用到char和varchar跟字符编码也有密切的联系latin1占用一个字节,gbk占用两个字节,utf8占用三个字节(不同字符编码占用的存储空间不同)表示时间值的日期和时间类型为datetime、 date、 timestamp、 time、 year 每个时间类型都有一个有效值范围和零值,当指定不合法的MySQL不能表示值时使用零值 timestamp类型有转悠的自动更新特性 datetime类型在5.6中长度为5个字节,在5.6中字段长度为8个字节
create table date_index( datetime_null datetime null, datetime_not_null datetime not null, index idx_date_null (datetime_null), index idx_date_not_null (datetime_not_null) )engine=innodb charset=utf8 show index from date_index -- 6 explain select * from date_index where datetime_null=1 -- 5 explain select * from date_index where datetime_not_null=1字符类型:
变长字段需要额外的两个字节(varchar 值保存时值保存需要的字符数,另外一个字节来记录长度(如果列声明长度超过255,则需要两个字节,所以varchar索引长度计算时候加2)),固定长度字段不需要额外的字节。而null都需要一个字节的额外空间,所以索引字段最好不要为null,因为null让统计更加复杂并且需要额外的存储空间。复合索引有最左前缀的特性,如果复合索引全部使用上,则是复合索引字段的索引长度之和,这也可以用来判定复合索引是否部分使用,还是全部使用。 整数、浮点数、时间类型索引长度not null = 字段本身的字段长度null = 字段本身的字段长度+1(需要一个字节存储是否为空的的标记)datetime类型在5.6中字段长度是5个字节,datetime类型在5.5字段长度是8个字节显示索引的哪一列被使用了,如果可能的话,是一个常数。哪些列或常量被用于查找索引列上的值。
根据表统计信息及索引选用情况,大致估算出找到所需的记录所需要读取的行数
包含不适合在其他列中显示但十分重要的额外信息。
建立索引列之后,能使用索引的尽量使用索引
-- 全值匹配 explain select * from staffs where name= 'jack' explain select * from staffs where name= 'jack' and age=22 explain select * from staffs where name= 'jack' and age=22 and pos='namager'如果索引了多列,要遵守最左前缀法则。指的是查询从索引的最左前列开始并且不跳过索引中的列
-- 最左前缀 -- 没用到索引 索引多列,必须有索引第一列,否则不会使用索引 explain select * from staffs where age=22 and pos ='manage' -- 索引多列,中间索引未使用 key_len=74 ->name explain select * from staffs where name='jack' and pos='manage' -- 索引多列,中间不断 key_len = 78 -> name age explain select * from staffs where name='jack' and age=22 -- 索引多列 key_len = 140-> name age pos 充分使用索引 explain select * from staffs where name= 'jack' and age=22 and pos='namager'不在索引列上做任何操作(计算、函数、(自动or手动)类型转换),会导致索引失效而转向全表扫描。
-- 不要在索引列做任何操作 -- 使用索引 key_len = 74 explain select * from staffs where name = 'jack' -- 全表扫描 explain select * from staffs where left(name,4) = 'jack'尽量使用覆盖索引(只访问索引的查询(索引列和查询列一致)),减少select *
-- 覆盖索引尽量用 explain select * from staffs where name= 'jack' and age=22 and pos='namager' explain select * from staffs where name= 'jack' and age>20 and pos='namager' -- 使用了覆盖索引 explain select name,age,pos from staffs where name= 'jack' and age=22 and pos='namager' -- 使用了覆盖索引 explain select name,age,pos from staffs where name= 'jack' and age>22 and pos='namager'mysql在使用不等于( != 或者 <>)的时候无法使用索引会导致全表扫描
-- 不等于要慎用 explain select * from staffs where name = 'staffs'; -- 索引失效 key_len = null key = null explain select * from staffs where name != 'staffs'; -- 索引失效 key_len = null key = null explain select * from staffs where name <> 'staffs';like以通配符开头(’%abc…’) mysql索引失效会变成全表扫描的操作
-- like查询要当心 -- key_len =74 explain select * from staffs where name like 'jack' -- key_len = null explain select * from staffs where name like '%jack%' -- key_len=null explain select * from staffs where name like '%jack' -- key_len = 74 explain select * from staffs where name like 'jack%'字符串不加单引号索引失效
-- 字符串类型加引号 -- key_len =74 explain select * from staffs where name = '123' -- key_len = null explain select * from staffs where name = 123