MySQL为数据表建立索引的原则详解

目录
  • 1.索引是什么?
  • 2.索引的优点?
  • 3.索引的缺点?
  • 4.在建立索引的时候,都有哪些需要考虑的因素呢?
    • 1、只为用于搜索、排序、分组的列创建索引
    • 2、索引列的类型尽量小
    • 3、为列前缀建立索引
    • 4、覆盖索引
    • 5、让索引列以列名的形式在搜索条件中单独出现
    • 6、新插入记录时主键大小对效率的影响
    • 7、冗余和重复索引
  • 总结

面试题:

  • 索引是什么?
  • 索引的优点?
  • 索引的缺点?
  • 在建立索引的时候都有哪些需要考虑的因素呢?
  • 为数据表建立索引的原则有哪些?
  • 什么是索引覆盖?
  • 非聚簇索引一定会回表查询吗?

1. 索引是什么?

索引是帮助我们实现快速查询数据库的数据结构。
在很多时候,表中存放的记录非常的多,需要用到好多的数据页来存放这些记录,在很多页中查找记录可以分为2个步骤:

1、定位到记录所在的页

2、从所在页内查找相应的记录

在没有索引的情况下,无论是根据主键列还是其他列的值进行查找,由于我们不能快速的定位到记录所在的页,所以只能从第一页沿着双向链表一直往下找,因为需要遍历所有的数据页,这种方式超级消耗性能。因此人们期望一种能高效完成搜索的方法,因此就出现了索引,索引就是一种数据结构。

2. 索引的优点?

索引可以让服务器快速的定位到表的指定位置 (索引大大减少了服务器需要需要扫描的数据量)。最常见的B-Tree索引,按照顺序存储数据,所以mysql可以来做order bygroup by操作(索引可以帮助服务器避免排序和临时表)。因为数据是有序的,所以B-Tree也就会将相关的列值都存储在一起(索引可以将随机IO变为顺序IO)。因为索引中存储了实际的列值,所以某些查询值使用索引就能完成全部查询。因此,总结下来的索引的优点为:

1、索引大大减少了服务器需要需要扫描的数据量;

2、索引可以帮助服务器避免排序和临时表;

3、索引可以将随机IO变为顺序IO;

3. 索引的缺点?

1、空间上的代价:

建立索引需要占用物理空间

InnoDB存储引擎默认使用的B+树索引,每建立一个索引,都要为它建立一颗B+树,每一颗B+树的每一个节点都是一个数据页,一个数据页默认会占用16KB的存储空间,而一颗很大的B+树由许多数据页组成,这将占用很大的一片存储空间。

2、时间上的代价:

因为每次对表记录进行增删改,需要进行动态维护索引,导致增删改时间变长

每当对表中的数据进行增删改查操作时,都需要修改各个B+树索引,B+树中的每层节点都按照索引列的值从小到大的顺序组成了双向链表,无论是叶子节点中的记录还是非叶子节点中的记录都按照索引列的值从小到大的顺序排成了单向链表。而增删改查操作可能会对节点和记录的排序造成破坏,所以存储引擎需要额外的时间进行页面分裂、页面回收等操作,以维护节点和记录的顺序。

在执行查询语句前,首先要生成一个执行计划,一般情况下,一条查询语句在执行过程中最多使用一个二级索引,因此,在生成执行计划时需要计算使用不同索引执行查询时所需的成本,最后选取成本最小的那个索引执行查询,如果建立了太多的索引,可能会导致成本分析耗时太多,从而影响查询语句的执行性能。

4. 在建立索引的时候,都有哪些需要考虑的因素呢?

在创建和使用索引是应该注意下列事项:

  • 只为用于搜索、排序、分组的列创建索引;
  • 当列中不重复值的个数在总记录条数中的占比很大时,才为列建立索引;
  • 索引列的类型尽量小;
  • 可以只为索引列前缀创建索引,以减少苏呕心占用的存储空间;
  • 尽量使用覆盖索引进行查询,以毕淼回表操作带来的性能损耗;
  • 让索引列以列名的形式单独出现在搜索条件中;
  • 为了尽可能少的让聚簇索引发生页面分裂的情况,建议让主键拥有anto_increment属性;
  • 定位并删除表中的冗余和重复索引;

建立一张数据库表:

create table single_table(
	id int not auto_increment,
	key1 varchar(100),
	key2 int,
	key3 varchar(100),
	key_part1 varchar(100),
	key_part2 varchar(100),
	key_part3 varchar(100),
    common_field varchar(100),
	primary key(id),          # 聚簇索引
	key idx_key1(key1),       # 二级索引
	unique key uk_key2(key2), # 二级索引,而且该索引是唯一二级索引
	key idx_key3(key3),       # 二级索引
	key idx_key_part(key_part1,key_part2,key_part3) # 二级索引,也是联合索引
)Engine=InnoDB CHARSET=utf8;

1、只为用于搜索、排序、分组的列创建索引

我们只为出现在where子句中的列、order bygroup by子句中的列、连接子句中的连接列创建索引。仅出现在查询列表中的列就没有必要建立索引了。

比如下面的查询语句:

select common_field,key_part3 from single_table where key1='a';

我们只需要为出现在where子句中的key1建立索引就可以了,而查询列表中的common_field、key_part3这两个列就没有必要建立索引了。

2、索引列的类型尽量小

在定义表结构时,要显式的指定列的类型。以整数类型为例,有tingint、mediumint、int、bigint这几种,他们占用的存储空间的大小依次递增,他们能表示的整数范围当然也是依次递增。如果想要对某个整数类型的列建立索引,在表示的整数范围允许的情况下,尽量让索引列使用较小的类型,比如使用int就不要用bigint,因为数据类型越小,索引占用的存储空间就越少,在一个数据页内就可以存放更多的记录,磁盘IO带来的性能损耗也就越小,读写效率也就越高。

这个建议对表的主键来说更加适用,因为不仅聚簇索引会存储主键值,所有的二级索引的节点都会存储一份记录的主键值,如果主键使用更小的数据类型,也就意味着能节省更多的存储空间。

3、为列前缀建立索引

一个字符串其实是由若干个字符组成的,假如字符串很长,那么存储这个字符串就会占用很多的存储空间。在为这个字符串所在的列建立索引时,就需要在对应的B+树中,把列的完整字符串存储起来,字符串越长,在索引中占用的存储空间越大。

索引列的字符串前缀其实也是排好序的,所以索引的设计人员设计了一个方案,即只将字符串的前几个字符存放到索引中,也就是说二级索引的记录中只保留字符串的前几个字符。

比如可以这样修改idx_key1索引,让索引中只保留字符串的前10个字符:

alert table single_table drop index idx_key1;
alert table single_table add index idx_key1(key1(10));

然后再执行下面的查询语句:

select * from single_table where key1='abcdefghijklmn';

由于在idx_key1的二级索引记录中只保留字符串的前10个字符,所以我们只能定位到前缀为‘abcdefghij'的二级索引记录,在扫描这些二级索引记录时再判断它们是否满足key1='abcdefghijklmn'条件,当列中存储的字符串包含的字符较多时,这种为列前缀建立索引的方式可以明显减少索引大小。

不过,在只对前缀建立索引的情况下,下面这个查询语句就不能使用索引来完成排序需求了:

select * from single_table order by key1 limit 10;

因为二级索引idx_key1中不包含完整的key1列信息,所以在仅使用idx_key1索引执行查询时,无法对key1列前10个字符相同但其余字符不同的记录进行排序,也就是说,只为列前缀建立索引的方式无法支持使用索引进行排序的需求。

4、覆盖索引

为了彻底告别回表操作带来的性能损耗,建议最好在查询列表中只包含索引列,比如这个查询语句:

select key1,id from single_table where key1>'a' and key1<'c';

由于我们只查询key1列和id列的值,所以在使用idx_key1索引来扫描('a','c')区间中的二级索引时,可以直接从获取到的二级索引记录中读出key1列和id列的值,而不需要再通过id值到聚簇索引中执行回表操作了,这样就省去了回表操作带来的性能损耗。

我们把这种索引中已经包含了所有需要读取的列的查询方式称为覆盖索引。如果索引的叶子节点中已经包含要查询的数据,那么还有必要再回表查询呢?如果一个索引包含所有需要查询的字段的值,就称为覆盖索引。

排序操作也优先使用覆盖索引进行查询,比如下面这个查询语句:

select key1 from single_table order by key1;

虽然这个查询语句中没有limit子句,但是由于可以采用覆盖索引,所以查询优化器会直接使用idx_key1索引进行排序,而不需要执行回表操作。

当然,如果业务需要查询索引列以外的列,还是要以保证业务需求为重,如无必要,最好把业务中需要的列放在查询列表中,而不是以简单的*替代。

5、让索引列以列名的形式在搜索条件中单独出现

在下面这两个查询语句中,搜索条件中的语义是一样的:

select * from single_table where key2*2<4;
select * from single_table where key2<4/2;

在第一个查询语句的搜索条件中,key2列并不是以单独列名的形式出现的,而是以key2*2这样的形式表现的,MySQL并不会尝试简化key2*2<4表达式,而是直接认为这个搜索条件不能形成合适的扫描区间来减少需要扫描的记录数量,所以该查询语句只能以全表扫描的方式来执行。

在第二个查询语句的搜索条件中,key2列并是以单独列名的形式出现的,MySQL可以分析出key2<2,这样可以减少需要扫描的记录数量。所以MySQL可能使用uk_key2来执行查询。

所以,如果想让某个查询使用索引来执行,请让索引列以列名的形式单独出现在搜索条件中。

6、新插入记录时主键大小对效率的影响

我们知道,对于一个使用InnoDB存储引擎的表来说,在没有显式创建索引时,表中的数据实际上存储在聚簇索引的叶子节点中,而且B+树的每一层数据页以及页面中的记录都是按照主键值从小到大的顺序排序的,如果新插入记录的主键值是依次递增的话,每插满一个数据页就换到下一个数据页继续插入,如果新插入记录的主键值忽大忽小,就比较麻烦了。

假设某个数据页存储的聚簇索引记录已经满了,它存储的主键值在1~100之间,此时如果再插入一提哦啊主键值为9的记录,因为这个数据页已经满了,新纪录应该插入到哪里呢?

我们需要把当前页面分裂成两个页面,把本页中的一些记录移动到新创建的一些页中,页分裂意味着性能损耗,所以如果想尽量避免这种无谓的性能损耗,最好让插入记录的主键值依次递增。即让主键拥有auto_increment属性,MySQl会自动为新插入的记录生成递增的主键值。

7、冗余和重复索引

针对single_table表,可以单独针对key_part1列建立一个idx_key_part1索引:

alert table single_table and index idx_key_part1(key_part1);

而此时我们已经有了一个针对key_part1、key_part2、key_part3列建立的联合索引idx_key_paridx_key_part索引的二级索引记录本身就是按照key_part1列的值排序的,此时再单独为key_part1列建立一个索引其实是没必要的,我们可以把整个新建的idx_key_part1索引看作一个冗余索引,该冗余索引是没有必要的。

有时,我们可能会对同一个列建立多个索引,比如下面两个添加索引的语句:

alert table single_table add unique key uk_id(id);
alert table single_table add index idx_id(id);

我们针对id列又建立了一个唯一二级索引uk_id,还建立了一个普通二级索引idx_id,可是id列本身就是single_table表的主键,InnoDB自动为该列建立了聚簇索引,此时uk_ididx_id就是重复的,这种重复索引应该避免。

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注我们的更多内容!

(0)

