You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用CrudRepository.saveAll()批量保存百万Redis记录耗时过长问题

优化Spring Data Redis批量插入百万数据的性能

问题根源

Spring Data Redis的CrudRepository.saveAll默认会逐条执行数据插入+索引维护操作,百万条数据会产生百万次独立的网络请求,这是导致耗时近4分钟的核心原因。以下是针对性的优化方案:


1. 改用Redis Pipeline批量操作

通过Redis流水线(Pipeline)将多个请求打包发送,大幅减少网络往返次数:

@Autowired
private RedisTemplate<String, Object> redisTemplate;

public void batchSave(List<ActionEventTypesModel> models) {
    redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
        RedisSerializer<String> stringSerializer = redisTemplate.getStringSerializer();
        RedisSerializer<Object> valueSerializer = redisTemplate.getValueSerializer();
        
        for (ActionEventTypesModel model : models) {
            // 生成Redis Hash的key:ActionEventTypes:{actionEventId}
            String key = "ActionEventTypes:" + model.getActionEventId().toString();
            // 组装Hash结构数据
            Map<byte[], byte[]> hash = new HashMap<>();
            hash.put(stringSerializer.serialize("actionEventId"), valueSerializer.serialize(model.getActionEventId()));
            hash.put(stringSerializer.serialize("actionEventTypeCd"), valueSerializer.serialize(model.getActionEventTypeCd()));
            hash.put(stringSerializer.serialize("actionEventTypeDesc"), valueSerializer.serialize(model.getActionEventTypeDesc()));
            
            connection.hMSet(stringSerializer.serialize(key), hash);
            
            // 手动维护索引(替代@Indexed的自动操作)
            String indexKey = "ActionEventTypes:actionEventTypeCd:" + model.getActionEventTypeCd();
            connection.sAdd(stringSerializer.serialize(indexKey), stringSerializer.serialize(model.getActionEventId().toString()));
        }
        return null;
    });
}

2. 临时禁用自动索引,批量插入后统一重建

@Indexed注解会在每条数据插入时自动维护集合索引,额外增加开销。可以先移除实体类的@Indexed,批量插入完成后再手动构建索引:

// 批量插入完成后统一构建索引
public void buildEventTypeIndex(List<ActionEventTypesModel> models) {
    redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
        RedisSerializer<String> stringSerializer = redisTemplate.getStringSerializer();
        
        for (ActionEventTypesModel model : models) {
            String indexKey = "ActionEventTypes:actionEventTypeCd:" + model.getActionEventTypeCd();
            connection.sAdd(stringSerializer.serialize(indexKey), stringSerializer.serialize(model.getActionEventId().toString()));
        }
        return null;
    });
}

3. 分批次插入,避免内存溢出

一次性加载百万条数据到内存可能引发OOM,建议分批次处理,比如每1000条为一批:

public void batchSaveInChunks(List<ActionEventTypesModel> allModels) {
    int chunkSize = 1000;
    for (int i = 0; i < allModels.size(); i += chunkSize) {
        int end = Math.min(i + chunkSize, allModels.size());
        List<ActionEventTypesModel> chunk = allModels.subList(i, end);
        batchSave(chunk); // 调用批量保存方法
    }
}

4. 优化Redis连接池配置

调整连接池参数提升并发处理能力,在application.properties中配置:

# Redis连接池配置
spring.redis.jedis.pool.max-active=50
spring.redis.jedis.pool.max-idle=20
spring.redis.jedis.pool.min-idle=5
spring.redis.timeout=2000ms

额外优化建议

  • 避免使用Double作为ID类型,Redis中字符串/长整型ID的处理效率更高,建议将actionEventId改为Long或String。
  • 若Redis部署在远程服务器,尽量保证应用与Redis同机房部署,降低网络延迟。

内容的提问来源于stack exchange,提问作者Noman Ahmed

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 01:03:10