索引 和 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,在找到第一匹配的元组后即停止找相同值的动作