Java 操作 Redis 技术怎么选?Jedis 真的过时了吗?

背景

某项目引入了一个新需求,需要根据某数据表的数据进行数据处理,这个数据存储在哪里呢?内存 VS 缓存组件呢?权衡之下选择使用 Redis 缓存。

调研 Java 操作 Redis 的客户端时,主要有 Jedis 和 lettuce 两种,而且各种资料显示后者比前者应用范围更广,效率更高,且是 spring-boot 生态默认支持的。

最开始我也是直接调研 lettuce 的用法的,部署测试时发现引用的 lettuce 依赖的 netty 版本和已有的 netty 版本不匹配,又继续调研 Jedis 。

lettuce 调研

lettuce 最新版本6.5.5 依赖的 netty 是4.1.65,我们那个老项目应用的 netty-all ,最后一次更新是在10年前,版本太低了,兼容不了 lettuce ,其中一些新版本的方法都没有,运行时会报异常。

Jedis 调研

Jedis 倒是一直在更新,依赖的包比较少,项目中都有,而且 json 这个包即使没有,API 也不会报错,可见它不是必须的。 此外,它依赖了连接池包 commons-pool2,注意,低版本的 jedis-2.9.0 依赖的连接池版本也低,高版本的连接池的构造函数语法有变动,需要匹配对应版本。

基于Jedis 的 Redis 操作

Redis 支持三种部署方式:单机、集群和哨兵。那么我们在编写 Redis 操作的时候就要考虑三种类型的客户端,通用的方法是使用连接池。这里可以简单实用工厂模式来实现 Redis 客户端对象的获取,定义顶层抽象类,包含公共的 Redis 的操作方法:

  1. ping()
  2. get(String key)
  3. set(String key, Object value)
  4. setBatch(List<Map> data)
  5. del(String key)

子类实现按三类部署方式对应的客户端API来处理。

第一类,单机连接池和批处理操作:

c 复制代码
// Part1:创建连接池对象
JedisPool jedisPool = new JedisPool(config, url, port, timeout, password, databaseIndex)

// Part2:批量写入,使用 Pipeline 对象
public void setBatch(Map<String, String> datas) {
    try (Jedis jedis = jedisPool.getResource()) {
        Pipeline pipelined = jedis.pipelined();
        datas.forEach((key, value) ->{
           pipelined.set(key, value);
        });
        
        // 一批次刷新
        pipelined.sync();

        // 必须关闭否则会资源泄漏
        pipelined.close();
    } catch (Exception e) {
        logger.error("Redis set data error", e);
    }
}

第二类,集群连接池和批处理操作:

c 复制代码
// Part1:创建连接池对象
Set<HostAndPort> jedisClusterNodes = new HashSet<>();
// TODO 添加集群节点到 jedisClusterNodes

// 创建集群连接池对象
JedisCluster jedisCluster = new JedisCluster(jedisClusterNodes,
                connectTimeout,
                timeout,
                maxRetryCount,
                password,
                poolConfig);

// Part2:批量写入,使用 Pipeline 对象  
public void setBatch(Map<String, String> datas) {
    try (ClusterPipeline pipelined = jedisCluster.pipelined()) {
        datas.forEach((key, value) ->{
           pipelined.set(key, value);
        });
        
        // 一批次刷新
        pipelined.sync();
    } catch (Exception e) {
        logger.error("Redis set data error", e);
    }
}           

第三类,哨兵连接池操作和批处理操作:

c 复制代码
// Part1:创建连接池对象
Set<String> sentinels = new HashSet<>(Arrays.asList(urls.split(",")));
JedisSentinelPool jedisPool = new JedisSentinelPool(masterName,     sentinels,
                poolConfig,
                timeout,
                password,
                databaseIndex);

// Part2:批量写入,使用 Pipeline 对象
public void setBatch(Map<String, String> datas) {
    try (Jedis jedis = jedisPool.getResource()) {
        Pipeline pipelined = jedis.pipelined();
        datas.forEach((key, value) ->{
           pipelined.set(key, value);
        });
        
        // 一批次刷新
        pipelined.sync();

        // 必须关闭否则会资源泄漏
        pipelined.close();
    } catch (Exception e) {
        logger.error("Redis set data error", e);
    }
}

启示录

解决这个需求后,我想到一个问题技术框架也有高低优劣吗?Jedis 这个短小精悍的工具包,而且一直在保持更新,依赖少,新版本提供了 Pipeline 这个批处理的方法,可以将一批操作封装在一起一次提交,效率非常高。

我测试了50万条数据写入 Redis,用 Pipeline 后可以在5秒内完成,真正能达到10万每秒。但是如果单独一条条发送,新版本「5.2.0」的效率反而低于低版本「2.9.0」,集群效率又低于单机效率,高版本的单机在150秒左右,高版本的集群在 600 秒左右。

版本更迭,依赖变化,保持最小依赖不容易啊!

相关推荐
子兮曰3 天前
jev-ultrafast 深度解析:7 秒订机票的浏览器 Agent 是如何炼成的
前端·后端·agent
子兮曰3 天前
Jev 爆发一周:7 秒 Agent 背后的 System One 生态与三场争议
前端·后端·ai编程
爱勇宝3 天前
ZCode 开源 24 小时:一份没有历史的账本,回答不了"有没有偷代码"
前端·后端·chatglm (智谱)
胡写代码3 天前
别再前后端各写一套表单校验了
java·后端
大勇前进3 天前
原生 PHP 还是 Laravel?小项目到底要不要上框架
后端
yuzhi_liu3 天前
我用 LangGraph4j 实现 Multi-Agent Supervisor
后端
alsmile3 天前
Node-RED 之外,国产规则引擎的新方案:基于标准语法,Go 先行实现
后端·开源·go
大白803 天前
PHP 内存溢出排查思路:看懂报错日志,精准定位问题
后端
二月龙3 天前
PHP 接口返回统一响应封装,让前后端对接更省心
后端
盖伦发发3 天前
软件工程SOLID 五大设计原则
后端·软件工程