70.Redis缓存优化实践(基于分类树场景)

news2024/11/18 6:41:49

文章目录

  • 前言
  • 第一次优化
  • 第二次优化
  • 第三次优化
  • 第四次优化
  • 第五次优化

前言

分类树查询功能,在各个业务系统中可以说随处可见,特别是在电商系统中。
在这里插入图片描述

而在实际工作中,这样一个分类树查询,我们都不断的改进了好几次。这是为什么呢?

由于当时这个是从0-1的新项目,为了开快速开发功能,我们第一版接口,直接从数据库中查询分类数据,组装成分类树,然后返回给前端。 通过这种方式,简化了数据流程,快速把整个页面功能调通了。

第一次优化

我们将该接口部署到开发dev环境,刚开始没啥问题。随着开发人员添加的分类越来越多,很快就暴露出性能瓶颈。我们不得不做优化了。

我们第一个想到的是:加Redis缓存。

流程图如下:
在这里插入图片描述

于是暂时这样优化了一下:

  1. 用户访问接口获取分类树时,先从Redis中查询数据。
  2. 如果Redis中有数据,则直接返回数据。
  3. 如果Redis中没有数据,则再从数据库中查询数据,拼接成分类树返回。
  4. 将从数据库中查到的分类树的数据,保存到Redis中,设置过期时间5分钟。
  5. 将分类树返回给用户。

我们在Redis中定义一个了keyvalue是一个分类树的json格式转换成了字符串,使用简单的key/value形式保存数据。

经过这样优化之后,测试环境的联调和自测顺利完成了。

第二次优化

我们将这个功能部署到预览(pre)环境了。刚开始测试同学没有发现什么问题,但随着后面不断地深入测试,隔一段时间就出现一次首页访问很慢的情况。原因就是开始加载到Redis中的数据过期了,需要从DB获取。

于是,我们马上进行了第2次优化。

我们决定使用Job定期异步更新分类树到Redis中,在系统上线之前,会先生成一份数据,比如在Go语言中,新开一个旁路服务,通过脚本的形式,当服务器实例启动的时候,在Main方法中通过cache.Init()开启一个协程,定期加载数据进Redis缓存,但是需要加分布式锁,不然同一个服务的所有服务器实例都去做这件事有点浪费性能,我们只需要有一个服务实例做这件事就行了,当然,如果是使用的一些定时调度平台的话,一般是调度平台自身就提供了只会让一台服务实例去执行任务的能力的。

当然为了保险起见,防止定时调度平台在哪天突然挂了,之前分类树同步写入Redis的逻辑还是保留。

于是,流程图改成了这样:

在这里插入图片描述

增加了一个旁路定时job每隔5分钟执行一次,从数据库中查询分类数据,封装成分类树,更新到Redis缓存中。其他的流程保持不变。
此外,Redis的过期时间之前设置的5分钟,现在可以设置10分钟,正常情况下,调度平台5分钟内都会更新Redis,但是如果调度平台挂了,导致没有更新,10分钟后,redis的会过期,此时还是可以从DB中拿到最新的数据的,不至于Redis无法得到更新而一直存着旧数据。

通过这次优化之后,预览环境就没有再出现过分类树查询的性能问题了。

第三次优化

测试了一段时间之后,整个网站的功能快要上线了。为了保险起见,我们需要对网站首页做一次压力测试。果然测出问题了,网站首页最大的qps100多,最后发现是每次都从Redis获取分类树导致的网站首页的性能瓶颈。

我们需要做第3次优化。

该怎么优化呢?

答:加内存缓存如果加了内存缓存,就需要考虑数据一致性问题。 内存缓存是保存在服务器节点上的,不同的服务器节点更新的频率可能有点差异,这样可能会导致数据的不一致性。但分类本身是更新频率比较低的数据,对于用户来说不太敏感,即使在短时间内,用户看到的分类树有些差异,也不会对用户造成太大的影响。因此,分类树这种业务场景,是可以使用内存缓存的。

Go中我们可以使用LocalCache或者Map做本地缓存。

改造后的流程图如下:

在这里插入图片描述

  1. 用户访问接口时改成先从本地缓存分类数查询数据。
  2. 如果本地缓存有,则直接返回。
  3. 如果本地缓存没有,则从Redis中查询数据。
  4. 如果Redis中有数据,则将数据更新到本地缓存中,然后返回数据。
  5. 如果Redis中也没有数据(说明Redis挂了或者调度平台挂了),则从数据库中查询数据,更新到Redis中(万一Redis恢复了呢),然后更新到本地缓存中,返回数据。

需要注意的是,需要给本地缓存设置一个过期时间,这里设置的5分钟,不然的话,没办法获取新的数据,比如如果用的map做本地缓存,可以开启一个协程,每隔五分钟重置一下map

这样优化之后,再次做网站首页的压力测试,qps提升到了500多,满足上线要求。

第四次优化

