十四.索引

1.索引概述

MySQL官方对索引的定义为: 索引(index)是帮助MySQL高效获取数据的数据结构(有序)。在数据之外,数据库系统还维护者满足特定查找算法的数据结构,这些数据结构以某种方式以某种方式引用(指向)数据,这样就可以在这些数据结构上实现高级查找算法,这种数据结构就是索引。如下面的示意图所示:
在这里插入图片描述
左边是数据表,一共有两列七条记录,最左边的是数据记录的物理地址(注意逻辑上相邻的记录在磁盘上也并不是一定物理相邻的)。为了加快Col2的查找,可以维护了一个右边所示的二叉查找树。每个节点分别包含了索引键值和一个指向对应数据记录物理地址的指针,这样就可以运用二叉查找快速获取到相应的数据。
一般来说索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储在磁盘上。索引是数据库中用来提高性能的最常用的工具。

2.索引优势劣势

优势
(1).类似于书籍的目录索引,提高数据检索的效率,降低数据库的IO成本。
(2).通过索引列对数据进行排序,降低数据排序成本,降低CPU的消耗。
劣势
(1).实际上索引也是一张表,该表中保存了主键与索引字段,并指向实体类的记录,所以索引列也是要占用空间的。
(2).虽然索引大大提高了查询效率,同时却也降低更新表的速度,如对表进行INSERT, UPDATE, DELETE。因为更新表时,MySQL不仅要保存数据,还要保存一下索引文件每次更新添加了索引列的字段,都会调整因为更新所带来的键值变化后的索引信息。

3.索引结构

索引是在MySQL的存储引擎层中实现的,而不是在服务器层实现的。所以每种存储引擎的索引都不一定完全相同,也不是所有的存储引擎都支持所有的索引类型的。MySQL目前提供了以下4种索引:

  • BTREE索引: 最常见的索引类型,大部分索引都支持B树索引。
  • HASH索引: 只有Memory引擎支持,使用场景简单。
  • R-tree索引(空间索引): 空间索引是MyISAM引擎的一个特殊索引类型,主要用于地理空间数据结构,通常使用较少。
  • Full-text(全文索引): 全文索引也是MyISAM的一个特殊索引类型,主要用于全文索引,InnoDB从MySQL5.6版本开始支持全文索引。
    MyISAM, InnoDB, Memory三种存储引擎对各种索引类型的支持
    索引InnoDB引擎MyISAM引擎Memory引擎
    BTREE索引支持支持支持
    HASH索引不支持不支持支持
    R-tree不支持支持不支持
    Full-text5.6版本之后支持支持不支持
    我们平常所说的索引,如果没有特别指明,都是指B+树(多路搜索树,并不一定是二叉的)结构组织的索引。其中聚集索引,符合索引,前缀索引,唯一索引默认都是使用B+tree树索引,统称为索引。

(1).BTREE结构

BTREE又叫多路平衡搜索树,一颗m叉的BTREE特性如下:

  • 树中每个节点最多包含m个孩子。
  • 除根节点与叶子节点外,每个节点至少有[ceil(m/2)]个孩子。
  • 若根节点不是叶子节点,则至少有两个孩子。
  • 所有的叶子节点都在同一层。
  • 每个非叶子节点由n个key与n+1个指针组成,其中[ceil(m/2)-1] <= n <= m-1

示例

以5叉BTREE为例,key的数量: 公式推导[ceil(m/2)-1] <= n <= m-1。所以2<=n<=4。当n>4时,中间节点分裂到父节点,两边节点分裂。
插入C N G A H E K Q M F W L T Z D P R X Y S数据为例。
演变过程如下:
[1].插入前4个字母 C N G A
在这里插入图片描述
[2].插入H, n>4, 中间元素G字母向上分裂到新的节点
在这里插入图片描述
[3].插入E, K, Q不需要分裂
在这里插入图片描述
[4].插入M,中间元素M字母向上分裂到父节点G
在这里插入图片描述
[5].插入F, W, L, T不需要分裂
在这里插入图片描述
[6].插入Z, 中间元素T向上分裂到父节点中
在这里插入图片描述
[7].插入D,中间元素D向上分裂到父节点中。然后插入P, R, X, Y不需要分裂
在这里插入图片描述
[8].最后插入S, NPQR节点n>5, 中间节点Q向上分裂,但分裂后父节点DGMT的n>5, 中间节点M向上分裂
在这里插入图片描述
到此,该BTREE树就已经构建完成了,BTREE树和二叉树相比,查询数据的效率更高,因为对于相同的数据量来说,BTREE的层级结构比二叉树小,因此搜索速度快。

