千万数据展示Redis淘汰策略的强大

1. 引言1ff0b486e3264bfc8c939dee3697d21d.png

在现代应用中,数据存储和管理是至关重要的。Redis作为一个高性能的内存数据库,提供了多种数据结构和灵活的淘汰策略,使其在处理大规模数据时表现出色。本文将展示如何在CentOS 7环境下,使用Redis 7.0进行千万级数据的测试,并验证其淘汰策略的强大。 

2. 准备工作

2.1 环境配置

  • 操作系统:CentOS 7
  • Redis版本:Redis 7.0
  • 内存:16GB

2.2 Redis启动配置

在Redis的配置文件中,我们需要设置最大内存大小,以便测试其淘汰策略。以下是配置步骤:

  1. 打开Redis配置文件(通常位于 /etc/redis.conf):

vi /etc/redis.conf

设置最大内存大小为3GB,默认的淘汰策略,保存并退出编辑器。

# 设置最大内存大小为3GB
maxmemory 3gb
# 默认的淘汰策略是 不淘汰任何键
maxmemory-policy noeviction

3. 开始测试

3.1 lua脚本随机生成数据测试

生成5千万的随机数据(小伙伴们请在测试环境模仿),key为memory-test进行插入操作,耗时22.49s

EVAL "for i=1,50000000 do local key = 'key-' .. math.random(1, 100000000) redis.call('SET', key, 'value-' .. math.random(1, 100)) end" 0

3.1 内存占用4.24G,键三千六百万

由于Redis默认的淘汰策略是不淘汰任何键的,已经超过启动配置文件配置的3GB

4. 更改淘汰策略(从所有键中选择最近最少使用(LRU)的键进行淘汰)

127.0.0.1:6379> CONFIG SET maxmemory-policy allkeys-lru

此时。。过了大概10秒钟。。。然后直接内存占用变成阈值的3G      

42e54f5a31c94f0993616a049bf6ce34.png

清理能力是相当可观的

5.更改淘汰策略noeviction追加数据,触发拒绝插入

再生成千万的随机数据(小伙伴们请在测试环境模仿),key为memory-test进行插入操作

127.0.0.1:6379> EVAL "for i=1,40000000 do local key = 'key-' .. math.random(1, 100000000) redis.call('SET', key, 'value-' .. math.random(1, 100)) end" 0
(error) OOM command not allowed when used memory > 'maxmemory'. script: dcd03f2a5173001fd0c77681844bc05262124649, on @user_script:1.

插入的数据都是这种随机的:

5.1内存占用变化

>>>

追加的过程中是无法进行操作的比如info memory会阻塞,因为Redis本身在单线程执行,此时我们可以看到Redis使用单核CPU占用率约100%,时而打到103%,大概是Redis底层处理其他事情的线程同时在搞

beab9a81f9f442689adc93e84d3ab21e.png

写入内存过程持续变化

22e7458ad9d643cfa4fd3b28e61bf19c.jpeg

系统可用内存逐渐变小直到10秒出现了这样的情况

没错!我们的Redis淘汰策略在发威,约1G的数据10秒被干掉

c49f5640e2e649959bb3385643167f9d.jpeg

6.持续Lua脚本原子性大批量插入过程中会遇到的问题

阻塞其他命令(如下Java服务)

如果其他命令在等待 Lua 脚本完成,可能导致连接池中的连接被占用,最终导致连接池耗尽,进而引发 RedisConnectionFailureException

