(七)MySQL索引和执行计划

索引 和 EXPLAIN/DESC

概念

索引是对数据库表中一列或多列的值进行排序的一种结构,使用索引可快速访问数据库表中的特定信息。如果想按特定职员的姓来查找他或她,则与在表中搜索所有的行相比,索引有助于更快地获取信息

索引分为聚簇索引和非聚簇索引两种

聚簇索引是按照数据存放的物理位置为顺序的,而非聚簇索引就不一样了;聚簇索引能提高多行检索的速度,而非聚簇索引对于单行的检索很快

MyISAM:非聚簇索引

  • 非聚簇索引的主索引和辅助索引几乎是一样的,只是主索引不允许重复,不允许空值,他们的叶子结点的key都存储指向键值对应的数据的物理地址
  • 非聚簇索引的数据表和索引表是分开存储的
  • 非聚簇索引中的数据是根据数据的插入顺序保存。因此非聚簇索引更适合单个数据的查询。插入顺序不受键值影响
  • 只有在MyISAM中才能使用FULLTEXT索引

InnoDB:聚簇索引

  • 聚簇索引的主索引的叶子结点存储的是键值对应的数据本身,辅助索引的叶子结点存储的是键值对应的数据的主键键值。因此主键的值长度越小越好,类型越简单越好
  • 聚簇索引的数据和主键索引存储在一起。
  • 聚簇索引的数据是根据主键的顺序保存。因此适合按主键索引的区间查找,可以有更少的磁盘I/O,加快查询速度。但是也是因为这个原因,聚簇索引的插入顺序最好按照主键单调的顺序插入,否则会频繁的引起页分裂,严重影响性能。
  • 在InnoDB中,如果只需要查找索引的列,就尽量不要加入其它的列,这样会提高查询效率。

分类

主键索引:即主索引,根据主键pk_clolum(length)建立索引,不允许重复,不允许空值;

  • 自动创建
ALTER TABLE table_name ADD PRIMARY KEY('col');

唯一索引:用来建立索引的列的值必须是唯一的,允许空值

ALTER TABLE table_name ADD UNIQUE('col');

普通索引:用表中的普通列构建的索引,没有任何限制

ALTER TABLE table_name ADD INDEX index_name('col');

全文索引:用大文本对象的列构建的索引

ALTER TABLE table_name ADD FULLTEXT('col');

组合索引:用多个列组合构建的索引,这多个列中的值不允许有空值

ALTER TABLE table_name ADD INDEX index_name('col1','col2','col3');

索引数据结构

BTree索引:

BTree是平衡搜索多叉树,设树的度为d(d>1),高度为h,那么BTree要满足以一下条件:

每个叶子结点的高度一样,等于h;

每个非叶子结点由n-1个key和n个指针point组成,其中d<=n<=2d,key和point相互间隔,结点两端一定是key;

叶子结点指针都为null;

非叶子结点的key都是[key,data]二元组,其中key表示作为索引的键,data为键值所在行的数据;

BTree的机构下,就可以使用二分查找的查找方式,查找复杂度为h*log(n),一般来说树的高度是很小,一般为3左右,因此BTree是一个非常高效的查找结构。

B+Tree索引:

B+Tree是BTree的一个变种,设d为树的度数,h为树的高度,B+Tree和BTree的不同主要在于:

​ B+Tree中的非叶子结点不存储数据,只存储键值;

​ B+Tree的叶子结点没有指针,所有键值都会出现在叶子结点上,且key存储的键值对应的数据的物理地址;

