让Redis循环写入时提高10倍的技巧

如何优化 Redis 的事件循环机制以提高性能? 使用高性能网络设备和驱动。 阅读详情

简介

有微服务的架构不代表性能好,而使用微服务的架构必须要求性能好。这句话不矛盾。矛盾在外面很多人认为微服务架构代表高并发,实际上不是。我们有“书面微服务”和“实际微服务”之说。比如说网上大量教人把httpConnection或者是FeignClient的timeout改成30秒就不会超时了?那这要什么微服务呢?微服务解决的到底是什么呢?

绝大多数人忘记了微信的本质是用来解决什么问题的。

互联网应用在To C端有6秒之说,即一个小程序/APP应用打开和加载过程>6秒,肯定新用户就不会再去用了,4秒算平均水平,一般大厂都是做到加载页面1秒。

各位要知道,任何一个“查询”式页面打开一秒,全站可能存在上千、上万个API,有时一个页面是需要通过几十个API组合在一起的。因此对于API在系统内我们互联网界的要求是约束在“一根API需要在前端万级并发的情况下+系统每张单表千万数据的Data Volumn下响应时间在100毫秒之内的“。

这就是1秒钟说的由来。

  • To C端界面加载1秒;
  • 任何微服务(即时响应类)要在100毫秒内

这是因为,你的应用上会有不少外联、不少回调、不少地图、配送、支付这种调用。它们都会对我们的应用造成“级联式雪崩”。

为了避免复杂系统的雪崩就需要及时“熔断、限流、升级”。这就是微服务的本质。

如果已经用了微服务架构、实际发觉这边老是超时,于是很多人一味地去放长这个连接时间自以为就解决了。

这样的人群占比大概超过90%(业界有统计)。

因此用了微服务即代表着对于开发的技能要求水平更高,任何可以提高100毫秒的地方都值得去做。

微服务架构里特别强调一个哲理“勿以善小而不为之,勿以恶小而为之”。

Redis在for循环里写大量数据的梗

通常情况我们经常会碰到要把千、万条数据一次写入Redis,在互联网应用场景中,我们经常把百万、千万级数据也会往Redis里塞,如:Redis Bloom。

经典的写法都是以下这样的:

@Test    public void testForLoopAddSingleString() throws Exception {        long startTime = System.currentTimeMillis();        String redisValue = "";        for (int i = 0; i < 10000; i++) {            StringBuilder redisKey = new StringBuilder();            redisKey.append("key_").append(i);            redisValue = String.valueOf(i);            redisTemplate.opsForValue().set(redisKey.toString(), redisValue);        }        long endTime = System.currentTimeMillis();        long costTime = endTime - startTime;        logger.info(">>>>>>test batch add into redis by using normal for loop spent->" + costTime);    }

运行后得到如下输出:

Come on man,这才1万条数据,插入Redis要用4.7秒。

我用的是我一直用于模拟千万级数据量的服务器,这台服务器比公司的生产服务器性能还要好几倍,在这样的服务器性能上插入1万条数据都要4.7秒,生产上我们还要算上读存储出来再写Redis的网络开销,实际只会更慢。这种效率是不能忍受的。

增强版写法-Redis Piepeline写法

我们给Redis在它的“写操作上”,不需要增加任何第三方包,自带一颗“永久有效”写加速器,如下代码:

@Test    public void testPipelineAddSingleString() throws Exception {        RedisSerializer<String> keySerializer = (RedisSerializer<String>)redisTemplate.getKeySerializer();        RedisSerializer<Object> valueSerializer = (RedisSerializer<Object>)redisTemplate.getValueSerializer();        long startTime = System.currentTimeMillis();        redisTemplate.executePipelined((RedisCallback<Object>)pipeLine -> {            try {                String redisValue = "";                for (int i = 0; i < 10000; i++) {                    StringBuilder redisKey = new StringBuilder();                    redisKey.append("key_").append(i);                    redisValue = String.valueOf(i);                    pipeLine.setEx(keySerializer.serialize(redisKey.toString()), 10,                        valueSerializer.serialize(redisValue));                }            } catch (Exception e) {                logger.error(">>>>>>test batch add into redis by using pipeline for loop error: " + e.getMessage(), e);            }            return null;        });        long endTime = System.currentTimeMillis();        long costTime = endTime - startTime;        logger.info(">>>>>>test batch add into redis by using pipeline for loop spent->" + costTime);    }