之后,这个功能顺利上线了。使用了很长一段时间没有出现问题。两年后的某一天,有用户反馈说,网站首页有点慢。我们排查了一下原因发现,分类树的数据太多了,一次性返回了上万个分类。原来在系统上线的这两年多的时间内,运营同学在系统后台增加了很多分类。

我们需要做第4次优化。

这时要如何优化呢?限制分类树的数量?

答:也不太现实,目前这个业务场景就是有这么多分类,不能让用户选择不到他想要的分类吧?

这时我们想到最快的办法是开启nginxGZip功能。让数据在传输之前,先压缩一下,然后进行传输,在用户浏览器中,自动解压,将真实的分类树数据展示给用户。之前调用接口返回的分类树有1MB的大小,优化之后,接口返回的分类树的大小是100Kb,一下子缩小了10倍。

这样简单的优化之后,性能提升了一些。

GoGzip压缩后然后存入Redis可以看本人另一篇博客:69.使用Go标准库compress/gzip压缩数据存入Redis避免BigKey

第五次优化

经过上面优化之后,用户很长一段时间都没有反馈性能问题。但有一天公司同事在排查Redis中大key的时候,揪出了分类树。之前的分类树使用key/value的结构保存数据的。

我们不得不做第5次优化。

为了优化在Redis中存储数据的大小,我们可以对数据进行瘦身后再压缩存入Redis

1. 只保存需要用到的字段。

例如:

type Category struct {
	Id         int64      `json:"id"`
	Name       string     `json:"name"`
	ParentId   int64      `json:"parent_id"`    // 父节点
	InDate     time.Time  `json:"in_date"`      // 分类添加的日期
	InUserId   int64      `json:"in_user_id"`   // 添加该分类的运营id
	InUserName string     `json:"in_user_name"` // 添加该分类的运营名字
	Children   []Category `json:"children"`     // 子节点
}

像这个分类对象中inDateinUserIdinUserName字段是可以不用保存的。

2. 修改字段名称。

例如:

type Category struct {
	Id         int64      `json:"i"`
	Name       string     `json:"n"`
	ParentId   int64      `json:"p"`    // 父节点
	Children   []Category `json:"c"`     // 子节点
}

由于在一万多条数据中,每条数据的字段名称是固定的,他们的重复率太高了。由此,可以在json序列化时,改成一个简短的名称,以便于返回更少的数据大小。

这还不够,我们还可以对存储的数据做压缩。

先将json字符串数据用GZip工具类压缩成byte数组,然后转为字符串后进行base64编码,最后保存到Redis中。在获取数据时,将解码、解压缩json字符串,然后再f反序列化成分类树。

这样优化之后,保存到Redis中的分类树的数据大小,一下子减少了10倍,Redis的大key问题被解决了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1399181.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Lite AD的安装

1、Lite AD的安装及配置 Lite AD流程: (1)创建一个新的Windows 10,安装tools,再安装ITA组件(安装Lite AD会自动安装VAG/VLB) (2)创建一个新的Windows 10,安…

【GNN报告】“青源Talk”-图可信学习与图大模型研究进展

北航王啸-图自监督学习 简介 介绍 浙大杨洋-探索大图模型预训练 总括 介绍 参考 Yang Yang - Zhejiang University dgraph-web DGraph: ALarge-Scale Financial Dataset for Graph Anomaly Detection All in One: Multi-task Prompting for Graph Neural Networks&#xf…

Spring Boot实现统一异常处理的技术解析

引言 在软件开发过程中,异常处理是非常重要的一环。一个好的异常处理机制可以帮助我们更好地定位问题,提高代码的可维护性和稳定性。Spring Boot作为一款轻量级的Java开发框架,提供了一种简单而高效的方式来实现统一异常处理。本文将详细介绍…

神器yakit之web fuzzer功能

前言 yakit并不像burp一样单独设置爆破模块,但是yakit也是可以爆破的,并且更好用(个人感觉)。 手工测试场景中需要渗透人员对报文进行反复的发送畸形或者特定的payload进行查看服务器的反馈并以此来进行下一步的判断。 Fuzz标签便…

动态规划:完全背包问题

本题力扣上没有,是刷的卡码网第52题52. 携带研究材料感兴趣的小伙伴可以去刷一下,是ACM模式。 题目: 题目描述: 小明是一位科学家,他需要参加一场重要的国际科学大会,以展示自己的最新研究成果。他需要带…

x-cmd pkg | skate - 个人键值对存储工具

目录 简介用户首次快速实验指南功能特点竞品和相关作品进一步探索 简介 skate 是个人键值对存储工具,具备数据加密、云端数据备份以及多设备同步等功能。 它由 Charm 团队开发,借用 Charm Cloud 向用户提供一种快捷的方式来保存和检索各种数据&#xf…

接口测试 02 -- JMeter入门到实战

