《Redis官方文档》使用Redis作为LRU缓存

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介:

原文链接  译者:boyhou (WeChat:HouYongBo923)

如果你使用redis作为缓存,当添加新数据时,若有内存大小等限制,系统默认会根据一定的规则自动清理旧数据。这种处理方式在开发社区中众所周知,因为它也是非常流行的缓存系统 memcached 的默认处理方式。

LRU(LRU全称是Least Recently Used,即最近最久未使用)实际上只是Redis支持的内存回收策略中的一种。这篇文章将要讲述Redis的 maxmemory 配置选项,该配置选项用来限制 Redis 的内存使用大小,同时深入研究 LRU(确切的说是近似LRU算法) 算法在 Redis 中的应用。

最大内存配置选项

maxmemory 配置选项使用来配置 Redis 的存储数据所能使用的最大内存限制。可以通过在内置文件redis.conf中配置,也可在Redis运行时通过命令CONFIG SET来配置。例如,我们要配置内存上限是100M的Redis缓存,那么我们可以在 redis.conf 配置如下:
maxmemory 100mb

设置 maxmemory 为 0 表示没有内存限制。在 64-bit 系统中,默认是 0 无限制,但是在 32-bit 系统中默认是 3GB。
当存储数据达到限制时,Redis 会根据情形选择不同策略,或者返回errors(这样会导致浪费更多的内存),或者清除一些旧数据回收内存来添加新数据。

回收策略

当内存达到限制时,Redis 具体的回收策略是通过 maxmemory-policy 配置项配置的。

以下的策略都是可用的:

  • noenviction:不清除数据,只是返回错误,这样会导致浪费掉更多的内存,对大多数写命令(DEL 命令和其他的少数命令例外)
  • allkeys-lru:从所有的数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰,以供新数据使用
  • volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰,以供新数据使用
  • allkeys-random:从所有数据集(server.db[i].dict)中任意选择数据淘汰,以供新数据使用
  • volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰,以供新数据使用
  • volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰,以供新数据使用

当 cache 中没有符合清除条件的 key 时,回收策略 volatile-lru, volatile-random 和volatile-ttl 将会和 策略 noeviction 一样返回错误。选择正确的回收策略是很重要的,取决于你的应用程序的访问模式。但是,你可以在程序运行时重新配置策略,使用 INFO 输出来监控缓存命中和错过的次数,以调优你的设置。

普适经验规则:

  • 如果期望用户请求呈现幂律分布(power-law distribution),也就是,期望一部分子集元素被访问得远比其他元素多时,可以使用allkeys-lru策略。在你不确定时这是一个好的选择。
  • 如果期望是循环周期的访问,所有的键被连续扫描,或者期望请求符合平均分布(每个元素以相同的概率被访问),可以使用allkeys-random策略。
  • 如果你期望能让 Redis 通过使用你创建缓存对象的时候设置的TTL值,确定哪些对象应该是较好的清除候选项,可以使用volatile-ttl策略。

当你想使用单个Redis实例来实现缓存和持久化一些键,allkeys-lru和volatile-random策略会很有用。但是,通常最好是运行两个Redis实例来解决这个问题。

另外值得注意的是,为键设置过期时间需要消耗内存,所以使用像allkeys-lru这样的策略会更高效,因为在内存压力下没有必要为键的回收设置过期时间。

回收过程

理解回收过程是运作流程非常的重要,回收过程如下:

  • 一个客户端运行一个新命令,添加了新数据。
  • Redis检查内存使用情况,如果大于maxmemory限制,根据策略来回收键。
  • 一个新的命令被执行,如此等等。

我们添加数据时通过检查,然后回收键以返回到限制以下,来连续不断的穿越内存限制的边界。
如果一个命令导致大量的内存被占用(比如一个很大的集合保存到一个新的键),那么内存限制很快就会被这个明显的内存量所超越。

近似LRU算法

Redis的LRU算法不是一个严格的LRU实现。这意味着Redis不能选择最佳候选键来回收,也就是最久未被访问的那些键。相反,Redis 会尝试执行一个近似的LRU算法,通过采样一小部分键,然后在采样键中回收最适合(拥有最久访问时间)的那个。

然而,从Redis3.0开始,算法被改进为维护一个回收候选键池。这改善了算法的性能,使得更接近于真实的LRU算法的行为。Redis的LRU算法有一点很重要,你可以调整算法的精度,通过改变每次回收时检查的采样数量。

这个参数可以通过如下配置指令:

maxmemory-samples 5

