Carry の Blog Carry の Blog
首页
  • Nginx
  • Prometheus
  • Iptables
  • Systemd
  • Firewalld
  • Docker
  • Sshd
  • DBA工作笔记
  • MySQL
  • Redis
  • TiDB
  • Elasticsearch
  • Python
  • Shell
  • MySQL8-SOP手册
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Carry の Blog

好记性不如烂键盘
首页
  • Nginx
  • Prometheus
  • Iptables
  • Systemd
  • Firewalld
  • Docker
  • Sshd
  • DBA工作笔记
  • MySQL
  • Redis
  • TiDB
  • Elasticsearch
  • Python
  • Shell
  • MySQL8-SOP手册
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • MySQL8-SOP

  • MySQL实战45讲学习笔记

    • MySQL45讲学习笔记
    • MySQL基础架构
    • MySQL日志系统 (Redo Log 与 Binlog)
    • MySQL字符串字段索引优化
      • 1. 前缀索引
        • 1.1 前缀索引的语法
        • 1.2 前缀长度选择
        • 1.3 前缀索引的局限性
      • 2. 其他字符串索引优化策略
        • 2.1 倒序索引
        • 2.2 Hash索引
      • 3. 实例分析
        • 3.1 使用前缀索引
        • 3.2 使用URL的Hash索引
      • 4. 最佳实践与建议
      • 5. 小结
    • MySQL索引原理与优化
    • MySQL锁机制详解
    • MySQL事务与MVCC机制
    • MySQL普通索引与唯一索引的选择
    • MySQL优化器如何选择索引
    • MySQL抖动刷脏页
    • 表空间管理与回收
    • count函数详解
    • 日志索引
    • orderby工作原理
    • 随机排序实现
    • SQL性能差异函数与转换
    • 慢查询分析锁与版本
    • 幻读与间隙锁
    • 加锁规则分析
    • 应急性能优化方法
    • 数据持久化保证
    • 主备一致性原理
    • 高可用架构与切换
    • 备库延迟分析与优化
    • 主备切换GTID
    • 读写分离实践与问题
    • 数据库健康检查
    • 锁与死锁
    • 数据误删恢复
    • Kill命令详解
    • 查询与内存使用分析
    • Join原理与选择
    • 临时表原理与应用
    • 内部临时表详解
    • Memory引擎详解
    • 自增ID详解
    • Insert加锁分析
    • 表复制方法比较
    • Grant与权限管理
    • 分区表详解
    • SQL语句中的Join问题
    • 自增ID用尽问题
  • 专题系列
  • MySQL实战45讲学习笔记
Carry の Blog
2024-07-28
目录

MySQL字符串字段索引优化

# MySQL字符串字段索引优化

在MySQL数据库中,字符串类型的字段(如VARCHAR、CHAR、TEXT等)索引优化是提升查询性能的重要手段。本文将详细介绍字符串索引的优化策略,重点讨论前缀索引、倒序索引和Hash索引等技术。

# 1. 前缀索引

在实际业务场景中,我们经常会遇到需要对很长的字符串建立索引的情况(如URL、电子邮箱等)。对于这类场景,MySQL提供了前缀索引功能,可以只索引字符串的一部分,从而节省索引空间,提高索引效率。

# 1.1 前缀索引的语法

CREATE TABLE t (
    id int(11) NOT NULL,
    email varchar(64) DEFAULT NULL,
    PRIMARY KEY (id),
    KEY idx_email(email(6))  -- 只索引email字段的前6个字符
) ENGINE=InnoDB;
1
2
3
4
5
6

# 1.2 前缀长度选择

前缀索引的关键是选择合适的前缀长度,需要在索引区分度和索引大小之间进行权衡:

  • 前缀太短:区分度低,可能导致大量回表
  • 前缀太长:空间占用大,降低索引效率

可以通过以下SQL查询来确定合适的前缀长度:

-- 计算完整列的选择性
SELECT COUNT(DISTINCT email) / COUNT(*) FROM t;

-- 计算不同前缀长度的选择性
SELECT 
  COUNT(DISTINCT LEFT(email, 4))/COUNT(*) AS L4,
  COUNT(DISTINCT LEFT(email, 5))/COUNT(*) AS L5,
  COUNT(DISTINCT LEFT(email, 6))/COUNT(*) AS L6,
  COUNT(DISTINCT LEFT(email, 7))/COUNT(*) AS L7
FROM t;
1
2
3
4
5
6
7
8
9
10

当某个前缀长度的选择性接近于整个字段的选择性时,通常就可以选择该长度作为前缀索引。

# 1.3 前缀索引的局限性