(2).B+TREE结构

B+Tree为BTree的变种,B+Tree与BTree的区别为:
[1].n叉B+Tree最多含有n个key,而BTree最多含有n-1个key。
[2].B+Tree的叶子节点保存所有的key信息,依key大小顺序排列。
[3].所有的非叶子节点都可以看作是key的索引部分。
在这里插入图片描述
由于B+Tree只有叶子节点保存key信息,查询任何key都要从root走到叶子,所以B+Tree的查询效率更加稳定。

MySQL中的B+Tree

MySQL索引数据结构对经典的B+Tree进行优化。在原B+Tree的基础上,增加一个指向相邻子节点的链表指针,就形成了带有顺序指针的B+Tree,提高区间访问的性能。
MySQL中的B+Tree索引结构示意图:
在这里插入图片描述

4.索引分类

(1).单值索引: 即一个索引只包含单个列,一个表可以有多个单列索引
(2).唯一索引: 索引列的值必须唯一,但允许有空值。
(3).复合索引: 即一个索引包含多个列

5.索引语法

索引在创建表的时候,可以同时创建,也可以随时增加新的索引。

(1).创建索引

语法:

CREATE [UNIQUE|FULLTEXT|SPATIAL] INDEX index_name
[USING index_type]
on tbl_name(index_col_name, ...)
index_col_name:column_name[(length)][ASC|DESC]

示例:

create index idx_city_name on city(city_name);

(2).查看索引

语法:

show index from table_name;

示例:
查看city表中的索引信息

show index from city\G;

(3).删除索引

语法:

drop index index_name on tbl_name;

示例:
删除city表上的索引idx_city_name

drop index idx_city_name on city;

(4).ALTER命令

语法:

[1].alter table tb_name add primary key(column_list);
该语句添加一个主键,这意味着索引值必须是唯一的,且不能为NULL

[2].alter table tb_name add unique index_name(column_list);
这条语句创建索引的值必须是唯一的(除了NULL外, NULL可能出现多次)

[3].alter table tb_name add index index_name(column_list);
添加普通索引,索引值可以出现多次。

[4].alter table tb_name add fulltext index_name(column_list);
该语句指定了索引为FULLTEXT, 用于全文索引。

6.索引设计原则

索引的设计可以遵循一些已有的原则,创建索引的时候请尽量考虑符合这些原则,便于提升索引的使用效率,更高效的使用索引。
(1).对查询频次较高,且数据量比较大的表建立索引。
(2).索引字段的选择,最佳候选列应当从where子句的条件中提取,如果where子句中组合比较多,那么应当挑选最常用,过滤效果最好的组合。
(3).使用唯一索引,区分度越高,使用索引的效率越高。
(4).索引可以有效的提升查询数据的效率,但索引数量不是多多益善,索引越多,维护索引的代价自然也就水涨船高,对于插入,更新,删除等DML操作比较频繁的表来说,索引过多,会引入相当高的维护代价,降低DML操作效率,增加相应操作的时间消耗。另外索引过多的话,MySQL也会犯选择困难病,虽然最终仍然会找到一个可用的索引,但无疑提高了选择的代价。
(5).使用短索引,索引创建之后也是使用硬盘来存储的,因此提升索引访问的I/O效率,也可以提升总体的访问效率。胶乳构成索引的字段总长度比较短,那么在给定的存储块内可以存储更多的索引值,相应的可以有效的提升MySQL访问索引的I/O效率。
(6).利用最左前缀,N个列组合而成的组合索引,那么相当于是创建了N个索引,如果查询时where子句中使用了组成该索引的前几个字段,那么这条查询SQL可以利用组合索引来提升效率。例如以下示例

创建复合索引:
create index idx_name_email_status on tb_seller(name, email, status);

就相当于
   对 name 创建了索引
   对 name, email 创建了索引
   对 name, email, status 创建了索引

7.索引的使用

索引是数据库优化最常用也是最重要的手段之一,通过索引通常可以帮助用户解决大多数的MySQL的性能优化问题。

(1).验证索引提升查询效率

示例:
在我们准备的表结构tb_item中,一共存储了300万记录
[1].根据id查询

