码农知识堂 - 1000bd
  •   Python
  •   PHP
  •   JS/TS
  •   JAVA
  •   C/C++
  •   C#
  •   GO
  •   Kotlin
  •   Swift
  • RedisCluster如何高效率地批量插入数据


    RedisCluster如何高效率地批量插入数据

      • Redis环境
      • 入库方式
      • 测试代码
        • redis工具类
        • 测试接口
      • 测试结果

    Redis环境

    RedisCluster:

    三主三从,每个节点在单独的服务器上

    Spring的redisTemplate自带的Lettuce(6.2.1版本,至2022.10.22是最新版):

    spring:
      redis:
        password: xxx  #密码
        lettuce:  #lettuce连接池配置
          pool:
            max-active: -1
            max-idle: -1
            min-idle: 0
            max-wait: -1
          shutdown-timeout: 100
        cluster:  #集群配置
          nodes:
            - 1x2.xx.5.xx:6379
            - 1x2.xx.5.xx:6379
            - 1x2.xx.5.xx:6379
            - 1x2.xx.5.xx:6379
            - 1x2.xx.5.xx:6379
            - 1x2.xx.5.xx:6379
          max-redirects: 3
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    入库方式:

    使用ThreadPoolExecutor,本次测试结果为10线程,每线程插入10w条

    入库方式

    ① for循环执行set
    ② Pipline管道
    ③ multiSet()

    方式②是客户端提供的一种批处理技术,用于一次处理多个 Redis 命令,从而提高整个交互的性能,解决了多个命令集中请求时造成网络资源浪费的问题,加快了 Redis 的响应速度,让 Redis 拥有更高的运行速度,其优势在于客户端与服务端的网络延迟越大,性能体能越明显,支持设置过期时间。

    方式③是RedisTemplate的方法,速度比Pipline要快很多很多,但是不支持设置过期时间。

    方式①是最原始的方式,效率很低,不推荐,这里也不进行测试了,大家可以自行测试。

    测试代码

    redis工具类

    package com.nwd.pressuretestutil.util;
    
    import com.topscomm.pressuretestutil.config.RedisConfig;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.dao.DataAccessException;
    import org.springframework.data.redis.connection.RedisConnection;
    import org.springframework.data.redis.connection.RedisStringCommands;
    import org.springframework.data.redis.core.RedisCallback;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.data.redis.core.types.Expiration;
    import org.springframework.data.redis.serializer.RedisSerializer;
    import org.springframework.stereotype.Component;
    
    import java.util.List;
    import java.util.Map;
    
    @Component
    public class BatchRunRedisUtil {
        @Autowired
        RedisTemplate stringRedisTemplate;
    
        @Autowired
        RedisConfig redisConfig;
    
        //批量添加
        public void batchSet(Map map) {
            stringRedisTemplate.opsForValue().multiSet(map);
        }
    
        //批量添加 并且设置失效时间
        public void batchSetOrExpire(Map map, Long seconds) {
            RedisSerializer serializer = stringRedisTemplate.getStringSerializer();
            stringRedisTemplate.executePipelined(new RedisCallback() {
                @Override
                public String doInRedis(RedisConnection connection) throws DataAccessException {
                    map.forEach((key, value) -> {
                        connection.set(serializer.serialize(key), serializer.serialize(value), Expiration.seconds(seconds), RedisStringCommands.SetOption.UPSERT);
                    });
                    return null;
                }
            }, serializer);
        }
    
        //批量获取
        public List batchGet(List list) {
            List objectList = stringRedisTemplate.opsForValue().multiGet(list);
            return objectList;
        }
    
        // Redis批量Delete
        public void batchDelete(List list) {
            stringRedisTemplate.delete(list);
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56

    测试接口

    调用url:

    http://localhost:7001/redisOperation/redisBatchInsertByThread?insNum=100000&saveType=2&threadNum=10

    
    /**
      * 多线程入redis
       * insNum:每个线程的存入数量
       * saveType:插入方式,1代表multi,2代表pipline
       * */
      CountDownLatch latch = null;
      @GetMapping("/redisBatchInsertByThread")
      public String redisBatchInsertByThread(@RequestParam("insNum") Integer insNum,@RequestParam("saveType") Integer saveType,@RequestParam("threadNum") Integer threadNum){
          try {
              //定义线程池
              ThreadFactory tf = new CustomizableThreadFactory("Thread-");
              ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
                      threadNum, threadNum, 60, TimeUnit.SECONDS,
                      new LinkedBlockingDeque(100000),
                      tf);
              //线程同步计数器
              latch =  new CountDownLatch(threadNum);
              long startTime = System.currentTimeMillis();
              for (int i=0;i {
                      Integer threadCount = Integer.parseInt(Thread.currentThread().getName().split("-")[1])-1;
                      String saveT = saveType==1?"multi":"pipline";
                      //multiSet()批量操作
                      long startTime1 = System.currentTimeMillis();
                      Map map = new HashMap(insNum);
                      for (int j = 0; j < insNum; j++) {
                          map.put(saveType+"_multi"+threadCount+":" + j, "b");
                      }
                      if(saveType==1){
                          batchRunRedisUtil.batchSet(map);
                      }else {
                          batchRunRedisUtil.batchSetOrExpire(map, 60L);
                      }
                      long endTime1 = System.currentTimeMillis();
                      System.out.println(saveT+"批量set消耗" + (endTime1 - startTime1) + "毫秒");
                      latch.countDown();
                  });
              }
              long endTime = System.currentTimeMillis();
              latch.await();
              System.out.println("共用时:"+ (endTime-startTime));
              return "ok";
          }catch (Exception e){
              e.printStackTrace();
              return "false:"+e.getMessage();
          }
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    测试结果

    pipline:10线程;每条线程插入10w:

    pipline_Thread-5消耗14167毫秒
    pipline_Thread-6消耗14173毫秒
    pipline_Thread-9消耗14302毫秒
    pipline_Thread-8消耗14384毫秒
    pipline_Thread-7消耗14436毫秒
    pipline_Thread-10消耗14480毫秒
    pipline_Thread-3消耗14493毫秒
    pipline_Thread-1消耗14498毫秒
    pipline_Thread-2消耗14501毫秒
    pipline_Thread-4消耗14502毫秒
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    multi:10线程;每条线程插入10w:

    multi_Thread-1消耗5689毫秒
    multi_Thread-9消耗5918毫秒
    multi_Thread-2消耗5996毫秒
    multi_Thread-3消耗6038毫秒
    multi_Thread-6消耗6407毫秒
    multi_Thread-10消耗6899毫秒
    multi_Thread-5消耗6936毫秒
    multi_Thread-8消耗6936毫秒
    multi_Thread-7消耗6939毫秒
    multi_Thread-4消耗6967毫秒
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    由此可见,multi的效率比pipline快得多
    需要注意的是pipline在配置文件参数设置不是最优的情况下还会报连接异常
    如果业务场景无需设置超时时间,推荐使用multiSet
    如果必须要用pipline,压力也很大的话,可以考虑拆分成多个小pipline来执行。

  • 相关阅读:
    Mybatis当传入参数为0时,动态sql语句条件不生效
    vue实现大屏适配方案 (lib-flexible和postcss-px2rem-exclude)
    腾讯云MySQL数据库有哪些优势?应用于哪些场景?
    Node.js的Web后端开发调研
    卡塔尔世界杯在哪里可以看直播?
    vue 01 创建一个简单vue页面
    长期戴耳机的危害有多大?有没有不伤耳朵的蓝牙耳机?
    Docker 部署 MySQL 8
    pbootcms中使用composer
    Intel汇编-CALL调用
  • 原文地址:https://blog.csdn.net/Funky_oaNiu/article/details/127459273
    • 最新文章
    • 攻防演习之三天拿下官网站群
      数据安全治理学习——前期安全规划和安全管理体系建设
      企业安全 | 企业内一次钓鱼演练准备过程
      内网渗透测试 | Kerberos协议及其部分攻击手法
      0day的产生 | 不懂代码的"代码审计"
      安装scrcpy-client模块av模块异常,环境问题解决方案
      leetcode hot100【LeetCode 279. 完全平方数】java实现
      OpenWrt下安装Mosquitto
      AnatoMask论文汇总
      【AI日记】24.11.01 LangChain、openai api和github copilot
    • 热门文章
    • 十款代码表白小特效 一个比一个浪漫 赶紧收藏起来吧!!!
      奉劝各位学弟学妹们,该打造你的技术影响力了!
      五年了,我在 CSDN 的两个一百万。
      Java俄罗斯方块,老程序员花了一个周末,连接中学年代!
      面试官都震惊,你这网络基础可以啊!
      你真的会用百度吗?我不信 — 那些不为人知的搜索引擎语法
      心情不好的时候,用 Python 画棵樱花树送给自己吧
      通宵一晚做出来的一款类似CS的第一人称射击游戏Demo!原来做游戏也不是很难,连憨憨学妹都学会了!
      13 万字 C 语言从入门到精通保姆级教程2021 年版
      10行代码集2000张美女图,Python爬虫120例,再上征途
    Copyright © 2022 侵权请联系2656653265@qq.com    京ICP备2022015340号-1
    正则表达式工具 cron表达式工具 密码生成工具

    京公网安备 11010502049817号