当前位置: 首页 > news >正文

69.使用Go标准库compress/gzip压缩数据存入Redis避免BigKey

文章目录

  • 一:简介
  • 二:Go标准库compress/gzip包介绍
    • Constants
    • Variables
    • type Header
    • type Reader
  • 三:代码实践
    • 1、压缩与解压工具包
    • 2、单元测试
    • 3、为何压缩后还要用base64编码

代码地址: https://gitee.com/lymgoforIT/golang-trick/tree/master/41-go-gzip

一:简介

在工作中,我们有时候需要用到Redis来缓存数据,减轻DB的压力,并提升访问性能。但是当要缓存的key数据过大了会带来BigKey问题:30.Go处理Redis BigKey

在介绍BigKey问题的影响和解决办法时,我们其实说过,最好的办法是预防BigKey的出现,而不是出现后去解决。

但有些场景要缓存的一条信息内容可能就是比较大,且无法拆分,这时候怎么办呢?那就压缩后再存入Redis吧。

二:Go标准库compress/gzip包介绍

compress/gzip是Go标准库中的一个压缩工具包,中文文档地址:https://studygolang.com/static/pkgdoc/pkg/compress_gzip.htm

gzip包实现了gzip格式压缩文件的读写,核心内容如下。

  • Constants:常量定义

  • Variables:变量定义

  • type Header:数据头结构

  • type Reader:解压

    • func NewReader(r io.Reader) (*Reader, error)
    • func (z *Reader) Reset(r io.Reader) error
    • func (z *Reader) Read(p []byte) (n int, err error)
    • func (z *Reader) Close() error
  • type Writer:压缩

    • func NewWriter(w io.Writer) *Writer
    • func NewWriterLevel(w io.Writer, level int) (*Writer, error)
    • func (z *Writer) Reset(w io.Writer)
    • func (z *Writer) Write(p []byte) (int, error)
    • func (z *Writer) Flush() error
    • func (z *Writer) Close() error

Constants

const (NoCompression = flate.NoCompression // 不压缩BestSpeed = flate.BestSpeed // 最快速度BestCompression = flate.BestCompression // 最佳压缩比DefaultCompression = flate.DefaultCompression   // 默认压缩比
)

这些常量都是拷贝自flate包,因此导入"compress/gzip"后,就不必再导入"compress/flate"了。

Variables

var (// 当读取的gzip数据的校验和错误时,会返回ErrChecksumErrChecksum = errors.New("gzip: invalid checksum")// 当读取的gzip数据的头域错误时,会返回ErrHeaderErrHeader = errors.New("gzip: invalid header")
)

type Header

//数据头结构
type Header struct {Comment string    // 文件注释Extra   []byte    // 附加数据ModTime time.Time // 文件修改时间Name    string    // 文件名OS      byte      // 操作系统类型
}

gzip文件保存一个头域,提供关于被压缩的文件的一些元数据。该头域作为WriterReader类型的一个可导出字段,可以提供给调用者访问。

type Reader

type Reader struct {Header// 内含隐藏或非导出字段
}

Reader类型满足io.Reader接口,可以从gzip格式压缩文件读取并解压数据。

一般情况下,一个gzip文件可以是多个gzip文件的串联,每一个都有自己的头域。从Reader读取数据会返回串联的每个文件的解压数据,但只有第一个文件的头域被记录在ReaderHeader字段里。

gzip文件会保存未压缩前数据的长度与校验和。当读取到未压缩数据的结尾时,如果数据的长度或者校验和不正确,Reader会返回ErrCheckSum,如果没有读取完毕后,长度和校验和与压缩前数据的一致,说明解压成功。因此,调用者应该将Read方法返回的数据视为暂定的,直到他们在数据结尾获得了一个io.EOF

func NewReader

func NewReader(r io.Reader) (*Reader, error)

NewReader返回一个从r读取并解压数据的Reader。其实现会缓冲输入流的数据,并可能从r中读取比需要的更多的数据(如长度和校验和)。调用者有责任在读取完毕后调用返回值的Close方法。

注意入参是io.Reader,返回值是*gzip.Reader,但*gzip.Reader(Struct)也是实现了io.Reader接口的

func (*Reader) Reset

func (z *Reader) Reset(r io.Reader) error

Resetz重置,丢弃当前的读取状态,并将下层读取目标设为r。效果上等价于将z设为使用r重新调用NewReader返回的Reader。这让我们可以重用z而不是再申请一个新的。(因此效率更高)

