7.4.4 【MySQL】索引字符串值的前缀

2023-09-19 10:33:11

我们知道一个字符串其实是由若干个字符组成,如果我们在 MySQL 中使用 utf8 字符集去存储字符串的话,编码一个字符需要占用 1~3 个字节。假设我们的字符串很长,那存储一个字符串就需要占用很大的存储空间。在我们需要为这个字符串列建立索引时,那就意味着在对应的 B+ 树中有这么两个问题:

  • B+ 树索引中的记录需要把该列的完整字符串存储起来,而且字符串越长,在索引中占用的存储空间越大。
  • 如果 B+ 树索引中索引列存储的字符串很长,那在做字符串比较时会占用更多的时间。

我们前边儿说过索引列的字符串前缀其实也是排好序的,所以索引的设计者提出了个方案 --- 只对字符串的前几个字符进行索引也就是说在二级索引的记录中只保留字符串前几个字符。这样在查找记录时虽然不能精确的定位到记录的位置,但是能定位到相应前缀所在的位置,然后根据前缀相同的记录的主键值回表查询完整的字符串值,再对比就好了。这样只在 B+ 树中存储字符串的前几个字符的编码,既节约空间,又减少了字符串的比较时间,还大概能解决排序的问题,何乐而不为,比方说我们在建表语句中只对 name 列的前10个字符进行索引可以这么写:

CREATE TABLE person_info( 
name VARCHAR(100) NOT NULL, 
birthday DATE NOT NULL,
 phone_number CHAR(11) NOT NULL, 
 country varchar(100) NOT NULL, 
 KEY idx_name_birthday_phone_number (name(10), birthday, phone_number)); 

name(10) 就表示在建立的 B+ 树索引中只保留记录的前 10 个字符的编码,这种只索引字符串值的前缀的策略是我们非常鼓励的,尤其是在字符串类型能存储的字符比较多的时候。

7.4.4.1 索引列前缀对排序的影响

如果使用了索引列前缀,比方说前边只把 name 列的前10个字符放到了二级索引中,下边这个查询可能就有点儿尴尬了:

SELECT * FROM person_info ORDER BY name LIMIT 10;

因为二级索引中不包含完整的 name 列信息,所以无法对前十个字符相同,后边的字符不同的记录进行排序,也就是使用索引列前缀的方式无法支持使用索引排序,只好乖乖的用文件排序。

7.4.5 让索引列在比较表达式中单独出现

假设表中有一个整数列 my_col ,我们为这个列建立了索引。下边的两个 WHERE 子句虽然语义是一致的,但是在效率上却有差别:

1. WHERE my_col *

2 < 42. WHERE my_col < 4/2

第1个 WHERE 子句中 my_col 列并不是以单独列的形式出现的,而是以 my_col * 2 这样的表达式的形式出现的,存储引擎会依次遍历所有的记录,计算这个表达式的值是不是小于 4 ,所以这种情况下是使用不到为 my_col 列建立的 B+ 树索引的。而第2个 WHERE 子句中 my_col 列并是以单独列的形式出现的,这样的情况可以直接使用B+ 树索引。

所以结论就是:如果索引列在比较表达式中不是以单独列的形式出现,而是以某个表达式,或者函数调用形式出现的话,是用不到索引的。

7.4.6 主键插入顺序

我们知道,对于一个使用 InnoDB 存储引擎的表来说,在我们没有显式的创建索引时,表中的数据实际上都是存储在 聚簇索引 的叶子节点的。而记录又是存储在数据页中的,数据页和记录又是按照记录主键值从小到大的顺序进行排序,所以如果我们插入的记录的主键值是依次增大的话,那我们每插满一个数据页就换到下一个数据页继续插,而如果我们插入的主键值忽大忽小的话,这就比较麻烦了,假设某个数据页存储的记录已经满了,它存储的主键值在 1~100 之间:

如果此时再插入一条主键值为 9 的记录,那它插入的位置就如下图:

