MySQL-索引

2023-10-31

一、介绍

索引是数据库对象之一,用于提高字段检索效率,使用者只需要对哪个表中哪些字段建立索引即可,其余什么都不做,数据库会自行处理。

索引提供指向存储在表的指定列中的数据值的指针,如同图书的目录,能够加快表的查询速度。但同时也增加了插入、更新和删除操作的处理时间。

二、索引的使用

2.1 实验数据准备

咱们向表中插入大量数据,进行查询。

-- 创建表
CREATE TABLE student (
  id int(11) NOT NULL COMMENT '主键',
  name varchar(50) DEFAULT NULL COMMENT '姓名',
  age int(11) DEFAULT NULL COMMENT '年龄'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- 基于存储过程插入5万条数据,为了效果明显你可以插入更多数据
CREATE PROCEDURE p_index()
BEGIN
  DECLARE i INT DEFAULT 1;
	WHILE i <= 50000 DO
		INSERT INTO student(id,name,age) VALUES(i,CONCAT('张三',i),10+i);
		SET i = i+1;
	END WHILE;
END;
-- 执行
CALL p_index();

-- 等待一会执行完毕,查询条数
SELECT COUNT(*) FROM student;  -- 50000条

查询数据看耗时:

-- 基于id查询
SELECT * FROM student WHERE id = 49007;
> OK
> 时间: 0.019s

-- 基于name查询
SELECT * FROM student WHERE name = '张三49007';
> OK
> 时间: 0.023s

2.2 索引操作

1.创建索引

create index 索引名 ON 表名(字段名);  -- 索引名:index_name

2.查询索引

show index from 表名;

3.删除索引

drop index 索引名 on 表名;

4.修改索引

alter table 表名 add index 索引名(字段名);

2.3 测试索引

① 为student表创建一个索引

create index index_student_id ON student(id);

② 测试索引对查询效率的提升

SELECT * FROM student WHERE id = 49007;
> OK
> 时间: 0.001s    -- 可以看出效率明显提升(从0.019s提升到了0.001s,提升近20倍)

三、索引分类

3.1 唯一索引

唯一索引(unique key),索引列中的值必须是唯一的,但是允许为空值。

-- 方式1:建表的时候指定
CREATE TABLE table_name(    
  字段1 类型,    
  字段2 类型,    
  ...  
  unique key (索引名(字段名));    
);

-- 方式2:建表后设置
create unique index index_name on 表名(字段名);

3.2 主键索引

主键索引(primary key)是一种特殊的唯一索引,不允许有空值。也就是说,建表时指定了主键字段,该字段本身就设置了索引。

3.3 普通索引

使用create index创建的就是普通索引。

3.4 组合索引

前面我们都是为单个字段创建索引,其实一个索引可以包含多个字段,我们称之为叫:组合索引,也叫联合索引。

create index 索引名 ON 表名(字段1,字段2,...);

-- 组合索引的最左优先(匹配)原则:
-- 组合索引的第一个字段必须出现在查询子句中,这个索引才会被用到。如果有一个组合索引(col_a,col_b,col_c),下面的情况都会用到这个索引:
	where col_a = "some value";
	where col_a = "some value" and col_b = "some value";
	where col_a = "some value" and col_c = "some value";  -- 本质等价于 where col_a = "some value";
	where col_a = "some value" and col_b = "some value" and col_c = "some value";
	where col_b = "some value" and col_a = "some value" and col_c = "some value"; -- mysql会自动优化成前一条的样子
	
-- 下面的情况就不会用到索引:
	where col_b = "aaaa";
	where col_b = "aaaa" and col_c = "ccc";

3.5 全文索引

全文索引(fulltext index),主要对字符串类型建立基于分词的索引,主要是基于CHAR、VARCHAR和TEXT的字段上,虽然可以使用like进行模糊匹配,但是其效率远低于全文索引。

① 全文创建

-- 方式1:建表时创建:
create table 表名(
    字段名1 类型,
    字段名2 类型,
    ...
    fulltext index 索引名(字段名)
);

-- 方式2:建表后创建
create fulltext index 索引名 on 表名(字段名);

② 全文使用

select ... from 表名 where match(全文索引字段名) against('检索内容');

3.6 explain

可以通过执行explain语句查看执行计划来判断是否使用索引。

explain select ... from table_name where ...;

四、聚簇索引和非聚簇索引

  • 聚簇索引:索引数据和行数据存储在一起。
  • 非聚簇索引:索引数据和行数据分开存储。

InnoDB引擎使用的是聚簇索引:数据和索引文件是.idb,该文件中即存储了索引也存储了数据本身。

MyISAM引擎使用的是非聚簇索引:索引文件.MYI和数据文件.MYD,分开存储。

在InnoDB引擎中,插入数据时一定会和索引值进行绑定,索引值默认是主键,如果没有主键找唯一键,如果没有唯一键找mysql自动生成的行id(rowid)。

五、索引建议

  • 在经常需要搜索查询的列上创建索引,可以加快搜索的速度。
  • 在作为主键的列上创建索引,强制该列的唯一性和组织表中数据的排列结构。
  • 在经常需要根据范围进行搜索的列上创建索引,因为索引已经排序,其指定的范围是连续的。
  • 在经常需要排序的列上创建索引,因为索引已经排序,这样查询可以利用索引的排序,加快排序查询时间。
  • 在经常使用WHERE子句的列上创建索引,加快条件的判断速度。
  • 为经常出现在关键字ORDER BY、GROUP BY、DISTINCT后面的字段建立索引。

六、浅谈索引底层原理

MySQL的底层数据存储的结构是B+树。
所以为了更好的学习MySQL索引,我们必须先简单了解相关的数据结构。

这个网站上可以在线演示各种数据结构。
地址:https://www.cs.usfca.edu/~galles/visualization/Algorithms.html

6.1 二叉树

二叉树(Binary tree)是树形结构的一种类型。树中的节点,最多可以有两个子节点,称为:左子树和右子树。如下图,就是一个二叉树结构。
在这里插入图片描述

6.2 二叉查找树(Binary Search Tree)

二叉查找树(Binary Search Tree)又称为:二叉排序树(Binary Sort Tree)和 二叉搜索树。
二叉查找树具有如下特点:
(1)若左子树不空,则左子树上所有结点的值均小于它的根结点的值;
(2)若右子树不空,则右子树上所有结点的值均大于它的根结点的值;
在这里插入图片描述
这样的结构好处是,有序,并且是折半查找,效率相对较高。例如:要找元素7
① 从根节点8开始,7比8小,所以去8的左子树找
② 遇到元素6,7比6大,所以去6的右子树找
③ 遇到元素7,匹配成功
可以看出,从7个元素中去找某一个元素,最多也就是3次,即:最多用树的高度次就能找到元素。

但是,在极端情况下,树会变成链表,如图:
在这里插入图片描述
因为后添加的元素大,所有,只能一直添加到右子树上,这就导致了整个树不平衡,形成了类似链表的结构,查找的效率就不高了。

为了解决这个问题,又有了红黑树。

6.3 红黑树

红黑树(Red Black Tree) 是一种自平衡二叉查找树。
他的主要思想是保证左右子树尽量平衡(即:左右子树高度尽量一致),但是红黑树不是严格意义上的平衡二叉树(AVL),因为它的左右子树高差有可能大于 1。
在这里插入图片描述

6.4 B树

在前面我们见到的几种树结构,每个节点只能有2个子节点,随着数据量的增加,会导致树的高度越来越大,从而造成查询的效率变低,那就可以让每个节点拥有多个子节点,以此减少树的高度,从而提升效率。 B树就是这样做的,其可以拥有多于2个子节点,并保持数据有序,即:多叉有序树。

在这里插入图片描述

上图中提到了磁盘块的概念,其实更准确的应该叫做:数据页,他是mysql与磁盘交互的最小单位,是mysql内部的数据结构,大小为16kb。mysql每次从磁盘中读取数据默认最小是16kb,要么不读,读了就是16kb(数据页的大小可以修改)。

因此,在16kb中如果既需要保存"索引关键字",又需要保存"数据",显然存储的数据个数是有限的,假设不考虑其他开销,一份数据+一个索引关键字占1k,16kb就是16份数据,按照上图的三层树能够存储的数据是:16*16*16 = 4096个,显然存储的数据量还是不多。

6.5 B+树

B+树在B树的基础上做了修改,他将数据保存在了叶子节点(叶子节点拥有全量数据),其余非叶子节点不保存数据,仅保存索引关键和指针信息。

在这里插入图片描述
如上图,这样做的好处是非叶子节点能够保存更多的"索引关键字范围",从而在树高不变的情况下保存更多的数据。假设还是3层的树高,如果一个索引关键字+指针信息占10字节,那么16KB中就可以保存:16*1024/10=1638.4个 ,3层树高也就是可以存储:1638*1638*16 ≈ 26830440 个。

由此得出结论:

  • B+树的非叶子节点,也称索引节点,不存储数据,只存储索引值,相比较B树来说,B+树一个节点可存储更多的索引值,使得整颗B+树变得更矮,减少I/O次数,磁盘读写代价更低。
  • B+树的叶子节点,是顺序存储的,并且数据页和数据页之间使用指针连接,范围查询性能更优。
  • B树只适合随机检索,而B+树同时支持随机检索和顺序检索。

七、回表

场景1:一张表设置了主键索引,此时,会创建一个B+树来保存数据,使用主键作为查询条件时,则仅需查找一次B+树。
例如:select * from user where id = 1;

场景2:一张表既设置了主键索引,又设置了其他字段为索引字段(例如:name字段),此时,会创建两个B+树来保存数据,第一个B+树和场景1一致,第二个B+树则是存储索引关键字和主键id的值,当使用name字段作为检索条件时,会先查找第二个B+树,基于关键字找到主键id,再用id值到第一个B+树中查找到数据。这种情况就称为:回表。
例如:select * from user where name = 'msk1024';

那么如何解决回表问题呢?
答案是:索引覆盖,最简单的方式就是创建联合索引。
案例:

-- 表t1有a,b,c三个字段,其中a是主键,b上建了索引
SELECT * FROM t1 WHERE a = 1; -- 这样不会产生回表,因为所有的数据在a的索引树中均能找到

SELECT * FROM t1 WHERE b = 2; 
-- 这样就会产生回表,因为where条件是b字段,那么会去b的索引树里查找数据,
-- 但b的索引里面只有a,b两个字段的值,没有c,那么这个查询为了取到c字段,
-- 就要取出主键a的值,然后去a的索引树去找c字段的数据。查了两个索引树,这就叫回表。


-- 索引覆盖就是查这个索引能查到你所需要的所有数据,不需要去另外的数据结构去查。
-- 其实就是不用回表。怎么避免?不是必须的字段就不要出现在SELECT里面。或者b,c建联合索引。但具体情况要具体分析,索引字段多了,存储和插入数据时的消耗会更大。


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

MySQL-索引 的相关文章

  • 使 pdo::query 静态

    当我运行下面的代码时出现此错误 我通常使用 msql 函数 但我尝试使用 PDO 代替 怎么了 致命错误 第 14 行无法静态调用非静态方法 PDO query
  • 如何在SSRS中的表上创建热图?

    如何在 SSRS 中创建这样的内容 颜色将根据行中的值 承销商 从红色变为绿色 所有这些都在一个组中 您可以通过右键单击各个单元格并根据表达式设置填充颜色来完成此操作 In the Image below I ve mistakingly
  • 查询中列的顺序重要吗?

    当从 MySQL 表中选择列时 与表中的顺序相比 选择列的顺序是否会影响性能 不考虑可能覆盖列的索引 例如 您有一个包含行 uid name bday 的表 并且有以下查询 SELECT uid name bday FROM table M
  • 根据表sql中的行替换字符串中的字符

    我需要用一些映射的字符替换字符串中的字符列表 我有一个表 dbo CharacterMappings 有 2 列 CharacterToFilter 和 ReplacementCharacter 假设这个表中有3条记录 Filter Rep
  • mysql-如何向列申请补助?

    用户名 撤销对数据库的选择 Person I set GRANT SELECT id ON database Person TO username localhost 不是工作 gt SELECT secret FROM Person Go
  • 如何使用 MySQL 的 LOAD DATA LOCAL INFILE 在导入 CSV 时将字符串日期更改为 MySQL 日期格式

    我正在使用 MySQL 的 LOAD DATA LOCAL INFILE SQL 语句将数据从 CSV 文件加载到现有数据库表中 下面是一个 SQL 语句示例 LOAD DATA LOCAL INFILE file csv INTO TAB
  • 如何在 Laravel 查询中使用多个 OR,AND 条件

    我需要 Laravel 查询帮助 我的自定义查询 返回正确结果 Select FROM events WHERE status 0 AND type public or type private 如何写这个查询Laravel Event w
  • SQLite HAVING 比较错误

    我有一个测试 SQLite 表 用于存储带有值的报告数据 CREATE TABLE IF NOT EXISTS test fact daily revenue date TEXT revenue NUMERIC product TEXT I
  • SQL Server 用分隔符分割字符串

    我有一个输入字符串 100 2 3 101 2 1 103 2 3 我想解析它并将其添加到具有 3 列的表中 因此它应该是 f x col1 col2 col3 100 2 3 类似的其他数据以逗号分隔作为记录和 作为列 Thanks ni
  • 复选框上的数据绑定

    我目前正在将数据从 SQL 数据库之一提取到我的应用程序中 我可以让它适用于我的文本框和其他项目 但是 我似乎无法让它适用于复选框 这是我正在使用的代码 DataTable dt new DataTable dt using SqlConn
  • 如何将可视选择的文本通过管道传输到 UNIX 命令并将输出附加到 Vim 中的当前缓冲区

    使用 Vim 我尝试将在可视模式下选择的文本通过管道传输到 UNIX 命令 并将输出附加到当前文件的末尾 例如 假设我们有一个 SQL 命令 例如 SELECT FROM mytable 我想做如下的事情
  • MySQL Connector/C++ 库链接错误问题

    PROBLEM 好吧 我一直在尝试遵循 MySQL Forge Wiki 和其他一些网站上的示例代码 这些网站提供了有关如何获得简单数据库连接的教程 但由于某种原因 我的项目总是因链接错误而失败 我可以我自己不明白为什么或如何解决它 我仍在
  • 如何在Mysql中仅将不同的值从一个表复制到另一个表?

    我有一个大约 2 5GB 的 MySql 数据库 表 A 具有以下列 anoid query date item rank url 我刚刚创建了另一个仅包含列的表 b query and date 我想在查询列中插入所有不同的记录 及其各自
  • MySQL 使用 ALTER IGNORE TABLE 出现重复错误

    我的 MySQL 中有一个有重复项的表 我尝试删除重复项并保留一项 我没有主键 我可以通过以下方式找到重复项 select user id server id count as NumDuplicates from user server
  • MYSQL 区分大小写的 utf8 搜索(使用 hibernate)

    我的登录表具有 utf8 字符集和 utf8 排序规则 当我想要检查用户名并检索该特定用户名的其他信息时 hql 查询会为我提供小写和大写相同的结果 我应该如何处理适用于案例的 HQL 查询 我使用 Mysql 5 和 java hiber
  • SQL 删除表并重新创建并保留数据

    在我们最初的设计中 我们搞砸了表中的外键约束 现在表已充满数据 我们无法在不删除表中所有记录的情况下更改它 我能想到的唯一解决方案是创建一个备份表并将所有记录放在那里 然后删除所有记录 更改表并开始将它们添加回来 还有其他 更好 的想法吗
  • 是否有适用于所有数据库的标准sql

    如下所示 不同数据库的语法有所不同 是否存在适用于所有数据库的标准方法 有没有什么工具可以将任意sql转换为任意sql SQL Server 2005 CREATE TABLE Table01 Field01 int primary key
  • 当所有维度值都具有 100% 重要性时处理多对多维度

    我至少会尽力保持简洁 假设我们正在跟踪一段时间内的账户余额 所以我们的事实表将包含诸如 账户余额情况表 FK 账户ID FK 日期ID Balance 显然你有一个账户维度表 and a 日期维度表 所以现在我们可以轻松地过滤帐户或日期 或
  • MySQL Connector C/C API - 使用特殊字符进行查询

    我是一个 C 程序 我有一个接受域名参数的函数 void db domains query char name 使用 mysql query 我测试数据库中是否存在域名 如果不是这种情况 我插入新域名 char query 400 spri
  • MySQL 查询计算上个月

    我想计算上个月的订单总额 我收到了从当前日期获取当月数据的查询 SELECT SUM goods total AS Total Amount FROM orders WHERE order placed date gt date sub c

随机推荐

  • 【配置文档】配置使用CGAL库的经验分享

    诸多经验贴都建议参考CGAL官方网站的步骤一步一步配置 因为我懒得看英文所以找的都是中文博客和问答 结果走了不少弯路 这里开一篇经验贴 记录错误也方便以后的查阅 本文是基于VS2017的配置和使用 这里是CGAL的使用手册 文章目录 一 需
  • Python案例篇2-pycharm import cx_Oracle模块引发的No module named ‘custom_exceptions‘

    一 问题描述 最近在自学python 然后用到Oracle数据库 于是开始学习cx Oracle模块 代码 import cx Oracle dbConnect host cx Oracle makedsn mylocalhost mypo
  • Linux防火墙查看及白名单添加

    一 临时白名单添加 执行即生效 重启防火墙后失效 查看防火墙状态 service iptables status 查看白名单列表 sudo iptables nL 添加白名单 sudo iptables I INPUT m state st
  • Spring AOP 源码分析 - 拦截器链的执行过程

    1 简介 本篇文章是 AOP 源码分析系列文章的最后一篇文章 在前面的两篇文章中 我分别介绍了 Spring AOP 是如何为目标 bean 筛选合适的通知器 以及如何创建代理对象的过程 现在我们的得到了 bean 的代理对象 且通知也以合
  • java:无法从静态上下文中引用非静态方法

    编辑以下代码 public class t public int i public void fun public static void main String args i 3 fun 编译 javac t java 得到以下报错 原因
  • c++svd算法_2020DCIC智能算法赛智慧海洋建设TOP1方案

    大家好 我是来自团队Pursuing the Past Youth的Ethan 天池ID是GrandRookie 和队友青禹小生 wbbhcb Chauncy YAO经过2个多月的 征途 最终在本届智能算法赛部分拿到了线上Top1的成绩 下
  • 蓝桥杯第十届青少年Python组省赛试题

    ns 1 3 5 8 cnt 0 for a in ns for b in ns for c in ns if a b and a c and b c print a 100 b 10 c cnt 1 print cnt for i in
  • SpringSecurity详解

    一 Spring Security简介 Spring Security是一个功能强大且高度可定制的身份验证和访问控制框架 Spring Security致力于为Java应用程序提供身份验证和授权的能力 像所有Spring项目一样 Sprin
  • Lora无线终端工作原理及优缺点

    LoRa 数据传输终端是一种基于LoRa 扩频技术的无线数据传输终端 利用 LoRa 网络为用户提供无线数据传输功能 该产品采用高性能的工业级 LoRa 方案 以嵌入式实时操作系统为软件支撑平台 同时提供 RS232 和 RS485 或 R
  • mipsel-openwrt-linux交叉编译libwebsockets

    mipsel openwrt linux交叉编译libwebsockets mipsel openwrt linux交叉编译libwebsockets 1 下载libwebsockets 2 准备条件 3 编译安装libwebsockets
  • 数据库——实体联系模型

    文章目录 1 实体 2 属性 3 联系 4 实体 联系图 5 弱实体集 1 实体 1 实体 客观存在并且可以相互区分的任何事物 可以是实际对象 也可以是抽象概念 2 属性 实体所代表的事物具有的某种特性 每个实体都可以用一组属性来刻画 例如
  • 思维导图怎么变成ppt?4个思维导图一键生成ppt的方法

    做好的思维导图如何变成一份ppt 本文罗列了4个可行方法 一起来看看吧 一 直接复制粘贴 这是最简单的方法 虽然这样可能会花费一些时间 但可以确保内容排版和布局与你想要的一致 当然 我们大可使用更高效的方法 二 导出为图片格式 大多数思维导
  • 矩阵相关定义性质全总结

    矩阵相关定义性质全总结 0 前言 矩阵是线性代数中的核心内容 所以我写这篇文章对矩阵 研究生以下阶段 进行一个完整的叙述 虽然是主要说矩阵 但是我也会将行列式 向量 线性方程组三个方面也包含在内 不过是概述的形式 具体的叙述会另外展开写 能
  • C++沉思录读书笔记1.如何定义一个完整的类

    C 沉思录 Ruminations On C 读书笔记1 如何定义一个完整的类 作者 2006 4 27 12 19 C 哲学 只为用到的东西付出代价 定义一个类时必须搞清楚的几个问题 需要构造函数吗 如果答案为 no 那么很可能你需要定义
  • 关于转义字符&

    1 情况是这样的 就是前段传的xml参数里存在 这种特殊字符 所以前端需要转义后再传给后端 也就是 转义为 后传给后端 但是后端接收但这个参数时 会拼接url 就像下面这样的 http www xx com path api gender
  • 【Colab】基本操作【LeNet】【MNIST】训练测试

    文章目录 1 介绍 2 查看基本配置 2 1查看pytorch版本 2 2查看是否可以使用cuda 2 3查看显卡配置 3 挂载 31 挂载谷歌云盘 3 2更改运行目录 4 训练 5 Reference Colab 官网初始界面 1 介绍
  • python函数用法之numpy.mgrid

    参考链接 python笔记 numpy中mgrid的用法 布衣小张 CSDN博客 mgrid numpy中的mgrid函数 KangLongWang的博客 CSDN博客 mgrid函数 mgrid函数返回多维结构 np mgrid 第1维
  • ISP图像处理流程

    文章目录 前言 ISP图像处理流程 总结 参考 前言 因工作需要 今天看了ISP图像处理的基本流程 为了检验自己的理解情况 这里根据自己的理解写下这篇文章 如有错误 敬请原谅 ISP图像处理流程 ISP Image Sensor Proce
  • 后台管理系统项目

    1 项目名称 后台管理 2 技术栈 vue全家桶 element ui axios less eachers 3 项目亮点 性能优化 百万级项目 新旧系统更迭 权限把控 项目开发流程 1 安装vue脚手架 2 vue create 项目名称
  • MySQL-索引

    一 介绍 索引是数据库对象之一 用于提高字段检索效率 使用者只需要对哪个表中哪些字段建立索引即可 其余什么都不做 数据库会自行处理 索引提供指向存储在表的指定列中的数据值的指针 如同图书的目录 能够加快表的查询速度 但同时也增加了插入 更新