降低Redis内存使用和提升性能的一些方案

前言
一、前言

Redis在现在开发中已经成为了一个不可或缺的组件,很多项目都会依赖Redis进行开发,当数据量和请求量以及Redis本身访问率不高的情况下,Redis不会成为性能瓶颈,但是如果本身处于高并发海量数据这些情况下,即便是Redis,也会成为性能瓶颈中的一环,本文就是基于Redis已经成为我项目中的一个性能瓶颈之后,深入研究后产生的,另外本文主要是针对开发过程中如何降低Redis内存使用率以及整体提升代码性能,不涉及到Redis集群搭建相关性能调优

二、降低内存占用
a)减少key长度

首先key本身也是存在redis内存中的,每条数据增加2个字节的长度,成百上千万数据加起来,就占用了不少的内存了,所以需要尽可能的缩减key的长度,能保证key唯一性就好,没有必要把一些前缀设置很长或者使用英文全称

b)合理的缓存有效期

让缓存在不被使用时尽快过期,避免占用内存,如果有大量缓存在业务中不会被使用,或者缓存从生效到不被使用只有1小时,但是缓存有效期却设置的1天,那么就会导致大量无用缓存占用大量内存,所以一定要合理评估每一个缓存的生命周期。当内存资源明显紧张超过CPU等资源时,可以采取主动删除明确不使用的缓存,或者针对热点周期进行缓存生命周期配置,并配置好合理的恢复缓存机制

示例:

1.主动删除缓存

假如有以下场景:某商城,下单订单时需要先预览订单,并且生成订单逻辑复杂,且订单量大,所以预览订单时会先生成订单缓存,创建订单后,因为某些业务还需要快速访问订单,如发起支付时获取金额信息以及验证等,并且支付有效期有1天,也就是如果没有支付,一天内发起支付的可能性较大,需要快速获取相关信息。 如果用固定有效期1天的话,那么其实在订单支付之后,访问订单详情概率变很低,也就是说一旦订单支付完成,订单详情基本上不访问,那么就可以采取主动删除缓存的方式。首先创建缓存的时候,设置缓存有效期是1天,同时订单真正创建时候时候,更新缓存为1天(支付有效期1天),在订单被支付之后,则主动删除缓存(如果还有跳转页面并且需要通过缓存获取订单详情的,则设置一个接口完全可以获取到时间)。这样如果订单没有支付,那么一天内还是可以快速访问到这个订单的缓存信息,如果被支付,因为基本上不访问了,缓存也能尽快过期避免占用内存

2.热点周期模式

假设还是上面商城,场景稍作变换,订单支付完成之后,其实很多人会很习惯的关注订单进度(会查看订单详情),但是有些人其实不关心,买完就不管,而查看的正常来说每天会关注几次,知道收货之后,那我们可以模拟TTI的方式(Redis本质上是没有TTI的),在我们每次通过Redis获取到缓存之后,重新设置缓存有效期为1天,这样可以让热点数据一直处于相对有效,当没有获取到数据时,就从数据库等其他地方查询并缓存

c)合理的结构

存放在Redis的数据结构尽可能缩减,避免无用字段进入缓存,比如常见的直接用数据库对应实体类对象直接存放到Redis,导致很多无用字段也进行了缓存,如一些乐观锁、创建时间等字段,精简缓存到满足业务需求即可。尽可能保证每个字段都是业务能用上的

d)合理的序列化

业务中常见的是把对象缓存到Redis中,而缓存对象,就会涉及到序列化,常见的序列化方式有json、默认序列化、kryo等,选择合适的序列化不仅能提升程序运行速度,还能降低内存占用,当然不同的结构也会影响不同的序列化对于内存占用的影响比例,所以具体选择那种序列化也需要根据自己实际情况做选择,但是通常情况下,加入对象中有10个字段,那么大概率内存占用是Java默认序列化 > json > kryo,但是不同的序列化也会产生很多其他问题,比如可读性和通用性,如果这个Redis缓存并不只是一种语言的程序会访问,那么就要考虑采用多种语言都能序列化的方式了

e)数据压缩

很多缓存数据可能本身只是一个字符串,或者因为通用性问题最终选择了json格式的序列化,那么也可以通过压缩来降低内存占用,但是目前redis本身是不支持压缩的,所以只能是在客户端进行压缩和解压操作,但是压缩和解压会带来额外的CPU消耗

三、提升性能
a)选择合适的客户端

常见的有Jedis、Lettuce(现在Spring的默认客户端)、Redisson,每个客户端都有自己的优劣,可以根据实际情况进行选择

b)合适的序列化

不同序列化方式也是有差异的,虽然差异很小,但是如果是海量操作,哪怕很小的差异,也是能提升不小的时间成本的,但是序列化也会影响内存占用,并且如果访问量很大的情况下,通常缓存的key也不会小,所以要在内存和性能中做平衡,但是相对来说,序列化本身是在客户端实现的,而客户端的CPU资源相对来说更容易扩展,所以大部分情况下序列化还是考虑内存占用为主

c)合理的压缩和缩减数据

首选Redis是以服务形式存在的,并且正常来说Redis是会有专门的服务器,所以如果访问量很大,并且数据本身也不少的话,网络IO也会成为性能瓶颈的一环,所以如果保存的内容越小,传输也会更快,更不容易造成网络阻塞

d)批量操作

批量操作包括批量保存和批量获取以及一些其他条件性的保存等,如果业务可以通过批量操作的方式来实现,尽可能使用批量操作,首先批量保存和获取是常用客户端已经支持的操作,同时如果是一些原子性的操作或者有条件判断的,可以利用lua脚本来实现,目前常用的客户端也是支持lua脚本的操作的,比如常见的判断某个key1是否存在,如果存在则保存key2的数据,如果不存在则删除key1的数据这些都可以通过lua脚本实现,这样不仅降低了网络占用,同时还能保证当前不会有并发问题,比如判断key1的时候,key1的确不存在,但是当你写入key2的时候,key1却存在了,但是使用lua脚本就不会有这种问题

相关推荐
精进攻城狮@36 分钟前
Redis(value的数据类型)
数据库·redis
酸奶代码2 小时前
Spring AOP技术
java·后端·spring
jwybobo20072 小时前
redis7.x源码分析:(3) dict字典
linux·redis
simpleGq2 小时前
Redis知识点整理 - 脑图
数据库·redis·缓存
代码小鑫2 小时前
A034-基于Spring Boot的供应商管理系统的设计与实现
java·开发语言·spring boot·后端·spring·毕业设计
莳光.2 小时前
122、java的LambdaQueryWapper的条件拼接实现数据sql中and (column1 =1 or column1 is null)
java·mybatis
.生产的驴3 小时前
SpringCloud Gateway网关路由配置 接口统一 登录验证 权限校验 路由属性
java·spring boot·后端·spring·spring cloud·gateway·rabbitmq
李少兄3 小时前
解决Spring Boot整合Redis时的连接问题
spring boot·redis·后端
提高记忆力3 小时前
SpringBoot整合FreeMarker生成word表格文件
java·spring
日里安3 小时前
8. 基于 Redis 实现限流
数据库·redis·缓存