一般来说B+Tree比BTree更适合实现外存的索引结构,因为存储引擎的设计专家巧妙的利用了外存(磁盘)的存储结构,即磁盘的一个扇区是整数倍的page(页),页是存储中的一个单位,通常默认为4K,因此索引结构的节点被设计为一个页的大小,然后利用外存的“预读取”原则,每次读取的时候,把整个节点的数据读取到内存中,然后在内存中查找,已知内存的读取速度是外存读取I/O速度的几百倍,那么提升查找速度的关键就在于尽可能少的磁盘I/O,那么可以知道,每个节点中的key个数越多,那么树的高度越小,需要I/O的次数越少,因此一般来说B+Tree比BTree更快,因为B+Tree的非叶节点中不存储data,就可以存储更多的key

哈希索引:

只有memory(内存)存储引擎支持哈希索引,哈希索引用索引列的值计算该值的hashCode,然后在hashCode相应的位置存执该值所在行数据的物理位置,因为使用散列算法,因此访问速度非常快,但是一个值只能对应一个hashCode,而且是散列的分布方式,因此哈希索引不支持范围查找和排序的功能。

全文索引:

对于较大的数据集,把数据添加到一个没有FULLTEXT索引的表,然后添加FULLTEXT索引的速度比把数据添加到一个已经有FULLTEXT索引的表快。
MySQL自带的全文索引只能用于MyISAM存储引擎,如果是其它数据引擎,那么全文索引不会生效。

在MySQL中,全文索引支队英文有用,目前对中文还不支持。

在MySQL中,如果检索的字符串太短则无法检索得到预期的结果,检索的字符串长度至少为4字节,此外,如果检索的字符包括停止词,那么停止词会被忽略。

索引应用场景

需要建立索引的场景:

1、主键自动建立唯一索引
2、频繁作为查询条件的字段应该建立索引
3、外键字段应当建立索引
4、单键索引/组合索引选择问题(高并发场景适合组合索引)
5、查询中的排序字段应该建立索引
6、查询中统计或分组字段

不适合建立索引的场景:

1、频繁更新的字段
2、where条件里用不到的字段不创建索引
3、表记录少
4、经常增删改的表
5、数据重复且分布平均的表字段,因此应该只为最经常排序的数据建立索引,如果某个数据列包含许多重复的内容,没必要建立索引

索引建立公式:

如果一列数据有1000条记录,不重复的记录有900条,则索引建立的必要性为 900/1000 = 90%
建立索引的必要性 = 不重复的记录数 / 总记录数 * 100%

索引操作:

-- 创建索引
CREATE [UNIQUE] INDEX index_name ON table_name(column_name(length));
ALTER table_name ADD [UNIQUE] INDEX [index_name] ON column_name(length);
-- 删除索引
DROP INDEX [index_name] ON table_name;
-- 查看
SHOW INDEX FROM table_name

ALTER使用:

ALTER TABLE table_name ADD PRIMARY KEY(column_list);    -- 添加主键,唯一索引,且非空
ALTER TABLE table_name ADD UNIQUE index_name(column_list); -- 唯一索引,可以为null值(多个)
ALTER TABLE table_name ADD INDEX index_name(column_list); -- 添加普通索引
ALTER TABLE table_name ADD FULLTEXT index_name(column_list);    -- 索引为FULLTEXT,用于全文检索

索引优劣

  • 优势:提高了数据查询排序的效率,降低了数据库检索的IO成本,排序降低CPU消耗
  • 劣势:
    • 索引表需要占用额外的空间
    • 索引降低写入记录的速度
    • 需要不断优化索引

EXPLAIN / DESC(执行计划)

分析指标:

  • 表的读取顺序
  • 数据读取操作
  • 数据读取操作的操作类型
  • 哪些索引可以使用
  • 哪些索引被实际使用
  • 表之间的引用
  • 每张表有多少行被优化器查询
mysql> EXPLAIN SELECT * FROM t_user\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: t_user
   partitions: NULL
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 20
     filtered: 100.00
        Extra: NULL
1 row in set, 1 warning (0.00 sec)

id:决定表的读取顺序

id: select 查询的序列号,包含一组数字,表示select子句操作表的顺序
    id相同:执行顺序从上到下(一般为简单查询)
    id序号递增:id值越大,越先被执行(子查询,自内向外)
    id有相同有不同:id值越大,越先被执行,相同则顺序执行