前言 JM eter毕竟是做压测的工具,自动化这块还是有缺陷。 如果公司做一些简单的接口自动化,可以考虑使用JMeter快速完成,如果想做完善的接口自动化体系,建议还是基于Python来做。 为什么学习接口测试要先从JMeter开始?…

VS Code + Python + Selenium 自动化测试基础-01

VS Code Python Selenium 自动化测试基础-01 让我们来讲一个故事为什么要写自动化开发前的准备工作牛刀小试开常用的web DriverAPI-定位元素id定位:find_element_by_id()name 定位:find_element_by_name()class 定位:find_element_by_class…

Oracle SQL Developer执行sql脚本文件

文件过于大,无法打开,直接在界面执行。 ①将文件放置到D盘,文件名 daochu5.sql ② 在工具执行SQL界面输入 d:\daochu5.sql;,点击运行按钮运行

范围运算between...and和空判断

目录 between...and 空判断 Oracle从入门到总裁:https://blog.csdn.net/weixin_67859959/article/details/135209645 between...and between...and的主要功能是用户进行范围查询,语法如下: select 字段 | 数值 between 最小值 and 最大值; 1.查询工资在 1500 ~ 3000 的所…

基于JavaWeb+SSM+Vue基于微信小程序生鲜云订单零售系统的设计和实现

基于JavaWebSSMVue基于微信小程序生鲜云订单零售系统的设计和实现 滑到文末获取源码Lun文目录前言主要技术系统设计功能截图订阅经典源码专栏Java项目精品实战案例《500套》 源码获取 滑到文末获取源码 Lun文目录 目录 1系统概述 1 1.1 研究背景 1 1.2研究目的 1 1.3系统设计…

逻辑运算

目录 AND OR NOT Oracle从入门到总裁:https://blog.csdn.net/weixin_67859959/article/details/135209645 逻辑运算可以保证连接多个条件,连接主要使用 AND、OR 、NOT完成 AND 1.查询职位不是办事员,但是工资低于 300 的员工信息 这个范例可以理…

【GitHub项目推荐--全球首个开源图像识别系统】【转载】

你知道人脸识别、商品识别、车辆识别,以图搜图乃至自动驾驶,背后的技术是什么嘛?并不是图像分类、目标检测这些东西,而是综合使用目标检测、图像分类、度量学习、图像检索的【通用图像识别系统】… 度量学习是啥?图像检…

Postman接口测试高阶——精通Mock Server模拟服务器的创建及使用等

文章目录 一、什么是Mock Server二、为什么使用Mock Server四、Mock Server使用场景五、创建Mock Server模拟服务器1.创建Mock Server2.配置Mock Server3.创建Mock Server模拟服务器成功 六、使用Mock Server模拟服务器七、修改Mock Server模拟服务器配置 一、什么是Mock Serve…

AI跟踪报道第25期-新加坡内哥谈技术-本周AI发展更新-酷炫来袭

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗?订阅我们的简报,深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同,从行业内部的深度分析和实用指南中受益。不要错过这个机会,成为AI领…

Java毕业设计-基于springboot的学习英语管理系统-第89期

获取源码资料,请移步从戎源码网:从戎源码网_专业的计算机毕业设计网站 项目介绍 基于springbootvue的医院管理系统:前端 vue、bootstrap、coreui,后端 maven、springmvc、spring、mybatis、redis,角色分为管理员、医…

后台管理系统: sku管理模块

完成添加SKU静态组件 完成添加SKU静态组件 点击添加sku&#xff0c;触发回调&#xff0c;场景为2 静态界面完成 <template><div><el-form ref"form" label-width"80px"><el-form-item label"SPU名称"> 海绵宝宝 <…

用红黑树封装实现map与set

红黑树 红黑树 &#xff0c;是一种 二叉搜索树 &#xff0c;但 在每个结点上增加一个存储位表示结点的颜色&#xff0c;可以是 Red 或 Black 。 通过对 任何一条从根到叶子的路径上各个结点着色方式的限制&#xff0c;红黑树确保没有一条路 径会比其他路径长出俩倍 &#xff…

新品发布 | 多通道总线记录仪TLog1004,是你期待的吗?

新品发布 2024年1月12日&#xff0c;同星智能又发布一款多通道 CAN &#xff08;FD&#xff09;总线、LIN 总线接口logger设备&#xff0c;此款产品在TLog1002基础上进行了升级&#xff0c;同时内置 3 路数字输入和 2 路数字输出&#xff0c;便于多种信号测量和系统集成。可以满…

12- OpenCV:算子(Sobel和Laplance) 和Canny边缘检测 详解

目录 一、Sobel算子 1、卷积应用-图像边缘提取 2、Sobel算子&#xff08;索贝尔算子&#xff09; 3、相关的API&#xff08;代码例子&#xff09; 二、Laplance算子 1、理论 2、API使用&#xff08;代码例子&#xff09; 三、Canny边缘检测 1、Canny算法介绍 2、API使…