相关推荐

  • mysql数据库之索引详细介绍

    目录 思维导图 简单理解 索引模型的演变 二叉查找树 自平衡二叉树 B树 B+树 聚集索引与二级索引 总结 如果你想深入了解为什么mysql可以快速的进行检索数据,那么你一定要来了解一下mysql的索引原理 思维导图 简单理解 你可以把索引理解为一本书的目录,我们可以通过索引快速的找到我们需要的数据,大概就像下面这个图,索引就像是右边的二叉树,每个节点指向具体的数据的物理地址,先通过二叉树找到数据的位置,然后再去物理磁盘中获取数据. 但是不同的二叉树的特性不同,我们还要选择合适的树来作为索引,所

  • MySQL数据库之索引详解

    目录 一.MySQL索引简介 二.MySQL五种类型索引详解 (一)普通索引 (二)唯一性索引 (三)主键索引 (四)复合索引 (五)全文索引 三.MySQL索引使用原则 总结 今天继续给大家介绍MySQL相关知识,本文主要内容是MySQL索引相关内容. 一.MySQL索引简介 索引是MySQL数据库为了加快数据查询的速度,给表中的某一个或者是某几个列添加的一种"目录".MySQL的索引是一个特殊的文件,但是InnoDB类型引擎(关于MySQL的引擎我们会在今后的文章中进行讲解)的表的

  • MySQL如何构建数据表索引

    理解索引概念最简单的方式是通过一个案例来进行,以下就是这样的一个案例. 假设我们需要设计一个在线的约会网站,这个网站的用户资料有许多列,例如国籍.省份.城市.性别.年龄.眼睛颜色等等.这个网站必须支持通过多种组合方式搜索用户资料.同时,也需要支持支持排序和根据用户最近在线时间和其他用户的评价返回有限的结果等等.对于这种复杂场景我们如何设计索引? 有点奇怪,首先要做的事情是要决定我们是否必须使用索引排序,或者检索后再排序是否能够接受.索引排序限制了索引和查询构建的方式.例如,在WHERE age

  • MySQL 数据库 索引和事务

    目录 1. 索引 1.1 概念 1.2 作用 1.3 索引的原理 1.3.1 减少磁盘的访问次数是构建索引的核心思想 1.3.2 B+ 树适用实现索引的底层 1.4 适用场景 1.5 使用语句 1.5.1 查看索引 1.5.2 创建索引 1.5.3 删除索引 2. 事务 2.1 概念 2.2 为什么使用事务 2.3 四大属性 2.3.1 原子性 2.3.2 一致性 2.3.3 持久性 2.3.4 隔离性 2.4 使用方法 1. 索引 1.1 概念 索引是为了加速对表中数据行的检索而创建的一种分散

  • MySQL索引底层数据结构详情

    目录 一.索引类型 1.B+树 2.MyISAM和InnoDB的B+树索引实现方式的区别(聚簇索引和非聚簇索引)? 3.非聚簇索引 4.聚簇索引的优缺点 5.哈希索引 6.自适应哈希索引 一.索引类型 1.B+树 为什么是B+树而不是B树? 首先看看B树和B+树在结构上的区别 B树结构: B+树: 可以看到: B树在每个节点上都有卫星数据(数据表中的一行数据),而B+树只在叶子节点上有卫星数据.这意味着相同大小的磁盘扇区,B+树可以存储的叶子节点更多,磁盘IO次数更少:同样也意味着B+树的查找效

  • MySQL数据优化-多层索引

    目录 一.多层索引 1.创建 2.设置索引的名称 3.from_arrays( )-from_tuples() 4.笛卡儿积方式 二.多层索引操作 1.Series 2.DataFrame 3.交换索引 4.索引排序 5.索引堆叠 6.取消堆叠 一.多层索引 1.创建 环境:Jupyter import numpy as np import pandas as pd a=pd.DataFrame(np.random.random(size=(4,4)),index=[['上半年','上半年','

  • MySQL 索引和数据表该如何维护

    查找和修复数据表冲突 数据表最糟糕的事情就是发生冲突.使用MyISAM存储引擎时,通常因为崩溃导致冲突.然而,当存在硬件故障.MySQL内部Bug或操作系统Bug时,所有的存储引擎都可能遭受索引冲突. 冲突的索引可能导致查询返回错误的结果,在没有重复值时的重复索引错误增加,甚至可能导致全表扫描或崩溃.如果你遇到过偶发的事件,例如一个你认为不会发生的错误,这个时候运行CHECK TABLE命令去检测数据表是否有冲突(注意有些数据库引擎不支持这个命令,有些则支持多种选项参数去指定如何检测表).通常,

  • MySQL为数据表建立索引的原则详解

    目录 1.索引是什么? 2.索引的优点? 3.索引的缺点? 4.在建立索引的时候,都有哪些需要考虑的因素呢? 1.只为用于搜索.排序.分组的列创建索引 2.索引列的类型尽量小 3.为列前缀建立索引 4.覆盖索引 5.让索引列以列名的形式在搜索条件中单独出现 6.新插入记录时主键大小对效率的影响 7.冗余和重复索引 总结 面试题: 索引是什么? 索引的优点? 索引的缺点? 在建立索引的时候都有哪些需要考虑的因素呢? 为数据表建立索引的原则有哪些? 什么是索引覆盖? 非聚簇索引一定会回表查询吗? 1

  • MySQL中的回表和索引覆盖示例详解

    目录 索引类型 索引结构 非聚簇索引查询 索引覆盖 总结 索引类型 聚簇索引: 叶子节点存储的是行记录,每个表必须要有至少一个聚簇索引.使用聚簇索引查询会很快,因为可以直接定位到行记录 普通索引:二级索引,除聚簇索引外的索引,即非聚簇索引.普通索引叶子节点存储的是主键(聚簇索引)的值. 聚簇索引递推规则: 如果表设置了主键,则主键就是聚簇索引 如果表没有主键,则会默认第一个NOT NULL,且唯一(UNIQUE)的列作为聚簇索引 以上都没有,则会默认创建一个隐藏的row_id作为聚簇索引 索引结

  • Mysql建表与索引使用规范详解

    一. MySQL建表,字段需设置为非空,需设置字段默认值.二. MySQL建表,字段需NULL时,需设置字段默认值,默认值不为NULL.三. MySQL建表,如果字段等价于外键,应在该字段加索引.四. MySQL建表,不同表之间的相同属性值的字段,列类型,类型长度,是否非空,是否默认值,需保持一致,否则无法正确使用索引进行关联对比.五. MySQL使用时,一条SQL语句只能使用一个表的一个索引.所有的字段类型都可以索引,多列索引的属性最多15个.六. 如果可以在多个索引中进行选择,MySQL通常

  • MySQL创建和删除数据表的命令及语法详解

    创建数据表 创建MySQL数据表需要以下信息: 表名 表字段名 定义每个表字段 语法 以下为创建MySQL数据表的SQL通用语法: CREATE TABLE table_name (column_name column_type); 以下例子中我们将在 TUTORIALS 数据库中创建数据表tutorials_tbl: tutorials_tbl( tutorial_id INT NOT NULL AUTO_INCREMENT, tutorial_title VARCHAR(100) NOT N

  • MySQL如何为表和字段取别名详解

    目录 为表和字段取别名 1.为表取别名 语法格式: 例子: 查询结果: 例子: 查询结果: 例子: 查询结果: 2.为字段取别名 语法格式: 例子 查询结果: 例子(取别名前): 查询结果: 例子(取别名后): 查询结果: 总结 为表和字段取别名 阿文之前介绍过MySQL的分组查询.集合函数查询和嵌套子查询,在编写SQL语句时有的地方使用到AS关键字为查询结果中的某一列指定一个特定的名字.在内连接查询时,则对相同的表分别指定两个不同的名字,在这里可以为字段或者表取一个表名,起到区分.标识和简化的

  • MySQL创建数据表并建立主外键关系详解

    前言 为mysql数据表建立主外键需要注意以下几点: 需要建立主外键关系的两个表的存储引擎必须是InnoDB. 外键列和参照列必须具有相似的数据类型,即可以隐式转换的数据类型. 外键列和参照列必须创建索引,如果外键列不存在索引,mysql将自动创建索引. 一.SQL语句创建数据表并设置主外键关系 create table demo.ChineseCharInfo ( ID int not null auto_increment, Hanzi varchar(10) not null, prima

  • 浅谈Mysql哪些字段适合建立索引

    1 数据库建立索引常用的规则如下: 1.表的主键.外键必须有索引: 2.数据量超过300的表应该有索引: 3.经常与其他表进行连接的表,在连接字段上应该建立索引: 4.经常出现在Where子句中的字段,特别是大表的字段,应该建立索引: 5.索引应该建在选择性高的字段上: 6.索引应该建在小字段上,对于大的文本字段甚至超长字段,不要建索引: 7.复合索引的建立需要进行仔细分析:尽量考虑用单字段索引代替: A.正确选择复合索引中的主列字段,一般是选择性较好的字段: B .复合索引的几个字段是否经常同

  • MySQL对数据表已有表进行分区表的实现

    目录 操作方式 操作过程 对现有的一个表进行创建分区表,并把数据迁移到新表,可以按时间来分区,然后这表不是实时更新,每天有一次插入操作. 时间比较充裕,但是服务器上有其他应用,使用较小资源为主要方式. 操作方式 @1 可以使用ALTER TABLE来进行更改表为分区表,这个操作会创建一个分区表,然后自动进行数据copy然后删除原表, 猜测服务器资源消耗比较大. 类似操作 ALTER TABLE tbl_rtdata PARTITION BY RANGE (Month(fld_date)) ( P

  • mysql中datetime字段建立索引并比较大小详解

    目录 1.问题背景 2.优化过程 补充:Mysql 时间Datetime 索引不生效问题 总结 1.问题背景 最近测试库查询一个表的数据,需要用到唯一的一个日期类型字段作为 where 的子查询(查询当天的数据),就正常写了个这样的 SQL,具体的表名我就不写了: # create_time 是 datetime 类型 select * from ${tablename} where date(create_time)='20220919' limit 20; 其中字段的值样本如下: 我知道我写

  • MySQL清空数据表的方法实例与分析

    MySQL数据库中,如果我们想清空数据表(删除数据表中所有内容)的话,可以通过下面两个语句来实现: truncate table table_n; delete from table_n; 实例 我们先通过实例看下通过这两种方式清空数据库的过程和结果 #delete演示 mysql> create table testforde( -> number int not null auto_increment, -> name varchar(20) not null, -> prim

随机推荐