大家来看这种写法,你可以认为这种写法和for循环里套着一条条的update table set field=value变成了合并后一个batchUpdate的用法一样。看一下,这种写法带来的效率上的区别吧:

Oh My God。。。写法上有一点不同。

4,745毫秒 VS 564毫秒,我算它600毫秒好了,但这得差多少?各位想想。

下面同样给出相应的简单Hash结构的批量写Redis写法

假设我们要往redis里插一个这样的结构的Hash

@Test    public void testPipelineAddHash() throws Exception {        RedisSerializer<String> keySerializer = (RedisSerializer<String>)redisTemplate.getKeySerializer();        RedisSerializer<Object> valueSerializer = (RedisSerializer<Object>)redisTemplate.getValueSerializer();        long startTime = System.currentTimeMillis();        String hashKey = "pipeline_hash_key";        redisTemplate.executePipelined((RedisCallback<Object>)pipeLine -> {            try {                String redisValue = "";                for (int i = 0; i < 10000; i++) {                    StringBuilder redisKey = new StringBuilder();                    redisKey.append("key_").append(i);                    redisValue = String.valueOf(i);                    pipeLine.hSet(keySerializer.serialize(hashKey), keySerializer.serialize(redisKey.toString()),                        valueSerializer.serialize(redisValue));                }            } catch (Exception e) {                logger.error(">>>>>>testPipelineAddHash by using pipeline for loop error: " + e.getMessage(), e);            }            return null;        });        long endTime = System.currentTimeMillis();        long costTime = endTime - startTime;        logger.info(">>>>>>testPipelineAddHash by using pipeline for loop spent->" + costTime);    }

只用了500毫秒左右。

批量往Redis里插入复杂类型Hash的写法

如下,1万个JavaBean以Hash结构存在Redis里。

@Test    public void testPipelineAddHashBean() throws Exception {        RedisSerializer<String> keySerializer = (RedisSerializer<String>)redisTemplate.getKeySerializer();        Jackson2JsonRedisSerializer jacksonSerial = new Jackson2JsonRedisSerializer<>(Object.class);        long startTime = System.currentTimeMillis();        String hashKey = "pipeline_hash_key";        redisTemplate.executePipelined((RedisCallback<Object>)pipeLine -> {            try {                String redisValue = "";                for (int i = 0; i < 10000; i++) {                    StringBuilder redisKey = new StringBuilder();                    redisKey.append("key_").append(i);                    redisValue = String.valueOf(i);                    UserBean user = new UserBean();                    user.setUt(redisKey.toString());                    user.setShareCode(redisValue);                    pipeLine.hSet(keySerializer.serialize(hashKey), keySerializer.serialize(redisKey.toString()),                        jacksonSerial.serialize(user));                }            } catch (Exception e) {                logger.error(">>>>>>testPipelineAddHash by using pipeline for loop error: " + e.getMessage(), e);            }            return null;        });        long endTime = System.currentTimeMillis();        long costTime = endTime - startTime;        logger.info(">>>>>>testPipelineAddHash by using pipeline for loop spent->" + costTime);    }

400毫秒左右(有时600毫秒、有时400毫秒、大多情况在400毫秒内)

Redis Bloom过滤器内的类Pipeline写法

同理,往Redis Bloom过滤器里喂值这个量还要大,那么喂入的越快,bloom拦截时的“黑窗期”就越短,对系统越有利。因此它也有同样的批量写入的好方法,如下代码:

public <T> void addByBloomFilter(BloomFilterHelper<T> bloomFilterHelper, String key, T value) {        Preconditions.checkArgument(bloomFilterHelper != null, "bloomFilterHelper不能为空");        int[] offset = bloomFilterHelper.murmurHashOffset(value);        BitFieldSubCommands commands = BitFieldSubCommands.create();// 使用合并写法,假设10万条数据一个for需要12秒        for (int i : offset) {            commands = commands.set(BitFieldSubCommands.BitFieldType.unsigned(1)).valueAt(i).to(1);// 合并bit        }        redisTemplate.opsForValue().bitField(key, commands);// 再一次写入redis    }

10万条数据用了45秒

如果用传统写法 如以下代码为传统写法

public <T> void addByBloomFilterSingleFor(BloomFilterHelper<T> bloomFilterHelper, String key, T value) {        Preconditions.checkArgument(bloomFilterHelper != null, "bloomFilterHelper不能为空");        int[] offset = bloomFilterHelper.murmurHashOffset(value);        for (int i : offset) {            redisTemplate.opsForValue().setBit(key, i, true);        }    }

10万条数据用了455秒

 

CXL3.0 CDAT(Coherent Device Attributes Table) 在说明CDAT之前,先讲一下CXL中的DOE类型:CXL协议里定义了两种DOE的类型,一种是Compliance就是合规性测试的内容,还有一种就是CDAT(一致性属性表)Memory_Info_Valid是CXL设备的内存配置完成标志,位于DVSEC(Designated Vendor-Specific Extended Capability)的CXL Range X Size Low寄存器中(X为1或2)。 阅读详情

相关推荐

【中软软件测试笔试题】中国软件与技术服务股份有限公司笔试一卷

请分别写出ISO7层架构,并分别说明TCP、UDP协议分别在处于哪一层,以及TCP、 UPD协议有什么区别。 答:7 应用层 6 表示层 5 会话层 4 传输层 3网络层 2 数据链路层 1 物理层 第四层;UDP是面向无连接、不可靠数据传输协议; TCP是面向连接、可靠数据传输协议......

Yanan990830的博客 1817

Redis 常见性能问题和解决方案?

Redis 常见性能问题和解决方案? (1) Master 最好不要做任何持久化工作,如 RDB 内存快照和 AOF 日志文件 (2) 如果数据比较重要,某个 Slave 开启 AOF 备份数据,策略设置为每秒同步一次 (3) 为了主从复制的速度和连接的稳定性, Master 和 Slave 最好在同一个局域网内 (4) 尽量避免在压力很大的主库上增加从库 (5) 主从复制不要用图状结构,用单向链表结构更为稳定, 即: Master <- Slave1 <- Slave2 <-Slav

m0_51684972的博客 434

GX WORKS2通过交换机连接PLC的方法

三菱PLC连接方法的分享

weixin_64927621的博客 3620

RedisTemplet批量插入数据

通过RedisTemplet快速、批量的插入数据

mlwsmqq的博客 4818

数学建模:相关性分析学习——皮尔逊(pearson)相关系数与斯皮尔曼(spearman)相关系数

相关性分析:求解相关系数并绘制热力图进行对比

weixin_67565775的博客 8万+

理解ADC:为什么需要抗混叠滤波器?

本文介绍 ADC 的混叠现象,引出抗混叠滤波器,并介绍一种开关电容滤波器,它具有很小的增益过渡带。

疯狂的运放 5946

redis循环键_For循环从redis延迟获取项目

我正在使用带有node_redis的Node.js,并遍历一个对象并在Redis中查找数据,然后返回结果。我有这样的设置:for (var key in items) {if (items.hasOwnProperty(key)) {app.client.llen(items[key].id+'_click', function(err, total) {items[key].total = to...

weixin_42412396的博客 919

Java对接海康威视明眸门禁设备

海康威视合作生态致力打造一个能力开放体系、两个生态圈,Hikvision AI Cloud开放平台是能力开放体系的核心内容。它是海康威视基于多年在视频及物联网核心技术积累之上,融合AI、大数据、云计算等技术,为合作伙伴提供的一个二次开发及创新的平台。的SDK复制到linux的lib目录下(如果是docker发布,记得做好共享映射哦)大家有没有想过,windows版的SDK为什么要复制到项目根目录的lib文件夹下?所以发布到linux的时候,我们需要将。打开AcsMain文件。................

Good Luck 7878

redis的第一个lua脚本for循环执行成功运行

在redis用lua脚本执行循环

6rl的专栏 1605

2025年CSP-X初赛真题及答案解析(江西)(完善程序第1题)

2025年CSP-X初赛真题及答案解析(江西)(完善程序第1题)

王老师青少年编程 715

树莓派 5 AI 套件(Hailo)示例

该用户自定义类作为输入传递给回调函数,该函数在管道输出上运行。它用于主程序与回调函数之间的通信。它对 hailo_rpi_common::app_callback_class 进行了扩展,并可根据应用程序的具体情况定制附加变量和方法。

机器人控制与仿真应用 4117

python中,循环写入

在python中,实现循环写入的功能: txt_file = open("D:/文本文档.txt", "a", encoding="utf-8") # 将w换成 a 案例: for temp in image_name: txt_file = open("D:/文本文档.txt", "a", encoding="utf-8") # 以写的格式打开先打开文件 if temp.e...

尚未佩妥剑 转眼便江湖 1万+

Magisk如何针对性隐藏Root避免被检测

Magisk隐藏特征码有两种途径,一种是通过插件,一种是定制源码。插件的方式相对简单,但是使用范围也有限,好处是成本低好上手。定制源码难度大,成本高,好处是稳定,后续也好更新维护。

zhonglunshun的专栏 1万+

微服务设计指导-让Redis循环写入时提高10倍的技巧

用了微服务即代表着对于开发的技能要求水平更高,任何可以提高100毫秒的地方都值得去做,如何让Redis遇到批量写入场景时提高8倍、10倍呢?本篇带你感受一下“叹为观止”的不同写法带来的性能、效率上的致命提升

打造全国最全的AI Agent开发知识领域的博客 1950

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

Python操作Redis的10倍效率提升技巧

最近在项目中频繁使用Redis作为缓存数据库,发现同样的功能用不同方式实现,性能差异能达到10倍以上。今天就用实际测试案例,分享几种Python操作Redis的效率优化方法。因为减少了客户端与服务端的交互,且Redis执行Lua非常高效。实测结果:耗时约1.2秒,内存波动明显。因为每次操作都有网络往返开销,连接创建销毁也很耗资源。经过这些优化,我们项目中Redis相关操作的吞吐量提升了15倍,服务器负载下降了60%。优化后耗时仅0.3秒,效率提升4倍。因为减少了网络往返次数,但连接管理仍有优化空间。

OnyxPanther23的博客 305

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

Windows开发必备:Redis性能调优10大技巧

经过多次实践和测试,我总结了10个关键技巧,帮助你在Windows环境下大幅提升Redis效率。通过slowlog-log-slower-than设置阈值(如5ms),定期分析slowlog get输出的TOP10慢查询。Windows版Redis默认无内存限制,建议通过maxmemory参数设置物理内存的70%-80%,避免系统交换导致性能下降。实际测试发现,综合应用这些技巧后,我们的订单处理系统吞吐量提升了8倍,平均延迟从120ms降到了15ms。排行榜用ZSET自带排序功能。

SilverfoxOwl19的博客 741

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

Redis 的多线程版本比 Redis 本身要快 5 倍

如果我告诉您有一个 Redis 的分支版本,它的性能比原生的 Redis 快 5 倍,而且延迟却降低近 5 倍,你会不会想了解一下这个项目?而如果您不再需要哨兵节点并且您的副本可以接受读取和写入,这将有可能使分片数量减少 10 倍,这样对你的吸引力是不是更大了呢? 我说的这个分支版本,它其实是 Redis 的一个分叉版本,名叫 KeyDB 。KeyDB是 Redis 开源的多线程分叉版本。本文...

十年代码写十年 420

为什么说Redis 的多线程版本比 Redis 本身要快 5 倍

如果我告诉您有一个 Redis 的分支版本,它的性能比原生的 Redis 快 5 倍,而且延迟却降低近 5 倍,你会不会想了解一下这个项目?而如果您不再需要哨兵节点并且您的副本可以接受读取和写入,这将有可能使分片数量减少 10 倍,这样对你的吸引力是不是更大了呢? 我说的这个分支版本,它其实是 Redis 的一个分叉版本,名叫 KeyDB 。KeyDB 是 Redis 开源的多线程分叉版本。本文我...

weixin_45793120的博客 595
上一篇: ServiceStack.Redis的源码分析
下一篇: 分析Nacos配置及源码
Java_ttcd
博客等级 码龄4年 198粉丝 190原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值