一次业务中关于批量读取redis的技术总结

该博客讲述了在不支持管道查询的Redis集群环境下,使用线程池批量获取视频数据的方法。通过CountDownLatch实现线程同步,并在ArrayList、CopyOnWriteArrayList、ConcurrentLinkedQueue和Collections.synchronizedList中选择了Collections.synchronizedList作为线程安全的List,以保证并发安全性。在性能测试后,选择了Collections.synchronizedList,因为它在转化成List时更为方便。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、业务背景

根据视频docId批量去redis中查询视频的各种数,由于redis使用的是集群模式,不支持管道查询(或者使用redis管道比较麻烦)。故在此使用了线程池去查询。

private ExecutorService executorService = new ThreadPoolExecutor(10, 20, 5, TimeUnit.SECONDS,
        new ArrayBlockingQueue<>(2000, true),
        new ThreadFactoryBuilder().setNameFormat("videoCountThread-%d").build(),
        new ThreadPoolExecutor.AbortPolicy());

public List<FeedbackInfo> getVideoCount(List<String> docIdList) {
    
    final CountDownLatch countDownLatch = new CountDownLatch(docIdList.size());
    List<FeedbackInfo> countList = Collections.synchronizedList(new ArrayList<>(docIdList.size()));
    
    try {
        for (String docId : docIdList) {
            executorService.submit(() -> {
                try {
                    // 此处省略.....
					countList.add(feedbackInfo);
                } catch (Exception e) {
                    LOGGER.error("getVideoCount error. param: docId = {}", docId, e);
                } finally {
                    countDownLatch.countDown();
                }
            });
        }
        countDownLatch.await();
        LOGGER.info("批量执行结束 countList size: {}, docIdList size: {} ", countList.size(), docIdList.size());
    } catch (Exception e) {
        LOGGER.error("getVideoCount error. param: docIdList = ", e);
    }
    return countList;
}

二、两个核心问题

1. 线程获取数据后如何同步返回

这里使用了CountDownLatch来完成同步

2. 线程并发List的选择

有以下List可以供选择:ArrayList、CopyOnWriteArrayList、ConcurrentLinkedQueue、Collections.synchronizedList。

其中,ArrayList不是线程安全的,CopyOnWriteArrayList的写操作效率太低,所以在ConcurrentLinkedQueue、Collections.synchronizedList两个中进行选择。

经过一些测试,这两个并发安全的List在本业务场景中效率差不多,考虑到使用ConcurrentLinkedQueue在最后异步需要转化为List,所以最终选用了Collections.synchronizedList。

### 如何通过 Java 读取 Redis 数据 以下是几种常见的方法来实现从 Redis读取数据的示例: #### 方法一:使用 Spring Data Redis 如果项目中集成了 Spring 框架,可以借助 `Spring Data Redis` 提供的功能轻松完成 Redis 数据的操作。 ```java import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Service; @Service public class RedisService { @Autowired private StringRedisTemplate redisTemplate; public String getDataFromRedis(String key) { return redisTemplate.opsForValue().get(key); } } ``` 上述代码展示了如何定义一个服务类并注入 `StringRedisTemplate` 对象[^1]。调用 `opsForValue().get()` 可以获取指定键对应的值。 --- #### 方法二:使用 Jedis 客户端库 Jedis 是一种轻量级的 Redis 驱动程序,适合不依赖于 Spring 的场景下直接访问 Redis。 ```java import redis.clients.jedis.Jedis; public class RedisClientExample { public static void main(String[] args) { try (Jedis jedis = new Jedis("localhost")) { // 连接本地 Redis 实例 String value = jedis.get("king"); System.out.println("Data retrieved from Redis: " + value); } catch (Exception e) { e.printStackTrace(); } } } ``` 此代码片段演示了创建 Jedis 实例并与 Redis 建立连接的过程。随后通过调用 `jedis.get()` 获取存储在 `"king"` 键中的值[^2]。 --- #### 方法三:基于 Reactor 和 Spring Data Reactive Redis 对于需要支持高并发和非阻塞 I/O 场景的应用,推荐采用反应式编程模型下的解决方案——即结合 `Spring Data Reactive Redis` 使用。 ```java import reactor.core.publisher.Mono; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.connection.ReactiveRedisConnection; import org.springframework.data.redis.core.ReactiveRedisTemplate; import org.springframework.stereotype.Service; @Service public class ReactiveRedisService { @Autowired private ReactiveRedisTemplate<String, String> reactiveRedisTemplate; public Mono<String> getReactiveData(String key) { return this.reactiveRedisTemplate.opsForValue() .get(key) .doOnNext(value -> System.out.println("Retrieved Value: " + value)); } } ``` 这里引入了 `Mono` 类型作为返回值容器,用于表示单个异步计算的结果。当执行查询时,会自动触发回调函数打印日志信息[^3]。 --- #### 注意事项 - **线程安全性**:无论是哪种方式,在多线程环境下都需要注意资源竞争问题。 - **异常捕获机制**:实际生产环境中应增加更完善的错误处理逻辑以防止单点失败影响整体业务流程。 - **性能优化建议**:合理设置超时参数以及批量操作策略有助于提升效率减少网络延迟带来的开销。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值