select * from tb_item where id = 1999\G;

在这里插入图片描述
查询速度很快,接近0s,主要原因是因为id为主键,有索引;
[2].根据名称查询

select * from tb_item where title = '阿尔卡特(OT-979) 冰川白 联通3G手机3';

在这里插入图片描述
查询速度较长,为4.6s,主要原因是因为title无索引;
现在针对title字段,创建索引:

create index idx_item_title on tb_item(title);

索引创建完之后,再次进行查询:
在这里插入图片描述

(2).索引的使用

[1].准备环境

-- 创建表
create table tb_seller(
    sellerid varchar(100),
    name varchar(100),
    nickname varchar(50),
    password varchar(60),
    status varchar(1),
    address varchar(100),
    createtime datetime,
    primary key(sellerid)
)engine=innodb default charset=utf8mb4;
-- 插入数据
insert into tb_seller (sellerid, name, nickname, password, status, address, createtime) values('alibaba', '阿里巴巴', '阿里小店', 'e10adc3949ba59abbe56e057f20f883e', '1', '北京市', '2088-01-01 12:00:00');
insert into tb_seller (sellerid, name, nickname, password, status, address, createtime) values('baidu', '百度科技有限公司', '百度小店', 'e10adc3949ba59abbe56e057f20f883e', '1', '北京市', '2088-01-01 12:00:00');
insert into tb_seller (sellerid, name, nickname, password, status, address, createtime) values('huawei', '华为科技有限公司', '华为小店', 'e10adc3949ba59abbe56e057f20f883e', '1', '北京市', '2088-01-01 12:00:00');
insert into tb_seller (sellerid, name, nickname, password, status, address, createtime) values('itcast', '传智播客教育有限公司', '传智播客', 'e10adc3949ba59abbe56e057f20f883e', '1', '北京市', '2088-01-01 12:00:00');
insert into tb_seller (sellerid, name, nickname, password, status, address, createtime) values('itheima', '黑马程序员', '黑马程序员', 'e10adc3949ba59abbe56e057f20f883e', '1', '北京市', '2088-01-01 12:00:00');
insert into tb_seller (sellerid, name, nickname, password, status, address, createtime) values('luoji', '罗技科技有限公司', '罗技小店', 'e10adc3949ba59abbe56e057f20f883e', '1', '北京市', '2088-01-01 12:00:00');
insert into tb_seller (sellerid, name, nickname, password, status, address, createtime) values('oppo', 'OPPO科技有限公司', 'OPPO官方旗舰店', 'e10adc3949ba59abbe56e057f20f883e', '0', '北京市', '2088-01-01 12:00:00');
insert into tb_seller (sellerid, name, nickname, password, status, address, createtime) values('ourpalm', '掌趣科技股份有限公司', '掌趣小店', 'e10adc3949ba59abbe56e057f20f883e', '1', '北京市', '2088-01-01 12:00:00');
-- 创建索引
create index idex_seller_name_sta_addr_on on tb_seller(name, status ,address);

[2].避免索引失效

[a].全值匹配,对索引中所有列都指定具体值。
改情况下,索引生效,执行效率高。

explain select * from tb_seller where name = '小米科技' and status = '1' and address = '北京市'\G;

在这里插入图片描述
[b].最左前缀法则:
如果索引了多列,要遵循最左前缀法则。指的是查询从索引的最左前列开始,并且不跳过索引中的列。
匹配最左前缀法则,走索引:
在这里插入图片描述
在这里插入图片描述
违反最左前缀法则,索引失效:
在这里插入图片描述
[c].范围查询右边的列,不能使用索引
在这里插入图片描述
根据前面的两个字段name, status查询是走索引的,但是最后一个条件address没有用到索引。
[d].不要在索引列上进行运算操作,索引将失效。
在这里插入图片描述
[e].字符串不加单引号,造成索引失效.
在这里插入图片描述
[f].尽量使用覆盖索引,避免select *
尽量使用覆盖索引(只访问索引的查询(索引列完全包括查询列)),减少select *
在这里插入图片描述
如果查询列,超出索引列,也会降低性能。
在这里插入图片描述
小结:
using index: 使用覆盖索引的时候就会出现
using where: 在查找使用索引的情况下,需要回表去查询所需的数据
using index condition: 查找使用了索引,但是需要回表查询数据
using index; using where: 查找使用了索引,但是需要的数据都在索引列中能找到,所以不需要回表查询数据。
[g].用or分隔开的条件,如果or前的条件中的列有索引,而后面的列中没有索引,那么涉及的索引都不会被用到。
示例:
name字段是索引列,而createtime不是索引列,中间是or进行连接是不走索引的:

explain select * from tb_seller where name ='黑马程序员' or createtime = '2088-01-01 12:00:00';\G;

在这里插入图片描述
[h].以%开头的Like模糊查询,索引失效。
如果仅仅是尾部模糊匹配,索引不会失效。如果头部模糊匹配,索引失效。
在这里插入图片描述
解决方案:
通过覆盖索引
[i].如果MySQL评估使用索引比全表更慢,则不使用索引。
在这里插入图片描述
[j].is NULL, is NOT NULL 有时索引失效。
在这里插入图片描述
[k].in走索引,not in索引失效。
在这里插入图片描述
[l].单列表和复合索引
尽量使用复合索引,而少使用单列索引。
创建复合索引

create index idx_name_sta_address on tb_seller(name, status, address);

就相当于创建了三个索引:
        name
        name + status
        name + status + address

创建单列索引

create index idx_seller_name on tb_seller(name);
create index idx_seller_status on tb_seller(status);
create index idx_seller_address on tb_seller(address);

数据库就会选择一个最优索引来使用,并不会使用全部索引。

[3].查看索引使用情况

show status like '%Handler_read%';
show global status like '%Handler_read%';

在这里插入图片描述
Handler_read_first: 索引中第一条被读的次数,如果较高,表示服务器正执行大量全索引扫描(这个值越低越好)。
Handler_read_key: 如果索引正在工作,这个值代表一个行被索引值读的次数,如果值越低,表示索引得到的性能改善不高,因为索引不经常使用(这个值越高越好)。
Handler_read_next: 按照键顺序读下一行的请求数。如果你用范围约束或如果执行索引描述来查询索引列,该值增加。
Handler_read_prev: 按照键顺序读前一行的请求数。该读方法用于优化order by … desc。
Handler_read_rnd: 根据固定位置读一行的请求数。如果你正执行大量查询并需要对结果进行排序该值较高,你可能使用了大量需要MySQL扫描整个表的查询或你的连接没有正确使用键,意味着运行效率低,应该建立索引来补救。
Hander_read_rnd_next: 在数据文件中读下一行的请求数。如果你正在大量的表扫描,该值较高。通常说明你的表索引不正确或写入的查询没有利用索引。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
当然,以下是一个删除Elasticsearch中三十天外索引数据的Shell脚本: ``` #!/bin/bash # 设置Elasticsearch的主机名、端口和索引的前缀 ES_HOST="localhost" ES_PORT="9200" INDEX_PREFIX="my-index-" # 计算30天前的时间戳 CURRENT_TIME=$(date +%s) DELETE_BEFORE=$(expr $CURRENT_TIME - 2592000) # 获取所有符合条件的索引名称 INDICES=$(curl -s -XGET "http://${ES_HOST}:${ES_PORT}/_cat/indices/${INDEX_PREFIX}*?h=index" | grep "${INDEX_PREFIX}" | awk '{print $1}') # 循环遍历所有符合条件的索引,并删除早于30天的索引 for INDEX in $INDICES do INDEX_DATE=$(echo $INDEX | awk -F"${INDEX_PREFIX}" '{print $2}') INDEX_TIME=$(date -d"${INDEX_DATE}" +%s) if [ $INDEX_TIME -lt $DELETE_BEFORE ]; then echo "Deleting index ${INDEX}..." curl -XDELETE "http://${ES_HOST}:${ES_PORT}/${INDEX}" fi done ``` 这个脚本首先定义了Elasticsearch的主机名、端口和索引前缀。然后,它计算了30天前的时间戳,并使用`curl`命令获取所有符合条件的索引名称。最后,它循环遍历所有符合条件的索引,并删除早于30天的索引。在循环中,它首先计算每个索引的日期,并将其转换为时间戳。如果索引时间戳早于删除时间戳,它将使用`curl`命令删除该索引。 请注意,这个脚本假设索引名称的格式为`my-index-YYYY.MM.DD`,其中`YYYY`是四位数的年份,`MM`是两位数的月份,`DD`是两位数的日期。如果您的索引名称不是这个格式,您需要相应地修改脚本。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值