Redis没有使用真实的LRU实现的原因,是因为这会消耗更多的内存。然而,近似值对使用Redis的应用来说基本上也是等价的。下面的图形对比,为Redis使用的LRU近似值和真实LRU之间的比较。

687474703a2f2f72656469732e696f2f696d616765732f7265646973646f632f6c72755f636f6d70617269736f6e2e706e67

用于测试生成了上面图像的Redis服务被填充了指定数量的键。键被从头访问到尾,所以第一个键是LRU算法的最佳候选回收键。然后,再新添加50%的键,强制一般的旧键被回收。

你可以从图中看到三种不同的原点,形成三个不同的带。

  • 浅灰色带是被回收的对象
  • 灰色带是没有被回收的对象
  • 绿色带是被添加的对象

在理论的LRU实现中,我们期望看到的是,在旧键中第一半会过期。而Redis的LRU算法则只是概率性的过期这些旧键。 你可以看到,同样使用5个采样点,Redis 3.0表现得比Redis 2.8要好,Redis 2.8中最近被访问的对象之间的对象仍然被保留。在Redis 3.0中使用10为采样大小,近似值已经非常接近理论性能。

注意,LRU只是一个预测模型用来指定键在未来如何被访问。另外,如果你的数据访问模式非常接近幂律,大多数的访问都将集中在一个集合中,LRU近似算法将能处理得很好。

在模拟实验的过程中,我们发现使用幂律访问模式,真实的LRU算法和Redis的近似算法之间的差异非常小,或者根本就没有。然而,你可以提高采样大小到10,这会消耗额外的CPU,来更加近似于真实的LRU算法,看看这会不会使你的缓存错失率有差异。

使用CONFIG SET maxmemory-samples <count>命令在生产环境上试验各种不同的采样大小值是很简单的。

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
1月前
|
缓存 NoSQL 安全
【Redis】缓存穿透
【Redis】缓存穿透
30 0
|
3天前
|
缓存 NoSQL Java
使用Redis进行Java缓存策略设计
【4月更文挑战第16天】在高并发Java应用中,Redis作为缓存中间件提升性能。本文探讨如何使用Redis设计缓存策略。Redis是开源内存数据结构存储系统,支持多种数据结构。Java中常用Redis客户端有Jedis和Lettuce。缓存设计遵循一致性、失效、雪崩、穿透和预热原则。常见缓存模式包括Cache-Aside、Read-Through、Write-Through和Write-Behind。示例展示了使用Jedis实现Cache-Aside模式。优化策略包括分布式锁、缓存预热、随机过期时间、限流和降级,以应对缓存挑战。
|
12天前
|
存储 缓存 NoSQL
Java手撸一个缓存类似Redis
`LocalExpiringCache`是Java实现的一个本地缓存类,使用ConcurrentHashMap存储键值对,并通过ScheduledExecutorService定时清理过期的缓存项。类中包含`put`、`get`、`remove`等方法操作缓存,并有`clearCache`方法来清除过期的缓存条目。初始化时,会注册一个定时任务,每500毫秒检查并清理一次过期缓存。单例模式确保了类的唯一实例。
11 0
|
1月前
|
缓存 NoSQL Java
spring cache整合redis实现springboot项目中的缓存功能
spring cache整合redis实现springboot项目中的缓存功能
45 1
|
1月前
|
缓存 NoSQL 数据库
[Redis]——数据一致性,先操作数据库,还是先更新缓存?
[Redis]——数据一致性,先操作数据库,还是先更新缓存?
|
1月前
|
存储 缓存 算法
深入探究LRU缓存机制:优化内存利用与提升性能
深入探究LRU缓存机制:优化内存利用与提升性能
114 1
|
1月前
|
缓存 NoSQL Java
【九】springboot整合redis实现启动服务时热点数据保存在全局和缓存
【九】springboot整合redis实现启动服务时热点数据保存在全局和缓存
40 0
|
1月前
|
缓存 监控 NoSQL
解析Redis缓存雪崩及应对策略
解析Redis缓存雪崩及应对策略
|
1月前
|
存储 缓存 NoSQL
Redis高效缓存:加速应用性能的利器
Redis高效缓存:加速应用性能的利器
|
1月前
|
存储 缓存 Java
【Spring原理高级进阶】有Redis为啥不用?深入剖析 Spring Cache:缓存的工作原理、缓存注解的使用方法与最佳实践
【Spring原理高级进阶】有Redis为啥不用?深入剖析 Spring Cache:缓存的工作原理、缓存注解的使用方法与最佳实践

热门文章

最新文章