Node source: NodeSource [slot=0, addr=null, redisClient=null, redirect=null, entry=null],
connection: RedisConnection@1920052236 [redisClient=[addr=redis://IP:6379], channel=[id: 0x3123abcd, L:/IP:49722 ! R:IP/IP:6379], currentCommand=null, usage=1],
command: (DEL), params: [[1,12,3,4,5,6,7, ...]] after 3 retry attempts;
nested exception is org.redisson.client.WriteRedisConnectionException: Unable to write command into connection! Increase connection pool size.
Node source: NodeSource [slot=0, addr=null, redisClient=null, redirect=null, entry=null],
connection: RedisConnection@1921005696 [redisClient=[addr=redis://IP:6379], channel=[id: 0x3357adg, L:/IP:10086 ! R:IP/IP:6379], currentCommand=null, usage=1],
command: (DEL), params: [[1,12,3,4,5,6,7, ...]] after 3 retry attempts
at org.redisson.spring.data.connection.RedissonExceptionConverter.convert(RedissonExceptionConverter.java:40)  
at org.redisson.spring.data.connection.RedissonExceptionConverter.convert(RedissonExceptionConverter.java:35)  
at org.springframework.data.redis.PassThroughExceptionTranslationStrategy.translate(PassThroughExceptionTranslationStrategy.java:44)  
at org.redisson.spring.data.connection.RedissonConnection.transform(RedissonConnection.java:195)  
at org.redisson.spring.data.connection.RedissonConnection.syncFuture(RedissonConnection.java:190)  
at org.redisson.spring.data.connection.RedissonConnection.sync(RedissonConnection.java:356)  
at org.redisson.spring.data.connection.RedissonConnection.write(RedissonConnection.java:722)  
at org.redisson.spring.data.connection.RedissonConnection.del(RedissonConnection.java:209)  
at org.springframework.data.redis.core.RedisTemplate.lambda$delete$2(RedisTemplate.java:713)  
at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:228)  
at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:188)  
at org.springframework.data.redis.core.RedisTemplate.delete(RedisTemplate.java:713)  
at sun.reflect.GeneratedMethodAccessor661.invoke(Unknown Source)  
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)  
at java.lang.reflect.Method.invoke(Method.java:498)  
at org.springframework.scheduling.support.ScheduledMethodRunnable.$sw$original$run$c8tpsq2(ScheduledMethodRunnable.java:84)  
at org.springframework.scheduling.support.ScheduledMethodRunnable.$sw$auxiliary$ac1ucb1.call(Unknown Source)  
at org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstMethodsInter.intercept(InstMethodsInter.java:86)  
at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:90)  
at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54)  
at org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:93)  
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)  

内存使用触发淘汰策略,导致数据会被删除

大规模操作应拆分为多个小批次异步写入,避免一次性操作过多数据,以减少对主线程的阻塞

执行长时间运行的 Lua 脚本可能会导致 Redis 的性能下降和阻塞其他命令。建议使用分批处理和异步操作来减轻这种影响。

7.更多种淘汰策略,自行选择

  • noeviction:不淘汰任何键,达到限制时返回错误。
  • allkeys-lru:从所有键中选择最少使用的键进行淘汰。
  • volatile-lru:只从设置了过期时间的键中选择最少使用的键进行淘汰。
  • allkeys-random:随机选择键进行淘汰。
  • volatile-random:随机选择设置了过期时间的键进行淘汰。
  • volatile-lfu:从设置了过期时间的键中选择使用频率最低的键进行淘汰。
  • allkeys-lfu:从所有键中选择使用频率最低的键进行淘汰。

以上Blog用到的命令有:

#查看当前的内存阈值配置
127.0.0.1:6379> CONFIG GET maxmemory

#设置最大内存阈值
127.0.0.1:6379> CONFIG SET maxmemory 3221225472

#查看redis内存使用情况的命令
127.0.0.1:6379> info memory

#设置 maxmemory-policy 内存阈值淘汰策略
127.0.0.1:6379> CONFIG SET maxmemory-policy 策略

#查看当前的内存阈值淘汰策略
127.0.0.1:6379> CONFIG GET maxmemory-policy

#生成随机 n 条数据的Lua命令
127.0.0.1:6379> EVAL "for i=1,n do redis.call('LPUSH', 'memory-test', 'value-' .. math.random(1, 100)) end" 0

以上命令需要在redis-cli中进行操作,直接登陆命令附这:

redis-cli -h  IP地址 -p 端口 -a 密码

在测试过程中,我们观察到Redis在达到最大内存限制后,成功地按照设定的淘汰策略移除了最少使用的数据。这证明了Redis在处理千万级数据时的高效性和灵活性。

通过本次测试,我们验证了Redis的淘汰策略在处理大规模数据时的强大能力。Redis不仅能够高效存储数据,还能在内存达到限制时智能地管理数据,确保系统的稳定性和性能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

服务端相声演员

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值