使用 Gzip 拯救你的 varchar¶
在处理大量数据时,数据压缩是优化存储和传输效率的重要手段。在 Go 语言中,我们可以通过自定义 JSON 的 Marshal 方法,实现在数据入库前自动进行 gzip 压缩,从而减少存储空间占用并提高传输效率。
在某些业务场景下,我们可能会在数据库中保存一些 JSON 格式的元数据,例如使用 varchar(2048)
存储一些复杂的原始输入数据或存储一些商品快照信息等,这类数据通常都会随着业务的发展不断膨胀。最终很有可能会超过最初设计的字符上限,导致在创建或更新数据记录时报错。
Bash | |
---|---|
常规操作:简单粗暴扩大字段¶
通常,我们能想到的解决办法就是修改数据表的字段,增加字段大小,比如将字段大小从 varchar(2048)
改为 varchar(4096)
。或者更激进的使用 TEXT
或 BLOB
大字段。这种方式操作起来最简单,但是会显著降低数据库的查询效率。
本文介绍另外一种思路,可以在数据入库前对数据进行压缩。
另辟蹊径:入库前自动压缩¶
考虑到我们使用这类 JSON 数据的场景多为入库前执行 JSON 序列化,查询后使用 JSON 反序列转为 Go 中结构体变量进行使用。我们可以利用 Go 语言的 json 包提供了自定义 MarshalJSON
和 UnmarshalJSON
方法的机制,在数据被序列化为 JSON 格式之前进行 gzip 压缩,在反序列化时进行解压缩。
具体实现步骤:
- 创建一个自定义类型,用于存储需要压缩的数据
- 为该类型实现 json.Marshaler 接口的 MarshalJSON 方法
- 在 MarshalJSON 方法中对数据进行 gzip 压缩
- 为该类型实现 json.Unmarshaler 接口的 UnmarshalJSON 方法
- 在 UnmarshalJSON 方法中对数据进行 gzip 解压缩
示例代码¶
下面是一个完整的示例代码,展示了如何实现这一功能:
优势何在?¶
- 节省存储空间:JSON 数据(尤其是包含大量重复文本或结构时)通常有不错的压缩率。这意味着你可以用更小的数据库字段(或者在现有字段里存更多数据)来存储相同的信息。
- 减少传输压力:如果这些数据需要在网络间传输(例如,从数据库到应用服务器),压缩也能减少网络I/O。
- “无感”集成:对业务代码的侵入性小。你只需要定义好你的
CompressedData[T]
类型,并在需要的地方使用它即可。序列化和反序列化时,Go 的encoding/json
会自动调用你的自定义方法。 - 可能避免或推迟数据库变更:在某些情况下,这可以让你在不立即修改数据库表结构(如将
varchar
升级到TEXT
)的情况下解决数据超长问题。
需要权衡的因素¶
当然,这种方法并非银弹,也有一些需要考虑的成本:
- CPU 开销:压缩和解压缩操作会消耗 CPU 资源。对于读写非常频繁且对延迟要求极高的场景,需要评估这个开销。通常 gzip 的速度是很快的,但还是需要测试。
- 压缩率的不确定性:压缩效果取决于数据的具体内容。对于已经高度压缩或随机性很强的数据,压缩效果可能不佳,甚至可能轻微增大体积(加上 Base64 编码的33%的体积膨胀)。但对于典型的 JSON 文本,效果通常不错。
- 数据库内可读性:数据在数据库中是以压缩和 Base64 编码的形式存储的,你不能直接在数据库管理工具里查看其原始内容,调试时可能需要先手动解压。
- 无法对压缩内容进行数据库级别的 JSON 查询:如果你的数据库支持对 JSON 字段内部进行查询(例如 PostgreSQL 的
->>
,->>
操作符,MySQL 的 JSON 函数),那么压缩后的数据将无法使用这些功能。你必须先将数据完整取出并解压后才能在应用层进行查询。 - 错误处理:在
MarshalJSON
和UnmarshalJSON
方法中,需要妥善处理可能发生的错误(如压缩/解压失败、编码/解码失败)。
一些有用的知识¶
Q:gzip 压缩后为什么要 Base64 编码?
A:JSON(JavaScript Object Notation)是一种纯文本格式,只能存储字符串、数字、布尔值等文本类型数据,而二进制数据(如 Gzip 压缩后的字节流)无法直接存入 JSON 字段。
Q:有没有更简单的方案?
A:直接使用数据库的 JSON 字段或使用 mongoDB 。
总结¶
通过自定义 Go 的 json.Marshaler
和 json.Unmarshaler
接口,我们可以巧妙地实现 JSON 数据的自动压缩与解压缩,从而在一定程度上缓解数据库字段长度限制带来的问题。这种方法不仅能够节省存储空间,还能保持应用层代码的简洁性。
当你遇到 Error 1406: Data too long
并且不想轻易扩大数据库字段时,不妨试试这个“无感压缩”的技巧。它就像给你的数据穿上了一件“压缩衣”,入库时自动收紧,出库时自动还原,是不是很酷?