可这个数据页已经满了啊,再插进来咋办呢?我们需要把当前页面分裂成两个页面,把本页中的一些记录移动到新创建的这个页中。页面分裂和记录移位意味着什么?意味着:性能损耗!所以如果我们想尽量避免这样无谓的性能损耗,最好让插入的记录的主键值依次递增,这样就不会发生这样的性能损耗了。所以我们建议:让主键具有 AUTO_INCREMENT ,让存储引擎自己为表生成主键,而不是我们手动插入 ,比方说我们可以这样定义person_info 表:

CREATE TABLE person_info( 
id INT UNSIGNED NOT NULL AUTO_INCREMENT, 
name VARCHAR(100) NOT NULL, 
birthday DATE NOT NULL, 
phone_number CHAR(11) NOT NULL, 
country varchar(100) NOT NULL, 
PRIMARY KEY (id), 
KEY idx_name_birthday_phone_number (name(10), birthday, phone_number)); 

我们自定义的主键列 id 拥有 AUTO_INCREMENT 属性,在插入记录时存储引擎会自动为我们填入自增的主键值。

7.4.7 冗余和重复索引

有时候有的同学有意或者无意的就对同一个列创建了多个索引,比方说这样写建表语句:

CREATE TABLE person_info( 
id INT UNSIGNED NOT NULL AUTO_INCREMENT, 
name VARCHAR(100) NOT NULL, 
birthday DATE NOT NULL, 
phone_number CHAR(11) NOT NULL, 
country varchar(100) NOT NULL, 
PRIMARY KEY (id), 
KEY idx_name_birthday_phone_number (name(10), birthday, phone_number), 
KEY idx_name (name(10))); 

我们知道,通过 idx_name_birthday_phone_number 索引就可以对 name 列进行快速搜索,再创建一个专门针对name 列的索引就算是一个 冗余 索引,维护这个索引只会增加维护的成本,并不会对搜索有什么好处。

另一种情况,我们可能会对某个列重复建立索引,比方说这样:

CREATE TABLE repeat_index_demo ( 
c1 INT PRIMARY KEY, 
c2 INT, 
UNIQUE uidx_c1 (c1), 
INDEX idx_c1 (c1)); 

我们看到, c1 既是主键、又给它定义为一个唯一索引,还给它定义了一个普通索引,可是主键本身就会生成聚簇索引,所以定义的唯一索引和普通索引是重复的,这种情况要避免。

7.5 总结

  1. B+ 树索引在空间和时间上都有代价,所以没事儿别瞎建索引。
  2. B+ 树索引适用于下边这些情况:
  • 全值匹配
  • 匹配左边的列
  • 匹配范围值
  • 精确匹配某一列并范围匹配另外一列
  • 用于排序
  • 用于分组
  1. 在使用索引时需要注意下边这些事项:
  • 只为用于搜索、排序或分组的列创建索引
  • 为列的基数大的列创建索引
  • 索引列的类型尽量小
  • 可以只对字符串值的前缀建立索引
  • 只有索引列在比较表达式中单独出现才可以适用索引
  • 为了尽可能少的让 聚簇索引 发生页面分裂和记录移位的情况,建议让主键拥有 AUTO_INCREMENT 属性。
  • 定位并删除表中的重复和冗余索引
  • 尽量使用 覆盖索引 进行查询,避免 回表 带来的性能损耗。
更多推荐

Service 层异常抛到 Controller 层处理还是直接处理?

0前言一般初学者学习编码和[错误处理]时,先知道[编程语言]有一种处理错误的形式或约定(如Java就抛异常),然后就开始用这些工具。但却忽视这问题本质:处理错误是为了写正确程序。可是1啥叫“正确”?由解决的问题决定的。问题不同,解决方案不同。如一个web接口接受用户请求,参数age,也许业务要求字段是0~150之间整数

JMeter:断言之响应断言

一、断言的定义断言用于验证取样器请求或对应的响应数据是否返回了期望的结果。可以是看成验证测试是否预期的方法。对于接口测试来说,就是测试Request/Response,断言即可以针对Request进行,也可以针对Response进行。但大部分是对Response做断言。JMeter常见的断言元件如下:1.响应断言2.J

