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

文章目录

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

前言

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

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

由于当时这个是从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问题被解决了。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

成功解决「MySQL问题1」启动mysql时:发生系统错误5拒绝访问

我 | 在这里 🕵️ 读书 | 长沙 ⭐软件工程 ⭐ 本科 🏠 工作 | 广州 ⭐ Java 全栈开发(软件工程师) ✈️公众号 | 热爱技术的小郑 。文章底部有个人公众号二维码。回复 Java全套视频教程 或 前端全套视频教程 即可获取 300G 教程资…

ctfshow反序列化(web254-web266)

目录 web254 web255 web256 web257 web258 web259 web260 web261 web262 web263 web264 web265 web266 web254 源码 <?php/* # -*- coding: utf-8 -*- # Author: h1xa # Date: 2020-12-02 17:44:47 # Last Modified by: h1xa # Last Modified time: 2020…

【机组】存储器、总线及堆栈寄存器实验的解密与实战

​&#x1f308;个人主页&#xff1a;Sarapines Programmer&#x1f525; 系列专栏&#xff1a;《机组 | 模块单元实验》⏰诗赋清音&#xff1a;云生高巅梦远游&#xff0c; 星光点缀碧海愁。 山川深邃情难晤&#xff0c; 剑气凌云志自修。 ​目录 &#x1f33a;一、 实验目的 …

拿捏!相关性分析,一键出图!皮尔逊、斯皮尔曼、肯德尔、最大互信息系数(MIC)、滞后相关性分析,直接运行!独家可视化程序!

适用平台&#xff1a;Matlab2020及以上 相关性分析是一种统计方法&#xff0c;用于衡量两个或多个变量之间的关系程度。通过相关性分析&#xff0c;我们可以了解变量之间的相互关系、依赖性&#xff0c;以及它们是如何随着彼此的变化而变化的。相关性分析通常包括计算相关系数…

第二百七十九回

文章目录 1. 概念介绍2. 思路与方法2.1 实现思路2.2 实现方法 3. 示例代码4. 内容总结 我们在上一章回中介绍了"如何播放视频"相关的内容&#xff0c;本章回中将介绍如何选择单个图片文件.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1. 概念介绍 我们在前面章…

人工智能原理实验2(2)——罗马尼亚问题(贪婪搜索、A*搜索、BFS、DFS)

&#x1f9e1;&#x1f9e1;实验内容&#x1f9e1;&#x1f9e1; 根据上图以Zerind为初始状态&#xff0c;Bucharest为目标状态实现搜索&#xff0c;分别以贪婪搜索&#xff08;只考虑直线距离&#xff09;和A算法求解最短路径。 按顺序列出贪婪算法探索的扩展节点和其估价函数…

两个bbox的IoU计算步骤分析

IoU&#xff1a;交并比&#xff0c;数值上等于交集面积除以并集面积。 两个bbox的位置关系无外乎以上三种情况&#xff1a;&#xff08;1&#xff09;部分相交。&#xff08;2&#xff09;不相交。&#xff08;3&#xff09;包含。 计算步骤&#xff1a; 计算交集&#xff08…

亚像素边缘检测——基于模糊边缘模型的亚像素圆检测方法

论文&#xff1a;A Novel Subpixel Circle Detection Method Based on the Blurred Edge Model 期刊&#xff1a;IEEE Transactions on Instrumentation and Measurement, 71:1-11, 2021. 作者&#xff1a;Weihua Liu, Xianqiang Yang, Xuebo Yang, Hao Sun, Xinghu Yu, Huij…

【实战】SpringBoot自定义 starter及使用

文章目录 前言技术积累SpringBoot starter简介starter的开发步骤 实战演示自定义starter的使用写在最后 前言 各位大佬在使用springboot或者springcloud的时候都会根据需求引入各种starter&#xff0c;比如gateway、feign、web、test等等的插件。当然&#xff0c;在实际的业务…

Find My卡片正成为消费电子香饽饽,伦茨科技ST17H6x可以帮到您

今年CES许多公司发布支持苹果Find My的卡片产品&#xff0c;这种产品轻薄可充电&#xff0c;放在钱包、背包或者手提包可以防丢查找&#xff0c;在智能化加持下&#xff0c;防丢卡片使得人们日益关心自行车的去向。最新的防丢卡片与苹果Find My结合&#xff0c;智能防丢&#x…

AI绘画Stable Diffusion进阶使用

本文讲解&#xff0c;模型底模&#xff0c;VAE美化模型&#xff0c;Lora模型&#xff0c;hypernetwork。 文本Stable Diffusion 简称sd 欢迎关注 使用模型 C站&#xff1a;https://civitai.com/ huggingface&#xff1a;https://huggingface.co/models?pipeline_tagtext-to-…

单体架构、微服务和无服务器架构

前言 在这篇文章中&#xff0c;我将演示在决定使用单体架构、微服务架构和无服务器架构时的权衡的简化心智模型。目标是突显每种风格的固有优势和缺陷&#xff0c;并提供关于何时选择哪种架构风格的指导。 单体架构 对于小团队或项目来说是理想的入门架构。它简单易上手&…

hot100:07接雨水

题目链接&#xff1a; 力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 算法思想&#xff1a; 这里采取的是暴力解法和双指针的解法&#xff0c;但是这个题目还有其他的两种解法&#xff08;单调栈和动态规划&#xff0c;同学可以自行了解&#xff…

万界星空科技mes系统可以为企业带来什么好处

随着信息技术的不断发展&#xff0c;MES生产制造系统的作用不断凸显。万界星空科技MES生产制造可以为企业带来四个方面的好处&#xff1a;提升生产效率、降低生产成本、优化生产过程、提高生产质量。本文将从这四个方面分别进行详细阐述&#xff0c;旨在通过对MES生产制造系统的…

2024最新 8 款电脑数据恢复软件推荐分享

数据恢复是一个涉及从设备硬盘驱动器检索已删除文件的过程。这可能需要存储在工作站、笔记本电脑、移动设备、服务器、相机、闪存驱动器上的数据——任何在独立或镜像/阵列驱动器上存储数据的东西&#xff0c;无论是内部还是外部。 在某些情况下&#xff0c;文件可能被意外或故…

[安全警报] Npm木马利用“Oscompatible“包悄然安装AnyDesk

最近&#xff0c;一个名为OsCompatible的恶意包被上传到npm 。该包被发现包含一个针对 Windows 的远程访问木马。 这个名为OsCompatible的软件包于2024年1月9日发布&#xff0c;在被撤下之前共吸引了380次下载。 据了解&#xff0c;OsCompatible包含“几个奇怪的二进制文件”…

力扣hot100 反转链表 指针 递归 一题多解

Problem: 206. 反转链表 文章目录 思路&#x1f496; 迭代 双指针&#x1f496; 递归 思路 &#x1f468;‍&#x1f3eb; 大佬题解 &#x1f496; 迭代 双指针 ⏰ 时间复杂度: O ( n ) O(n) O(n) &#x1f30e; 空间复杂度: O ( 1 ) O(1) O(1) /*** Definition for …

【llm 微调code-llama 训练自己的数据集 一个小案例】

这也是一个通用的方案&#xff0c;使用peft微调LLM。 准备自己的数据集 根据情况改就行了&#xff0c;jsonl格式&#xff0c;三个字段&#xff1a;context, answer, question import pandas as pd import random import jsondata pd.read_csv(dataset.csv) train_data data…

安装MySQL8.0

安装MySQL8.0 第一步我们先把MySQL8.0的镜像拉一下&#xff08;建议在网络好的情况下 下拉镜像&#xff09; 之后我们在创造一个容器 conf目录 必须提前上传my.cnf文件到/data/conf目录 并且它与window中的配置文件my.ini后缀名是不一样 data目录 数据保存到宿主机中&#x…

Centos7 如何设置开机启动某个程序

以设置自动启动sentinel-dashboard作为案例 要在CentOS 7上设置开机启动一个Java程序&#xff0c;你可以按照以下步骤进行操作&#xff1a; 1. 进入应用程序的目录 cd /usr/localvim sentinel-dashboard.sh 2. 在sentinel-dashboard.sh 文件中 输入启动脚本 nohup java -D…