一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第29天,点击查看活动详情。
前言
大家都知道我前几天介绍了我们的开发团队基于flink消费kafka的超多数据的场景。今天我将介绍一下我们在处理超多数据存储到redis的实践
业务场景简介
我们的业务场景十分简单,要实现1秒之内统计至少2万条数据的然后生成产量、最新的记录、最新的三条记录存储到redis。
但是在我们分析之后,发现需要寻找解决方案让redis实现“超频”,突破每秒10万的限制。主要的原因为:我们存储产量需要统计的颗粒度比较精细,需要统计总量、年、月、日、时、分的产量,加上统计最新的记录命令和5条记录最后三条数据的命令2条,我们在一条数据上就要执行6+5+2=11个redis命令。
现在的需求变为了:需要让redis可以实现每秒22w+的数据落盘
大家如果对最终实现感兴趣,我可以单开一篇文件介绍一下最终实现,今天我们只介绍redis的“超频”探索,希望会给大家带来一些收获
常见的redis超频方式
- lettuce
- lua脚本
- pipelined管道传输 首先先和大家说一下,使用以上三种方式都无法突破10w/s的限制,本文只是总结一下三种常用的连接器的传输效率
lettuce
lettuce简介
lettuce是什么?一言以蔽之:基于netty的nio redis传输框架,因为我们使用lettuce时对于它的异步不太放心,最终就没有使用了。大家如果有不是很重要我的数据可以使用lettuce来存储。
lettuce使用
想在项目中使用lettuce传输redis其实十分简单,如果是spring boot项目引入如下依赖即可注入redisTemplate来使用lettuce
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
我发现有很多文章写了使用lettuce时引入common-pool2来做了连接池,但是参考lettuce github的wiki连接池,添加连接池显得画蛇添足了
lua
lua简介
lua 是一个脚本语言,使用lua脚本的好处是。可以单线程原子性的来推送数据至redis。我们可以将11个命令中非固定命令的部分作为参数传入lua脚本中,一次就能执行
使用lua
我这里以spring boot +jedis为例,简单写一下如何使用
定义脚本、加载脚本
我们在resources文件夹下创建script.lua内容如下
redis.call('lpush',KEYS[1],ARGV[1])
redis.call('set', KEYS[2], ARGV[2])
redis.call('set', KEYS[3], ARGV[3])
redis.call('set', KEYS[4], ARGV[4])
然后建议在项目启动之后加载script中的脚本信息放到一个bean中,最后lua 脚本在我们的项目中以字符串的形式调用
使用lua
jedis.eval(scriptStr,keys,argvs);
使用lua总体来说是个不错的解决方案,可以满足大部分的业务场景,接下来我们使用管道传输来进一步加快传输效率
pipelined
pipelined简介
属性nio的读者大部分对应文件流比较熟悉,其中channel就是使用管道传输大大加快了数据传输效率。感兴趣的可以去redis官网看一下管道传输。
如何使用pipelined
我这里还是使用jedis来简单介绍一下使用方法 jedis获取管道
Pipeline pipeline = jedis.pipelined();
管道执行hset命令
pipeline.hset("key", "params",value);
最后执行sync
pipeline.sync();
关于pipeline有几点注意事项 1:最大支持在一个sync周期内刷新1万条记录 2:记录过多会内存溢出 3:sync建议:建议在一个周期内,每隔1000条刷新一次,且在最后一定要刷新一次 4:pipeline不是原子性!!!
结语
今天我们简单介绍了常见的几种redis优化策略,希望各位在阅读完之后能有所收获