func (*Reader) Read

func (z *Reader) Read(p []byte) (n int, err error)

func (*Reader) Close

func (z *Reader) Close() error

调用Close会关闭z,但不会关闭下层io.Reader接口。

type Writer

type Writer struct {Header// 内含隐藏或非导出字段
}

Writer满足io.WriteCloser接口。它会将提供给它的数据压缩后写入下层io.Writer接口。

func NewWriter

func NewWriter(w io.Writer) *Writer

NewWriter创建并返回一个Writer。写入返回值gzip.Writer的数据都会在压缩后写入入参指定的w中。调用者有责任在结束写入后调用返回值gzip.Writer的Close方法。因为写入的数据可能保存在缓冲中没有刷新入下层。

如要设定Writer.Header字段,调用者必须在第一次调用Write方法或者Close方法之前设置。Header字段的CommentName字段是goutf-8字符串,但下层格式要求为NUL中止的ISO 8859-1 (Latin-1)序列。如果这两个字段的字符串包含NUL或非Latin-1字符,将导致Write方法返回错误。

func NewWriterLevel

func NewWriterLevel(w io.Writer, level int) (*Writer, error)

NewWriterLevel类似NewWriter但指定了压缩水平而不是采用默认的DefaultCompression

参数level可以是DefaultCompression、NoCompressionBestSpeedBestCompression之间的任何整数。如果level合法,返回的错误值为nil

func (*Writer) Reset

func (z *Writer) Reset(dst io.Writer)

Resetz重置,丢弃当前的写入状态,并将下层输出目标设为dst。效果上等价于将w设为使用dstw的压缩水平重新调用NewWriterLevel返回的*Writer。这让我们可以重用z而不是再申请一个新的。(因此效率更高)

func (*Writer) Write

func (z *Writer) Write(p []byte) (int, error)

Writep压缩后写入下层io.Writer接口。压缩后的数据不一定会立刻刷新,除非Writer被关闭或者显式的刷新,即调用Close方法或者Flush方法。

func (*Writer) Flush

func (z *Writer) Flush() error

Flush将缓冲中的压缩数据刷新到下层io.Writer接口中。

本方法主要用在传输压缩数据的网络连接中,以保证远端的接收者可以获得足够的数据来重构数据报。Flush会阻塞直到所有缓冲中的数据都写入下层io.Writer接口后才返回。如果下层的io.Writetr接口返回一个错误,Flush也会返回该错误。在zlib包的术语中,Flush方法等价于Z_SYNC_FLUSH

func (*Writer) Close

func (z *Writer) Close() error

调用Close会关闭z,但不会关闭下层io.Writer接口。

三:代码实践

1、压缩与解压工具包

package utilimport ("bytes""compress/gzip""context""encoding/base64""fmt"
)// GzipEncode 采用gzip算法压缩字符串,输出base64编码的字符串
func GzipEncode(ctx context.Context, input string) (string, error) {if len(input) == 0 {return input, nil}var b bytes.Buffer // 实现了io.Writergz := gzip.NewWriter(&b)defer func() {if err := gz.Close(); err != nil {fmt.Println(fmt.Sprintf("gz.Close() err:%v", err))}}()if _, err := gz.Write([]byte(input)); err != nil {fmt.Println(fmt.Sprintf("[GzipEncode] gz write err:%v", err))return "", err}// 将gzip.Writer缓冲中的数据刷到底层io.Writer中if err := gz.Flush(); err != nil {fmt.Println(fmt.Sprintf("[GzipEncode] gz flush err:%v", err))return "", err}// 在读取数据之前必须close,否则读取的数据会有问题,在这里作用同Flush一样// 即将压缩后的数据立即写入底层io.Writer中,在这里是b(bytes.Buffer)if err := gz.Close(); err != nil {fmt.Println(fmt.Sprintf("[GzipEncode] gz close err:%v", err))return "", err}newStr := base64.StdEncoding.EncodeToString(b.Bytes())return newStr, nil
}// GzipDecode 采用gzip算法解压字符串
func GzipDecode(ctx context.Context, input string) (string, error) {newInput, err := base64.StdEncoding.DecodeString(input)if err != nil {fmt.Println(fmt.Sprintf("[GzipDecode] base decode err:%v", err))return "", err}bReader := bytes.NewReader(newInput)gReader, err := gzip.NewReader(bReader)if err != nil {fmt.Println(fmt.Sprintf("[GzipDecode] new reader err,%v", err))return "", err}if err = gReader.Close(); err != nil {fmt.Println(fmt.Sprintf("[GzipDecode] reader close err:%v", err))return "", err}buf := new(bytes.Buffer)if _, err = buf.ReadFrom(gReader); err != nil {fmt.Println(fmt.Sprintf("[GzipDecode] read from greader err:%v", err))return "", err}return buf.String(), err
}