【MySQL系列】如何在MySQL中使用触发器?MySQL触发器详解

MySQL可以通过触发器来实现自动化业务逻辑和操作。触发器是一种在数据库表发生特定操作时自动执行的存储过程,能够响应特定事件,如INSERT、UPDATE和DELETE语句。本文将详细介绍MySQL中的触发器概念、创建和使用方法,以及一些注意事项。一、概念触发器是一种与表相关联的一段代码,它会在特定事件(INSERT、

利用 SOAR 加快事件响应并加强网络安全

随着攻击面的扩大和攻击变得越来越复杂,与网络攻击者的斗争重担落在了安全运营中心(SOC)身上。SOC可以通过利用安全编排、自动化和响应(SOAR)平台来加强组织的安全态势。这一系列兼容的以安全为中心的软件可加快事件调查和响应速度。SOAR平台提高了对所有安全数据的可见性,简化了IT流程,自动执行了与安全相关的手动任务,

[EI复现】基于主从博弈的新型城镇配电系统产消者竞价策略(Matlab代码实现)

💥💥💞💞欢迎来到本博客❤️❤️💥💥🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。⛳️座右铭:行百里者,半于九十。📋📋📋本文目录如下:🎁🎁🎁目录💥1概述📚2运行结果🎉3参考文献🌈4Matlab代码实现💥1概述新型城镇的发展需要实现能源结构清洁化和能源利用高效

{案例分析}**市 SA-接通率CCE参数优化报告

【问题描述】SA-无线接通率主要包含了SA-RRC连接建立成功率,SA-NG接口UE相关逻辑信令连接建立成功率和SA-QosFlow建立成功率;现网接通率99.27%,【问题分析】接通率主要问题在于QosFlow建立成功率相对较差,RRC建立成功率基本处于99.6%以上,NG接通率接近百分之百;【问题根因】针对于Qos

Jtti:在centos7中怎么使用yum如何删除php

要在CentOS7中使用YUM删除PHP,请执行以下步骤:打开终端:可以通过SSH远程连接到您的CentOS服务器,或者在本地访问终端。以超级用户(root)身份登录:如果不是root用户,可以使用以下命令切换到超级用户:sudosu然后输入管理员密码以获得root权限。删除PHP:您可以使用YUM删除PHP及其相关包

pytest进阶之conftest.py

前言前面几篇随笔基本上已经了解了pytest命令使用,收集用例,finxture使用及作用范围,今天简单介绍一下conftest.py文件的作用和实际项目中如是使用此文件!实例场景首先们思考这样一个问题:如果我们在编写测试用的时候,每一个测试文件里面的用例都需要先登录后才能完成后面的操作,那么们该如何实现呢?这就需要我

Python入门自学进阶-Web框架——42、Web框架了解-bottle、flask

WEB框架的三大组件:路由系统、控制器(含模板渲染)、数据库操作微型框架:依赖第三方写的socket,WSGI,本身功能少安装:pipinstallbottlepipinstallflask安装flask,同时安装了MarkupSafe、Werkzeug、Jinja2、itsdangerous。Werkzeug是Pyt

【Linux】生产者消费者模型

文章目录一、生产者消费者模型1.生产者消费者模型的概念2.生产者消费者之间的关系3.生产者和消费者的特点二、基于BlockingQueue的生产者消费者模型1.单生产单消费随机数任务计算器任务Task2.多生产多消费3.为什么生产者消费者模型高效三、基于环形队列的生产消费模型1.POSIX信号量2.基于环形队列的生产消

java版工程管理系统Spring Cloud+Spring Boot+Mybatis实现工程管理系统源码

工程项目管理软件(工程项目管理系统)对建设工程项目管理组织建设、项目策划决策、规划设计、施工建设到竣工交付、总结评估、运维运营,全过程、全方位的对项目进行综合管理工程项目各模块及其功能点清单一、系统管理1、数据字典:实现对数据字典标签的增删改查操作2、编码管理:实现对系统编码的增删改查操作3、用户管理:管理和查看用户角

热文推荐