返回

MySQL JSON 列存储优化:解决空间占用过大问题

mysql

MySQL JSON 列存储空间优化

JSON 列存储空间过大问题分析

MySQL 的 JSON 数据类型为存储和检索非结构化数据提供了便利。不过,有时会发现 JSON 列的存储空间占用远大于 JSON 数据本身的尺寸, 比如数据文本量只有1KB,而实际列占用达到了数百KB。这会对性能造成一定影响,并增加存储成本。 此问题背后的原因是什么? 我们又该如何解决?

原因解析

主要原因有两点。一是 MySQL JSON 类型会存储一些额外的元数据用于内部管理和快速查找。二是 JSON 文本更新导致的碎片化问题。当修改 JSON 列时,旧的数据会被标记删除,新的数据存储在不同的位置。如果反复执行此操作,数据存储会出现碎片,占用空间自然也就增大,即使数据大小本身没有明显变化。 这些碎片不会立即被清除。

解决方法

我们从以下三个角度分析解决方案:

方法一:使用 OPTIMIZE TABLE 命令

这个命令能够重建表,对数据进行物理整理,消除碎片,释放空间。 尤其对于长期进行增删改操作的表来说效果很显著。

操作步骤:

  1. 登录 MySQL 客户端:

    mysql -u your_user -p
    
  2. 选择数据库:

    USE your_database;
    
  3. 执行优化命令:

    OPTIMIZE TABLE your_table;
    
  4. 检查存储大小: 可以重新用JSON_STORAGE_SIZE() 查看优化效果。

  5. 安全性: OPTIMIZE TABLE 执行时会锁定表,对于生产环境,需在业务低峰期执行。考虑使用 pt-online-schema-change 工具来进行在线表结构变更。该工具会创建新表、拷贝数据,并无缝切换,以避免影响应用服务。

原理: OPTIMIZE TABLE 实际上执行了 ALTER TABLE your_table ENGINE=INNODB, 它重新整理表空间,从而回收了已删除的空间。这能将存储空间缩减到更合理的大小,也改善查询效率。

方法二:使用压缩 JSON 数据

从 MySQL 5.7 版本开始,MySQL 支持存储压缩后的 JSON 数据。 可以利用内置函数将 JSON 字符串转换为更小的压缩版本,然后存储在 JSON 列。

操作步骤:

  1. 使用 JSON_COMPACT 函数: 压缩 JSON 数据存储

     UPDATE your_table SET json_column = JSON_COMPACT(json_column);
    

这个语句会对当前 json_column 列数据进行一次压缩。在应用程序中更新 JSON 数据时也应当先压缩后再写入。

  1. 查看压缩效果: 使用 JSON_STORAGE_SIZE() 查看更新前后,列的存储空间使用变化。

  2. 查询 JSON 数据: 查询时使用 JSON_UNCOMPACT 函数进行解压。

     SELECT JSON_UNCOMPACT(json_column) FROM your_table where ...;
    
  3. 安全性: 虽然 JSON 压缩能够节约存储空间,但在读取数据时会有额外的解压开销。选择使用哪种策略要根据实际需求进行权衡。

原理: JSON_COMPACT() 函数可以删除 JSON 数据中不必要的空格、制表符等空白字符,从而减少 JSON 文本大小。这种压缩虽然是无损的,却可以节省一定的存储空间。配合 JSON_UNCOMPACT() 则能够在不改变查询方式的同时保证数据压缩。

方法三:重新定义 JSON 列(适用于有修改机会的情况)

当数据出现比较严重的碎片时,通过重建表的办法可能也会存在一定的效率问题。一种更为彻底的方式就是创建新表, 将原有表中的数据按照正确的结构导入到新表中。如果业务可以允许此项操作,推荐使用此方式。

操作步骤:

  1. 创建新表: 创建表结构和原表结构一致的新表,然后使用 insert 语句拷贝原表数据。

      CREATE TABLE your_new_table LIKE your_table;
    
     INSERT INTO your_new_table SELECT * FROM your_table;
    

    LIKE 语句确保表结构一致,INSERT INTO SELECT 拷贝原表所有数据。

  2. 执行数据压缩 (可选): 在数据迁移前,如果决定压缩, 使用 JSON_COMPACT 进行 JSON 数据压缩

         UPDATE your_new_table SET json_column = JSON_COMPACT(json_column);
    
  3. 删除原表,并将新表改名为原表名: 保证切换时没有服务中断。

     RENAME TABLE your_table TO your_table_backup, your_new_table TO your_table;
    
  4. 可选删除: 如果有必要,后续再删除 your_table_backup

  5. 安全性: 此操作风险较高,必须提前备份数据库,同时保证在业务低峰期操作,做好数据校验。也可以使用一些专门的数据迁移工具进行在线迁移,从而保证服务的稳定性。

原理: 此方案彻底解决了因为数据碎片造成空间浪费问题,等价于数据重新入库。 另外,此方法可以在新表上启用 JSON 数据压缩策略。

总结
通过 OPTIMIZE TABLE, 使用 JSON_COMPACT 函数压缩数据或重新创建表并导入数据的方式可以有效地解决 JSON 列占用存储空间过大的问题。选用合适的方案需要根据具体的业务场景, 并考虑数据量大小、性能要求、系统维护窗口期等因素进行选择。 对所有可能影响服务运行的操作都需要进行全面考虑后再执行, 做好安全保护措施, 例如做好备份。