2、单元测试

package utilimport ("context""fmt""testing"
)func TestGzipEncode(t *testing.T) {encode, err := GzipEncode(context.Background(), "hello world")if err != nil {fmt.Println(fmt.Sprintf("GzipEncode err:%v", err))return}fmt.Println(fmt.Sprintf("res:%v", encode))
}func TestGzipDecode(t *testing.T) {decode, err := GzipDecode(context.Background(), "H4sIAAAAAAAA/8pIzcnJVyjPL8pJAQAAAP//AQAA//+FEUoNCwAAAA==")if err != nil {fmt.Println(fmt.Sprintf("GzipEncode err:%v", err))return}fmt.Println(fmt.Sprintf("res:%v", decode))
}

当执行TestGzipEncode后,我们可以看到输出为
在这里插入图片描述
当执行TestGzipDecode后,可以看到输出为
在这里插入图片描述
测试结果符合预期。

此时可能会有一个疑问,一个hello world压缩并用base64编码后,字符串长度更长了,这不是与压缩的初衷背道而驰了嘛?是的,这里只是为了演示,所以用了个hello world,实际场景下,既然选择了压缩,那肯定是已知要压缩的内容是比较大的。

比如我们稍微将字符串变长一些,看看效果就知道压缩对于减少内存占用确实是有用的
在这里插入图片描述

3、为何压缩后还要用base64编码

我们可以将util中的源代码去掉base64编码后,单元测试一下看看原始gzip压缩后,字符串会是什么形式呢?
在这里插入图片描述

可以看到压缩后字符串确实很小了,但是美观,且复制后对其解压会报错GzipEncode err:unexpected EOF,所以还是把base64编码用上吧!!

相关文章:

69.使用Go标准库compress/gzip压缩数据存入Redis避免BigKey

文章目录 一:简介二:Go标准库compress/gzip包介绍ConstantsVariablestype Headertype Reader 三:代码实践1、压缩与解压工具包2、单元测试3、为何压缩后还要用base64编码 代码地址: https://gitee.com/lymgoforIT/golang-trick/t…...

JavaScript实现的一些小案例

