redis之批量操作 pipeline重写

目录

 

起因 

redis批量操作

批量操作产生的问题

解决办法

2019年11月5日更新(使用pipeline改写调用redis)

2019年12月11日更新(pipeline调用产生的问题)

 

 


 

起因 

最近在测试环境上发现了一个比较慢的链路调用,如下图所示:

该操作做了大量的hgetall,发现入参中的List参数高达2700个,导致这里操作redis的时候一直循环查询,

因为这个接口在设计的时候没有料想到有如此巨大的数据入口(所以没有限制入口参数列表长度,也没有优化循环操作),

导致该调用链直接耗时16s之久;经过调查,决定重写redis查询接口;

 

redis批量操作

查阅资料得知,redis有批量操作,org.springframework.data.redis.core.ValueOperations#multiGet

写自测例子如下:

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @GetMapping("/combine")
    @ApiOperation(value = "组合服务测试")
    public R<?> combine() {

        Map<String, String> map = IntStream.rangeClosed(1, 100).boxed()
                .collect(Collectors.toList()).stream()
                .collect(Collectors.toMap(k -> "test-app-" + k, v -> "" + v));
        redisTemplate.opsForValue().multiSet(map);
        List<String> collect = IntStream.rangeClosed(1, 100).boxed().map(p -> "test-app-" + p).collect(Collectors.toList());
        List<String> list = stringRedisTemplate.opsForValue().multiGet(collect);
        return R.success(list);
    }

可以成功返回,

拦截请求如下:

 

StringRedisTemplate 有multiget方法
1. 会把所有key所在的集群槽位分组
2. 每个槽位取一个redis连接
3. 没有开启pipeline, 从上个抓包图可以看出来, 每个请求都有回应

 

批量操作产生的问题

但是这里存在一个问题:只能操作String类型的数据,其他数据均会返回null

这点从官网可以看出(其实最后都是用的MGET命令)

 

	@Override
	public List<V> multiGet(Collection<K> keys) {

		if (keys.isEmpty()) {
			return Collections.emptyList();
		}

		byte[][] rawKeys = new byte[keys.size()][];

		int counter = 0;
		for (K hashKey : keys) {
			rawKeys[counter++] = rawKey(hashKey);
		}

		List<byte[]> rawValues = execute(connection -> connection.mGet(rawKeys), true);

		return deserializeValues(rawValues);
	}




	/**
	 * Get multiple {@code keys}. Values are returned in the order of the requested keys.
	 *
	 * @param keys must not be {@literal null}.
	 * @return empty {@link List} if keys do not exist or when used in pipeline / transaction.
	 * @see <a href="http://redis.io/commands/mget">Redis Documentation: MGET</a>
	 */
	@Nullable
	List<byte[]> mGet(byte[]... keys);

 

解决办法

如果要使用其他类型的批量数据操作,需要使用pipeline操作,所谓的pipeline操作其实就是

pipeline通过减少客户端与redis的通信次数来实现降低往返延时时间,而且Pipeline 实现的原理是队列,而队列的原理是时先进先出,这样就保证数据的顺序性。

主要思想就是尽可能的减少网络传输的时间损耗,以提高性能。

下面给出一篇文章具体详解redis的pipeline操作:分布式缓存Redis之Pipeline(管道)

具体实现接下来详解补充(待续)。。。

 


2019年11月5日更新(使用pipeline改写调用redis)

修改為pipeline方式獲取數據

        List<String> stringList = new ArrayList<String>() {
            {
                add("xxx");
                add("xxx");
            }
        };


        List<Object> List = stringStringRedisTemplate.executePipelined((RedisCallback<Object>) redisConnection -> {
            stringList.forEach(string->{
                redisConnection.hVals(string.getBytes());
            });
            return null;
        });

这里是不需要openconnect和close的,看最后调用的redistemplate的方法org.springframework.data.redis.core.RedisTemplate#executePipelined(org.springframework.data.redis.core.RedisCallback<?>, org.springframework.data.redis.serializer.RedisSerializer<?>)

    public List<Object> executePipelined(RedisCallback<?> action, @Nullable RedisSerializer<?> resultSerializer) {
        return (List)this.execute((connection) -> {
            connection.openPipeline();
            boolean pipelinedClosed = false;

            List var7;
            try {
                Object result = action.doInRedis(connection);
                if (result != null) {
                    throw new InvalidDataAccessApiUsageException("Callback cannot return a non-null value as it gets overwritten by the pipeline");
                }

                List<Object> closePipeline = connection.closePipeline();
                pipelinedClosed = true;
                var7 = this.deserializeMixedResults(closePipeline, resultSerializer, this.hashKeySerializer, this.hashValueSerializer);
            } finally {
                if (!pipelinedClosed) {
                    connection.closePipeline();
                }

            }

            return var7;
        });
    }

這裏是獲取hash的value的方式,其他方式類似。

完结撒花...

 

2019年12月11日更新(pipeline调用产生的问题)

 

更改了pipeline方式后,速度确实上去了不少,但是根据监控链路的查看,发现有少量的调用链路出现了如下图问题:

 

目前暂未找到解决方法,未发现会产生什么负面影响

这里有个最相近的,并未给出解决方法:

https://github.com/lettuce-io/lettuce-core/issues/971

https://github.com/lettuce-io/lettuce-core/issues/1027

https://github.com/reactor/reactor-netty/issues/564

可能是因为生菜的问题吧。。。感觉更像是netty的问题,,,

我太南了

 

2020-1-7 更新 ........解决办法

https://blog.csdn.net/dongying1751/article/details/103812175

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值