为MySQL中的JSON字段设置索引的两种方法

 更新时间:2025年09月09日 08:34:10   作者:程序新视界  
MySQL在2015年中发布的5.7.8版本中首次引入了JSON数据类型,自此,它成了一种逃离严格列定义的方式,可以存储各种形状和大小的JSON文档,虽然MySQL提供了读写JSON数据的函数,但JSON缺失了索引功能,本文介绍了如何为MySQL中的JSON字段设置索引,需要的朋友可以参考下

背景

MySQL在2015年中发布的5.7.8版本中首次引入了JSON数据类型。自此,它成了一种逃离严格列定义的方式,可以存储各种形状和大小的JSON文档,例如审计日志、配置信息、第三方数据包、用户自定义字段等。

虽然MySQL提供了读写JSON数据的函数,但你很快会发现一个显著的缺失:直接给JSON列建立索引的能力。

在其他数据库中,直接索引JSON列的最佳方法通常是使用一种叫做广义倒排索引(Generalized Inverted Index,简称GIN)的类型。然而,由于MySQL没有提供GIN索引,我们无法直接对整个存储的JSON文档建立索引。不过不必担心!MySQL确实为我们提供了一种间接索引存储在JSON文档中特定部分的方式。

根据所使用的MySQL版本,有两个选项可以给JSON建立索引:

  • 如果使用MySQL 5.7,需要创建一个中间生成列(Generated Column)
  • 从MySQL 8.0.13开始,可以直接创建函数索引(Functional Index)

接下来,我们以一个示例表为例,该表用于记录应用程序中的各种操作日志:

CREATE TABLE `activity_log` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `properties` json NOT NULL,
  `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
   PRIMARY KEY (`id`)
)

在该表的properties字段中插入如下结构的JSON文档:

{
  "uuid": "e7af5df8-f477-4b9b-b074-ad72fe17f502",
  "request": {
    "email": "little.bobby@tables.com",
    "firstName": "Little",
    "formType": "vehicle-inquiry",
    "lastName": "Bobby",
    "message": "Hello, can you tell me what the specs are for this vehicle?",
    "postcode": "75016",
    "townCity": "Dallas"
  }
}

在本例中,我们将尝试索引request对象内的email键,这可以让用户快速找到由特定人员提交的表单。

方法一:通过“生成列”索引JSON

生成列(Generated Column) 可以视为计算列、派生列或公式列。它的值是某个表达式的运算结果,而不是直接的数据输入。表达式可以包含常量值、内置函数或对其他列的引用。表达式的结果必须是定量的(Scalar)且具有确定性(Deterministic)。

由于我们试图索引properties列中的request.email字段,生成列将使用JSON的解引用(Unquoting Extraction)运算符来提取该值。

首先,运行一个SELECT语句来验证表达式是否正确:

mysql> SELECT properties->>"$.request.email" FROM activity_log;
+--------------------------------+
| properties->>"$.request.email" |
+--------------------------------+
| little.bobby@tables.com        |
+--------------------------------+

符号->>是解引用运算符,它等价于如下的写法:

mysql> SELECT JSON_UNQUOTE(JSON_EXTRACT(properties, "$.request.email"))
    ->   FROM activity_log;
+-----------------------------------------------------------+
| JSON_UNQUOTE(JSON_EXTRACT(properties, "$.request.email")) |
+-----------------------------------------------------------+
| little.bobby@tables.com                                   |
+-----------------------------------------------------------+

上述两种写法,具体使用哪种方式可完全取决于个人偏好。

确认表达式的有效性和准确性后,我们使用它创建一个生成列

ALTER TABLE activity_log ADD COLUMN email VARCHAR(255)
  GENERATED ALWAYS as (properties->>"$.request.email");

这条ALTER语句的前半部分非常熟悉,添加了一个名为email的列,并将其定义为VARCHAR(255)类型。而后半部分声明该列为生成列,并定义它始终等于表达式properties->>"$.request.email"的结果。

我们可以像其他列一样查询它,确认生成列已被成功添加:

mysql> SELECT id, email FROM activity_log;
+----+-------------------------+
| id | email                   |
+----+-------------------------+
|  1 | little.bobby@tables.com |
+----+-------------------------+

从结果可以看到,MySQL将动态维护这个列。如果我们更新了JSON数据,生成列的值也会随之改变。

接下来,我们像其他普通列一样为这生成列添加索引:

ALTER TABLE activity_log ADD INDEX email (email) USING BTREE;

现在已经成功为JSON中request.email键建立了索引。可以通过EXPLAIN验证索引是否会被用于查询:

mysql> EXPLAIN SELECT * FROM activity_log WHERE email = 'little.bobby@tables.com';

结果显示MySQL计划使用email索引来满足该查询。

索引生成列与优化器(Optimizer)

MySQL的优化器是一个强大但神秘的组件。当我们给MySQL下达命令时,它理解的是我们想要什么,而不是我们明确指定如何实现。通常,MySQL会稍微改写我们的查询,这通常是一件好事。

对于生成列上的索引,优化器能“透过”不同的访问模式以确保使用索引。例如,在以下查询中,我们通过JSON提取运算符访问数据,而不是直接使用生成的email列:

mysql> EXPLAIN SELECT * FROM activity_log
    ->   WHERE properties->>"$.request.email" = 'little.bobby@tables.com';

结果可以看到优化器仍然使用了email索引。哪怕使用长写的表达式,也可以看到优化器仍然“穿透”表达式并利用了索引,甚至可以通过SHOW WARNINGS查看优化器改写后的查询:

mysql> SHOW WARNINGS;

显示结果表明查询被改写为直接参考了索引的列。

方法二:函数索引(Functional Index)

从MySQL 8.0.13开始,可以跳过创建生成列的中间步骤,直接创建表达式索引(Function Index)。例如:

ALTER TABLE activity_log
  ADD INDEX email ((properties->>"$.request.email")) USING BTREE;

然而,当你尝试运行上述语句时会遇到错误:

ERROR: Cannot create a functional index on an expression that returns a BLOB or TEXT. Please consider using CAST.

这是因为MySQL自动推断JSON解引用操作返回LONGTEXT类型,而无法对其直接建立索引。可通过CAST将值转化为MySQL可索引的数据类型:

ALTER TABLE activity_log
  ADD INDEX email ((CAST(properties->>"$.request.email" AS CHAR(255)))) USING BTREE;

此外还需要解决字符集不匹配的问题,需要显式设置排序规则为utf8mb4_bin

ALTER TABLE activity_log
  ADD INDEX email ((
    CAST(properties->>"$.request.email" AS CHAR(255)) COLLATE utf8mb4_bin
  )) USING BTREE;

运行EXPLAIN后可以确认函数索引已成功被使用。

总结

尽管MySQL无法直接对JSON列建立索引,但通过生成列和函数索引的方式间接索引特定字段能够满足绝大多数场景。同时这种方式不仅适用于JSON,还适用于其它复杂或难以索引的模式。

到此这篇关于为MySQL中的JSON字段设置索引的两种方法的文章就介绍到这了,更多相关MySQL JSON设置索引内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • IDEA找不到Database的完美解决方法

    IDEA找不到Database的完美解决方法

    IntelliJ IDEA中可以用database来连接数据库,但也会经常遇到问题,下面这篇文章主要给大家介绍了关于IDEA找不到Database的完美解决方法,文中通过图文介绍的非常详细,需要的朋友可以参考下
    2023-05-05
  • 使用MySQL MySqldump命令导出数据时的注意事项

    使用MySQL MySqldump命令导出数据时的注意事项

    这篇文章主要介绍了使用MySQL MySqldump命令导出数据时的注意事项,很实用的经验总结,需要的朋友可以参考下
    2014-07-07
  • CentOS7 通过YUM安装MySQL5.7的步骤详解

    CentOS7 通过YUM安装MySQL5.7的步骤详解

    这篇文章主要介绍了CentOS7 通过YUM安装MySQL5.7的步骤详解,本文给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-01-01
  • KingbaseES中的MySQL案例实战讲解

    KingbaseES中的MySQL案例实战讲解

    本文将通过KingbaseES来创建一个视图,带你体验先进的kesonline带来的新式学习方式,本文给大家介绍的非常详细,感兴趣的朋友一起看看吧
    2025-07-07
  • MyBatis中实现动态SQL标签

    MyBatis中实现动态SQL标签

    动态SQL是MyBatis的一项强大功能,它允许开发者根据条件动态地生成SQL语句,本文主要介绍了MyBatis中实现动态SQL标签,感兴趣的可以可以了解一下
    2024-09-09
  • MySQL 5.7.13 源码编译安装配置方法图文教程

    MySQL 5.7.13 源码编译安装配置方法图文教程

    这篇文章主要介绍了MySQL 5.7.13 源码编译安装配置方法图文教程,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-01-01
  • Windows下mysql-5.7.28下载、安装、配置教程图文详解

    Windows下mysql-5.7.28下载、安装、配置教程图文详解

    这篇文章主要介绍了Windows下mysql-5.7.28下载、安装、配置教程,本文图文并茂给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2019-12-12
  • 3种高效的Tags标签系统数据库设计方案分享

    3种高效的Tags标签系统数据库设计方案分享

    这篇文章主要介绍了3种高效的Tags标签系统数据库设计方案分享,现在主流的博客、CMS系统都有一个标签系统,本文就探讨它的数据库设计方式,需要的朋友可以参考下
    2014-07-07
  • SQL重复记录查询 查询多个字段、多表查询、删除重复记录的方法

    SQL重复记录查询 查询多个字段、多表查询、删除重复记录的方法

    下面小编就为大家带来一篇SQL重复记录查询 查询多个字段、多表查询、删除重复记录的方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-09-09
  • MySQL索引与事务定义到使用详解

    MySQL索引与事务定义到使用详解

    这篇文章主要介绍了MySQL数据库索引事务,索引是为了加速对表中数据行的检索而创建的一种分散的存储结;事物是属于计算机中一个很广泛的概念,一般是指要做的或所做的事情,下面我们就一起进入文章了解具体内容吧
    2022-12-12

最新评论