select_type:

simple:简单的 select 查询,查询中不包含子查询或者 UNION
primary:查询中若包含任何复杂的子部分,最外层查询则被标记为 primary
subquery:子查询
derived:在 FROM 列表中包含的子查询被标记为 derived(衍生),MySQL会递归执行这些子查询,将结果放入临时表中
union:若第二个 select 出现在 union 之后,则被标记为 union 若union包含在FROM子句的子查询中,最外层select被标记为derived
union result:从union表获取结果的select

table:显示查询相关的表:

type:访问类型排列

显示查询使用的类型,最好到最差依次是:

NULL > system > const > eq_ref > ref > fulltext > ref_of_null > index_merge > unique_subquery > index_subquery > range > index > ALL

重点:NULL > system > const > eq_ref > ref > range > index > ALL

详解:

  • system:表只有一行记录(等于系统表),const类型的特例
  • const:通过索引一次就找到了,用于比较 primary key 或者 unique 索引(例如将主键置于where列表中,MySQL会将查询转换为一个常量)
  • eq_ref:唯一索引扫描,对于每个索引键,表中只有一条记录与之匹配,常见于 primary key 或者 unique 索引,和const的区别在于 eq_ref 用于联表查询的情况,按联表的主键或唯一键联合查询
  • ref:非唯一索引扫描
  • range:只检索给定范围的行,使用一个索引来选择行,key列显示哪个索引,一般为 where 后的 between, >, <, in 等查询,扫描索引比扫描全表好,因为它只需要开始于索引的某一个点,结束于另一个点,不用扫描全表
    • 不包含like
  • index:Full Index Scan 全表索引扫描,和ALL的区别在于ALL扫描数据文件,Index扫描索引树文件
  • ALL:全表扫描,扫描数据文件

possible_keys、key:

possible_keys:可能使用到的索引

key:查询实际使用的索引,查询中若使用了覆盖索引,则该索引仅出现在key列表中

覆盖索引概念:

查询字段和索引字段刚好匹配

-- 索引
index: idx_user_name_age(name, age)
-- 查询1
SELECT * FROM user -- 不匹配索引
-- 查询2
SELECT name, age FROM user  -- 匹配索引,为覆盖索引

key_len:索引长度

表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度,在不损失精度性的前提下,长度越短越好

key_len显示的是索引字段最大可能长度,并非实际使用的长度,根据表定义计算所得,不是实际检索所得

ref:

显示索引的那一列被使用了,如果可能的话是一个常数,哪些列或常量被用于查找索引列上的值

  • const:引用常量
  • testDB.table.filed:testDB库中,table表的field字段

row:大概要扫描的行数

Extra:包含不适合在其他列显示但十分重要的额外信息

  • using filesort(必须优化):会对数据使用一个外部索引排序,而不是按照表内的读取顺序进行读取,无法利用索引进行的排序称为文件排序
  • using temporary(必须优化):使用了临时表保存中间结果,MySQL在对查询结果排序时,常见于order by 和 group by
  • using index:使用了覆盖索引,避免访问了表的数据行,索引用来读取数据,如果同时出现using where 表示索引被用来执行索引键值的查找
  • using where:使用了where
  • Using index condition:会先条件过滤索引,过滤完索引后找到所有符合索引条件的数据行,随后用 WHERE 子句中的其他条件去过滤这些数据行;
  • using join buffer:使用了连接缓存
  • impossible where:where 子句的值恒为false
  • select tables optimized away:在没有GROUPBY子句的情况下,基于索引优化MIN/MAX操作或者对于MyISAM存储引擎优化
  • distinct:优化distinct,在找到第一匹配的元组后即停止找相同值的动作

猜你喜欢

转载自www.cnblogs.com/zuier/p/10555929.html