Pipeline才是批量添加数据的正确方式

725 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第3天,点击查看活动详情

我们在使用redis的时候,有时会遇到批量导入数据的情况,比如向set或者List当中。相信操作过得同学会发现一个严重的问题,那就是性能非常低,相同数据量,比插入数据库还要慢太多太多。

那么如何解决上面的问题?redis其实本来就有解决方案,那就是使用Pipeline进行导入。

目前在java项目基本都是基于springboot构建的,所以我直接使用RedisTemplate进行操作。如常用的jedis也有对应的操作方式,本文不介绍了。

引入redis依赖

如果已经引入就不需要了,这个是使用redis的依赖,不是专门针对pipeline的。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

常用添加数据操作

此处以添加数据到Set为例。我们通常可以使用如下的方式进行set数据添加:

/**
 * 缓存Set
 *
 * @param key     缓存键值
 * @param dataSet 缓存的数据
 * @return 缓存数据的对象
 */
public <T> BoundSetOperations<String, T> setSet(String key, Set<T> dataSet) {
    // 获取操作对象
    BoundSetOperations<String, T> setOperations = this.redisTemplate.boundSetOps(key);
    // 遍历参数Set
    Iterator<T> iterable = dataSet.iterator();
    while (iterable.hasNext()) {
        // 添加数据
        setOperations.add(iterable.next());
    }
    return setOperations;
}

如此操作性能非常低,随着数据量不断增加,消耗时间就越长。

使用pipeline方案

下面我们通过使用pipeline来进行数据的添加:

/**
 * 缓存Set,使用pipeline
 *
 * @param key     缓存键值
 * @param dataSet 缓存的数据
 * @return 缓存数据的对象
 */
public void setSetPipeline(String key, Set<Map<String, String>> dataSet) {
    // 对redis的key和value进行序列化
    RedisSerializer keySerializer = redisTemplate.getKeySerializer();
    RedisSerializer valueSerializer = redisTemplate.getValueSerializer();
    // 启用pipeline
    this.redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
        connection.openPipeline();
        // 设置key、value
        connection.set(keySerializer.serialize(key), valueSerializer.serialize(dataSet));
        return null;
    });
}

如上所示,我们需要对key和value进行序列化操作,我们简单看下connection.set方法

Boolean set(byte[] key, byte[] value);

参数是两个byte[],有一些文章推荐我们将key和value直接转成字符串进行getBytes,我不否认这种做法,但并不是能保证成功,会抛出异常,比如我就抛出了异常,之后才修改成如上的方式。所以建议同学们直接一步到位,不要做无谓的时间浪费。

总结

修改过后,操作时间从原本的几十秒,变成了目前的1秒左右,我们的数据只有3000多条。性能提升相当明显,pipeline是redis提供给我们非常有用的操作方式,后续的文章中,会详细讲解关于pipeline的内容。