前缀索引虽然可以节省空间,但它也带来了一些限制:

  1. 无法使用覆盖索引:由于索引中只保存了字段的前缀,如果查询需要返回完整的字段值,则必须回表获取。
  2. 无法支持ORDER BY排序优化:前缀索引无法用于优化ORDER BY操作,因为前缀索引中只包含了字段的前几个字符。
  3. 无法支持GROUP BY分组优化:同样的原因,前缀索引也无法用于优化GROUP BY操作。

# 2. 其他字符串索引优化策略

当前缀索引无法满足需求时,可以考虑以下替代策略:

# 2.1 倒序索引

对于一些特殊场景,如果字符串的前部分区分度低,而后部分区分度高(例如邮箱的域名后缀),可以考虑使用倒序索引:

-- 创建一个存储倒序字符串的字段和索引
ALTER TABLE t ADD COLUMN email_reverse VARCHAR(64) GENERATED ALWAYS AS 
  (REVERSE(email)) VIRTUAL;
ALTER TABLE t ADD INDEX idx_email_reverse (email_reverse(6));

-- 使用时需要倒序查询条件
SELECT * FROM t WHERE email_reverse = REVERSE('example@gmail.com');
1
2
3
4
5
6
7

# 2.2 Hash索引

对于需要精确匹配而不关心范围查询的场景,可以使用Hash索引:

-- 创建一个存储Hash值的字段和索引
ALTER TABLE t ADD COLUMN email_hash VARCHAR(64) GENERATED ALWAYS AS 
  (MD5(email)) STORED;
ALTER TABLE t ADD INDEX idx_email_hash (email_hash);

-- 使用时需要对查询条件做相同的Hash处理
SELECT * FROM t WHERE email_hash = MD5('example@gmail.com') AND email = 'example@gmail.com';
1
2
3
4
5
6
7

注意:需要在WHERE子句中额外增加原始字段的判断条件,因为Hash函数可能存在冲突。

# 3. 实例分析

考虑以下场景:我们有一个表存储了大量的URL地址,需要根据URL进行查询。

CREATE TABLE web_pages (
    id INT NOT NULL AUTO_INCREMENT,
    url VARCHAR(2048) NOT NULL,
    content TEXT,
    PRIMARY KEY (id)
);
1
2
3
4
5
6

# 3.1 使用前缀索引

-- 添加URL的前缀索引
ALTER TABLE web_pages ADD INDEX idx_url_prefix (url(20));

-- 查询时直接使用完整URL
EXPLAIN SELECT * FROM web_pages WHERE url = 'https://www.example.com/articles/mysql-optimization';
1
2
3
4
5

执行计划会显示使用了前缀索引,但可能需要回表检查完整的URL是否匹配。

# 3.2 使用URL的Hash索引

-- 添加URL的Hash值字段
ALTER TABLE web_pages ADD COLUMN url_hash VARCHAR(32) GENERATED ALWAYS AS (MD5(url)) STORED;
ALTER TABLE web_pages ADD INDEX idx_url_hash (url_hash);

-- 查询时使用Hash值
EXPLAIN SELECT * FROM web_pages WHERE url_hash = MD5('https://www.example.com/articles/mysql-optimization') 
  AND url = 'https://www.example.com/articles/mysql-optimization';
1
2
3
4
5
6
7

这种方式对于精确匹配的查询效率会更高,但不支持LIKE等模糊查询。

索引选择示意图

# 4. 最佳实践与建议

  1. 评估场景需求:在选择字符串索引策略前,先评估应用场景的需求(精确匹配、范围查询、排序、分组等)。

  2. 考虑字段特性:分析字符串的分布特性,确定区分度高的部分是在前部、后部还是分散的。

  3. 测试不同策略:在生产环境应用前,使用真实数据集测试不同索引策略的性能表现。

  4. 混合使用:对于复杂场景,可以混合使用不同的索引策略,例如同时建立前缀索引和Hash索引。

  5. 定期维护:随着数据量的增长和业务变化,定期评估并调整索引策略。

  6. 避免过度索引:索引会占用额外的存储空间并影响写入性能,避免创建不必要的索引。

# 5. 小结

字符串字段的索引优化是一个需要权衡多方面因素的复杂问题。MySQL提供了多种字符串索引策略,包括前缀索引、倒序索引和Hash索引等。根据具体的业务场景和数据特性,选择合适的索引策略可以显著提升查询性能,优化数据库整体运行效率。

在实际应用中,我们应该综合考虑查询模式、字符串特性、存储开销和维护成本等因素,制定最适合自己系统的索引策略。

#MySQL#索引优化#字符串索引#前缀索引#学习笔记
上次更新: 6/21/2025

← MySQL日志系统 (Redo Log 与 Binlog) MySQL索引原理与优化→

最近更新
01
表空间管理与回收
06-21
02
MySQL抖动刷脏页
06-21
03
count函数详解
06-21
更多文章>
Theme by Vdoing
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式