Redis JSON全量取值函数性能优化方案咨询
现有函数
public IQueryable<T> getAllPositions<T>(RedisDbs redisDbKey) { List<T> positions = new List<T>(); List<string> keys = new List<string>(); foreach (var key in _redisServer.Keys((int)redisDbKey)) { keys.Add(key.ToString()); } var sportEventRet = _redis.GetDatabase((int)redisDbKey).JsonMultiGetAsync(keys.ToArray()); foreach (var sportEvent in sportEventRet.Result) { var redisValue = (RedisValue)sportEvent; if(!redisValue.IsNull) { var positionEntity = JsonConvert.DeserializeObject<T>(redisValue, jsonSerializerSettings); positions.Add(positionEntity); } } return positions.AsQueryable(); }
调用方式
IQueryable<IPosition> union = redisClient.getAllPositions<Position>(RedisDbs.POSITIONDB);
问题背景
Position是仅包含少量简单属性的模型,RedisDbs是代表特定数据库编号的枚举。在本地高性能服务器上,该函数查询20k条Redis JSON数据耗时2秒,需优化至最长1秒、最好600毫秒以内。
可行优化方案
- 异步改造消除阻塞:将方法改为异步签名
public async Task<IQueryable<T>> getAllPositionsAsync<T>(RedisDbs redisDbKey),用await sportEventRet替代sportEventRet.Result,避免线程阻塞导致的资源浪费,提升并发效率。 - 替换Keys命令为Scan命令:Redis的
Keys命令会全量遍历数据库,数据量越大耗时越长,改用Scan分批次迭代获取键,避免阻塞Redis实例,同时降低单次内存占用。 - 预分配List容量:初始化
List<T>和List<string>时指定初始容量(比如20000),避免List因扩容频繁触发内存分配和数据拷贝,提升内存操作效率。 - 并行处理反序列化:反序列化属于CPU密集型操作,用
Parallel.ForEach处理Redis返回的结果,充分利用多核CPU资源,缩短序列化耗时。注意使用线程安全的集合或拆分任务保证线程安全。 - 替换JSON序列化库:将
Newtonsoft.Json的JsonConvert替换为System.Text.Json,后者对简单模型的序列化/反序列化性能更优,尤其在.NET Core/.NET 5+环境下。 - 分批次批量获取键值:将
Scan和JsonMultiGet结合,每批次获取1000条左右的键值,避免单次请求传递过大的键数组,减少Redis服务器处理压力和网络传输开销。 - 优化Redis连接池配置:确保连接池的最小/最大连接数设置合理,避免因连接不足导致的等待时间,同时启用连接复用,减少连接建立的开销。
- 调整数据存储结构:如果所有
Position属于同一业务集合,可改用Redis Hash结构(键为数据ID,值为JSON),或使用RedisJSON的批量查询语法,进一步提升批量读取效率。
内容的提问来源于stack exchange,提问作者user3533755
相关产品推荐
相关产品推荐