小案例 灯开关案例 <body><img id"light" src"img/off.jpg"><script>var light document.getElementById("light");var flag false;if(flag){light.src "img/on.jpg";flag false;}else{light.src "img/…...

MVC模式

Model-View-Controller : 模型-视图-控制器模式&#xff0c;用于应用程序的分层开发。 Model(模型)&#xff1a;代表一个存取数据的对象。也可以带有逻辑&#xff0c;在数据变化时更新控制器。 View(视图)&#xff1a;代表模型包含的数据的可视化。 Controller(控制器)&#xf…...

Java中的代理模式(一)

大家好&#x1f44b;&#xff0c;我是极客涛&#x1f60e;&#xff0c;今天我们聊一聊java中的代理模式&#xff0c;话不多说&#xff0c;还是老思路&#xff0c;什么是代理模式&#xff0c;为什么要有代理模式&#xff0c;如何实现代理模式 代理模式 在说java中的代理模式之前…...

跳跃游戏-算法

题目 给定一个数组nums {1,2,3,4,5}&#xff0c;每个元素nums[i]表示从i这个位置最多可以向前跳跃nums[i]个台阶&#xff0c;求最小需要跳几次就可以调到末尾 思路 反向查找 从末尾开始逐个向前判断最远的起跳位置&#xff0c;接着再以该位置递归的判断 public int jumpT…...

ERP系统哪个好用?用友,金蝶,ORACLE,SAP综合测评

ERP系统哪个好用&#xff1f;用友&#xff0c;金蝶&#xff0c;ORACLE&#xff0c;SAP综合测评 ERP领域SAP、ORACLE相对于国内厂商如用友、金蝶优势在哪&#xff1f; SAP&#xff0c;ORACLE操作习惯一般国人用不惯&#xff1b;相对于国产软件&#xff0c;界面也很难看&#x…...

外汇天眼:美国证券交易委员会(SEC)采纳了一系列规定,以加强与特殊目的收购公司(SPACs)相关的投资者保护

美国证券交易委员会&#xff08;SEC&#xff09;今天通过了一系列新规和修订&#xff0c;以增强特殊目的收购公司&#xff08;SPACs&#xff09;的首次公开募股&#xff08;IPOs&#xff09;中的披露&#xff0c;并在SPACs与目标公司之间的后续业务合并交易&#xff08;de-SPAC…...

kotlin map 与 flatmap

kotlin map 与 flatmap 是2个不同的概念的 map 是一种数据结构&#xff0c;flatmap 是一个高阶函数&#xff0c;处理集合用的 Map Map 是一种数据结构&#xff0c;它由一系列的键值对组成&#xff0c;每个键都是唯一的&#xff0c;并且与一个特定的值相关联。你可以通过键来…...

nginx-rtmp-module 支持 Enhancing RTMP HEVC(H.265)

Enhancing RTMP, FLV 2023年7月31号正式发布&#xff0c;主要支持了HEVC(H.265)、VP9、AV1视频编码&#xff0c;发布差不多半年了&#xff0c;很多开源项目已支持&#xff0c;最近打算播放和推送端也支持下&#xff0c;想找个支持的rtmp server方便测试用&#xff0c;但没找到合…...

2024最新JDK1.8+JDK17+JDK21安装包下载+文档

2024年更新&#xff0c;JDK8的64位和32位安装包都有&#xff0c;Java8最新文档也有&#xff0c;JDK17和JDK21的最新安装包也有 因为网上的安装包都不是最新的&#xff0c;所以自己去Oracle官网登录下载保存了一份&#xff0c;需要的朋友下面网盘链接下载 JDK8—64位安装程序&…...

如何利用chatgpt提升工作效率

chatgpt全领域小助手 项目管理&#xff1a;制定项目计划、跟踪进度、分配任务和记录里程碑。客户服务&#xff1a;回答常见问题、提供产品支持和处理客户投诉&#xff0c;提升客户满意度。销售支持&#xff1a;提供销售培训、销售脚本和客户资料&#xff0c;辅助销售团队进行销…...

WinSCP下载安装并实现远程SSH本地服务器上传文件

文章目录 1. 简介2. 软件下载安装&#xff1a;3. SSH链接服务器4. WinSCP使用公网TCP地址链接本地服务器5. WinSCP使用固定公网TCP地址访问服务器 1. 简介 ​ Winscp是一个支持SSH(Secure SHell)的可视化SCP(Secure Copy)文件传输软件&#xff0c;它的主要功能是在本地与远程计…...

QEMU搭建arm虚拟机开发环境

获取QEMU代码 git clone https://gitlab.com/qemu-project/qemu.git 切换对应的工程分支 使用git指令切换到对应的分支上&#xff0c;我这里使用的是stable-4.0的分支 git checkout -b stable-4.0 remotes/origin/stable-4.0 配置&编译 在工程的根目录下执行 ./conf…...

web 应用常见的安全问题

一xss攻击 人们经常将跨站脚本攻击&#xff08;Cross Site Scripting&#xff09;缩写为CSS&#xff0c;但这会与层叠样式表&#xff08;Cascading Style Sheets&#xff0c;CSS&#xff09;的缩写混淆。因此&#xff0c;有人将跨站脚本攻击缩写为XSS。 跨站脚本攻击&#xff…...

502. IPO

502. IPO 题目链接&#xff1a;502. IPO 代码如下&#xff1a; //堆的使用 class Solution { public:int findMaximizedCapital(int k, int w, vector<int>& profits, vector<int>& capital) {vector<pair<int,int>> mp;//优先队列默认的是大…...

如何安装MeterSphere并实现无公网ip远程访问服务管理界面

文章目录 前言1. 安装MeterSphere2. 本地访问MeterSphere3. 安装 cpolar内网穿透软件4. 配置MeterSphere公网访问地址5. 公网远程访问MeterSphere6. 固定MeterSphere公网地址 正文开始前给大家推荐个网站&#xff0c;前些天发现了一个巨牛的 人工智能学习网站&#xff0c; 通…...

做FP独立站怎么引流?这个引流法宝收好了!

近年来&#xff0c;由于卖家数量飙升导致平台竞争持续升级&#xff0c;卖家之间的恶性循环竞争以及平台政策的不断调整等&#xff0c;造成了众多亚马逊等跨境卖家纷纷从平台转向独立站。可是&#xff0c;转型做独立站前要先考虑清楚独立站与平台二者之间的区别。 如果在第三方平…...

幻兽帕鲁PalWorld服务器搭建教程,1分钟开服,纯小白教程,无需基础

雨云面板服快速开幻兽帕鲁PalWorld服务器的教程&#xff0c;配置文件修改方法和配置项中文注释。 最近这游戏挺火&#xff0c;很多人想跟朋友联机&#xff0c;如果有专用服务器&#xff0c;就不需要房主一直开着电脑&#xff0c;稳定性也好得多。 幻兽帕鲁简介 《幻兽帕鲁》…...

算法小抄01

1. 计数排序是一种基于 统计 的排序算法 2. 基于比较的排序算法有&#xff1a;&#xff08;1&#xff09;直接插入排序&#xff1b;&#xff08;2&#xff09;冒泡排序&#xff1b;&#xff08;3&#xff09;简单选择排序&#xff1b;&#xff08;4&#xff09;希尔排序&#…...

Spring Boot 集成 API 文档 - Swagger、Knife4J、Smart-Doc

文章目录 1.OpenAPI 规范2.Swagger: 接口管理的利器3.Swagger 与 SpringFox&#xff1a;理念与实现4.Swagger 与 Knife4J&#xff1a;增强与创新5.案例&#xff1a;Spring Boot 整合 Swagger35.1 引入 Swagger3 依赖包5.2 优化路径匹配策略兼容 SpringFox5.3 配置 Swagger5.4 S…...

测试微信模版消息推送

进入“开发接口管理”--“公众平台测试账号”&#xff0c;无需申请公众账号、可在测试账号中体验并测试微信公众平台所有高级接口。 获取access_token: 自定义模版消息&#xff1a; 关注测试号&#xff1a;扫二维码关注测试号。 发送模版消息&#xff1a; import requests da…...

【Java学习笔记】Arrays类

Arrays 类 1. 导入包&#xff1a;import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序&#xff08;自然排序和定制排序&#xff09;Arrays.binarySearch()通过二分搜索法进行查找&#xff08;前提&#xff1a;数组是…...

FastAPI 教程:从入门到实践

FastAPI 是一个现代、快速&#xff08;高性能&#xff09;的 Web 框架&#xff0c;用于构建 API&#xff0c;支持 Python 3.6。它基于标准 Python 类型提示&#xff0c;易于学习且功能强大。以下是一个完整的 FastAPI 入门教程&#xff0c;涵盖从环境搭建到创建并运行一个简单的…...

CocosCreator 之 JavaScript/TypeScript和Java的相互交互

引擎版本&#xff1a; 3.8.1 语言&#xff1a; JavaScript/TypeScript、C、Java 环境&#xff1a;Window 参考&#xff1a;Java原生反射机制 您好&#xff0c;我是鹤九日&#xff01; 回顾 在上篇文章中&#xff1a;CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...

css的定位(position)详解:相对定位 绝对定位 固定定位

在 CSS 中&#xff0c;元素的定位通过 position 属性控制&#xff0c;共有 5 种定位模式&#xff1a;static&#xff08;静态定位&#xff09;、relative&#xff08;相对定位&#xff09;、absolute&#xff08;绝对定位&#xff09;、fixed&#xff08;固定定位&#xff09;和…...

工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配

AI3D视觉的工业赋能者 迁移科技成立于2017年&#xff0c;作为行业领先的3D工业相机及视觉系统供应商&#xff0c;累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成&#xff0c;通过稳定、易用、高回报的AI3D视觉系统&#xff0c;为汽车、新能源、金属制造等行…...

AI编程--插件对比分析:CodeRider、GitHub Copilot及其他

AI编程插件对比分析&#xff1a;CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展&#xff0c;AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者&#xff0c;分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...

QT: `long long` 类型转换为 `QString` 2025.6.5

在 Qt 中&#xff0c;将 long long 类型转换为 QString 可以通过以下两种常用方法实现&#xff1a; 方法 1&#xff1a;使用 QString::number() 直接调用 QString 的静态方法 number()&#xff0c;将数值转换为字符串&#xff1a; long long value 1234567890123456789LL; …...

JVM暂停(Stop-The-World,STW)的原因分类及对应排查方案

JVM暂停(Stop-The-World,STW)的完整原因分类及对应排查方案,结合JVM运行机制和常见故障场景整理而成: 一、GC相关暂停​​ 1. ​​安全点(Safepoint)阻塞​​ ​​现象​​:JVM暂停但无GC日志,日志显示No GCs detected。​​原因​​:JVM等待所有线程进入安全点(如…...

DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”

目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...