如何在Redis中执行指定LINQ逻辑以优化查询性能?
可行优化方案
1. 用Lua脚本在Redis端直接执行逻辑
Lua脚本可在Redis服务器内运行,彻底避免全量拉取数据到客户端的开销,是当前场景下最直接的解决方案。核心步骤如下:
- 通过
SCAN命令分批获取所有RedisJSON键(避免一次性加载20k键造成服务器阻塞) - 对每个键,用
JSON.GET读取所需字段(EventKey,BetCategory,CustomParticipant,CustomLine,BetPosition,OddsDecimal,PositionId,BetData) - 在Lua内按复合键分组,将同一组的Position数据存入临时表
- 遍历每个分组,对组内Position两两配对,检查
t1.BetPosition != t2.BetPosition和(1/t1.OddsDecimal + 1/t2.OddsDecimal) < 0.985的条件 - 收集符合条件的结果,一次性返回给客户端
示例Lua核心逻辑(简化版):
local cursor = "0" local groups = {} repeat local result = redis.call("SCAN", cursor, "MATCH", "*", "COUNT", 1000) -- 分批扫描键,COUNT值可根据Redis性能调整 cursor = result[1] local keys = result[2] for _, key in ipairs(keys) do local json = redis.call("JSON.GET", key) local data = cjson.decode(json) -- 构建复合键作为分组标识 local compositeKey = data.EventKey .. "|" .. data.BetCategory .. "|" .. data.CustomParticipant .. "|" .. data.CustomLine -- 初始化分组并加入数据 if not groups[compositeKey] then groups[compositeKey] = {} end table.insert(groups[compositeKey], { PositionId = data.PositionId, OddsDecimal = data.OddsDecimal, BetData = data.BetData, BetPosition = data.BetPosition, BetCategory = data.BetCategory }) end until cursor == "0" -- 遍历分组生成符合条件的结果 local finalResult = {} for _, group in pairs(groups) do local len = #group if len >= 2 then -- 组内两两配对(避免重复检查i<j) for i=1, len do for j=i+1, len do local t1 = group[i] local t2 = group[j] if t1.BetPosition ~= t2.BetPosition then local sum = 1/t1.OddsDecimal + 1/t2.OddsDecimal if sum < 0.985 then table.insert(finalResult, { PositionId1 = t1.PositionId, OddsDecimal1 = t1.OddsDecimal, BetData1 = t1.BetData, PositionId2 = t2.PositionId, OddsDecimal2 = t2.OddsDecimal, BetData2 = t2.BetData, BetCategory = t1.BetCategory }) end end end end end end return cjson.encode(finalResult)
C#调用示例:
var script = File.ReadAllText(@"path/to/your/script.lua"); var result = await _redis.GetDatabase(1).ScriptEvaluateAsync(script); var finalList = JsonSerializer.Deserialize<List<YourResultType>>(result.ToString());
2. 预构建分组索引,降低Lua遍历开销
如果全量SCAN键仍有性能压力,可提前维护分组索引优化流程:
- 每次写入/更新RedisJSON数据时,将对应Position ID添加到以复合键命名的Redis Set中,例如键名格式为
group:{EventKey}:{BetCategory}:{CustomParticipant}:{CustomLine} - Lua脚本优先扫描所有分组键(
SCAN MATCH group:*),再通过SMEMBERS获取分组内的Position ID列表 - 用
JSON.MGET批量读取这些Position的字段,后续配对逻辑与上述Lua脚本一致
这种方式减少了全量扫描数据键的开销,适合数据更新频率较低的场景。
3. 改用RedisGraph实现关系查询
RedisGraph是Redis的图数据库模块,支持Cypher查询语言,可简洁处理这类分组配对逻辑:
- 将每个Position作为图节点,节点属性包含所有所需字段
- 执行Cypher查询直接筛选符合条件的节点对:
MATCH (p1:Position), (p2:Position) WHERE p1.EventKey = p2.EventKey AND p1.BetCategory = p2.BetCategory AND p1.CustomParticipant = p2.CustomParticipant AND p1.CustomLine = p2.CustomLine AND p1.BetPosition <> p2.BetPosition AND (1/p1.OddsDecimal + 1/p2.OddsDecimal) < 0.985 RETURN p1.PositionId AS PositionId1, p1.OddsDecimal AS OddsDecimal1, p1.BetData AS BetData1, p2.PositionId AS PositionId2, p2.OddsDecimal AS OddsDecimal2, p2.BetData AS BetData2, p1.BetCategory AS BetCategory
可通过C#的RedisGraph客户端库(如StackExchange.Redis.Extensions.RedisGraph)执行查询,直接获取结果,无需拉取全量数据。
方案选型建议
- Lua脚本:无需额外安装模块,适配现有RedisJSON环境,处理20k数据的性能可轻松控制在600ms以内
- 预构建索引:进一步降低Lua脚本的执行耗时,适合数据更新不频繁的场景
- RedisGraph:语法最简洁,长期维护成本低,需安装RedisGraph模块,但查询性能最优
内容的提问来源于stack exchange,提问作者user3533755
相关产品推荐
相关产品推荐

