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

如何高效从Apache Ignite缓存仅读取键且不加载全量数据集

Apache Ignite 仅读取缓存键的最优实现(大数据集场景)

问题根因

你遇到的性能问题、运行失败问题核心原因有两个:

  • 默认ScanQuery会同时把缓存条目的键和值全部序列化后通过网络传输到厚客户端,哪怕你只需要读取键,值对象的序列化、网络传输、反序列化会产生90%以上的无意义开销,数据集越大这部分开销越明显
  • 调用.ToList()会一次性把所有查询结果全量加载到客户端内存,数据集超过客户端堆内存阈值时直接OOM失败
    你后续尝试的游标遍历实现虽然解决了一次性全量加载的问题,但没有关闭值的返回,所以依然存在大量无效传输,耗时很长。

最优实现方案

方案1:配置ScanQuery仅返回键(90%场景下的首选,改造成本最低)

ScanQuery提供了IncludeValue配置项,默认值为true,将其设为false后,服务端节点不会序列化、传输值对象,仅返回键,性能可以提升数倍到数十倍。
配合游标流式读取,避免一次性加载全量键到内存,代码示例:

// 注意泛型参数要和缓存实际键值类型匹配,你之前代码里键是int就不要用string
var cache = ignite.GetOrCreateCache<int, IBinaryObject>("my-cache").WithKeepBinary<int, IBinaryObject>();

var keys = new List<int>();
// 核心配置:IncludeValue = false,不返回值对象
var query = new ScanQuery<int, IBinaryObject>
{
    IncludeValue = false
};

// 游标流式拉取,不会一次性加载全量结果到内存
using (var cursor = cache.Query(query))
{
    foreach (var entry in cursor)
    {
        // entry.Value此时为默认值,服务端根本没有传输值,没有额外开销
        keys.Add(entry.Key);
        
        // 如果键总量超千万级,建议在这里直接处理键,不要全部攒到List中,避免客户端OOM
    }
}

该方案的优势:

  • 改造成本极低,仅需加一行配置
  • 网络传输量仅为原来的几十分之一(取决于值对象的大小)
  • 客户端不需要做值的反序列化,CPU开销大幅降低
  • 流式拉取对客户端内存要求低,只要处理逻辑跟得上,亿级键量也可以稳定运行

方案2:服务端Compute执行(超大规模数据集首选,性能最高)

如果你的数据集规模特别大(单缓存键量过亿),不需要把所有键拉到客户端处理,可以直接通过Ignite Compute将任务派发到所有服务端节点,每个节点仅遍历本地存储的键,处理完成后只返回汇总结果,完全不需要跨节点传输全量键:

// 把任务广播到所有服务端节点,每个节点处理本地分片的键
var nodeLocalKeyResults = ignite.GetCompute().Broadcast(new GetLocalCacheKeysTask(), "my-cache");

// 汇总所有节点返回的结果即可
var allKeys = nodeLocalKeyResults.SelectMany(r => r).ToList();

服务端执行的任务定义:

private class GetLocalCacheKeysTask : IComputeFunc<string, List<int>>
{
    public List<int> Invoke(string cacheName)
    {
        var ignite = Ignition.GetIgnite();
        var localCache = ignite.GetCache<int, IBinaryObject>(cacheName)
            .WithKeepBinary<int, IBinaryObject>();
        
        var localKeys = new List<int>();
        // Local=true表示仅遍历当前节点本地存储的缓存数据,无网络开销
        var query = new ScanQuery<int, IBinaryObject>
        {
            IncludeValue = false,
            Local = true
        };

        using var cursor = localCache.Query(query);
        foreach (var entry in cursor)
        {
            localKeys.Add(entry.Key);
            // 可以直接在服务端做键的过滤、统计,不需要传回客户端
        }

        return localKeys;
    }
}

原有代码问题说明

  • 第一版实现调用.ToList():会把所有包含完整值的缓存条目一次性加载到客户端内存,大数据集下必然OOM,且等待时间极长
  • 第二版游标实现:虽然避免了一次性全量加载,但没有设置IncludeValue = false,值对象依然会被全量传输、反序列化,所以耗时没有本质下降
  • 代码中存在键类型不匹配问题:缓存定义的键是int类型,ScanQuery却用string作为键泛型参数,会导致类型转换错误,额外增加开销

额外优化建议

  • 如果键是基础值类型(int/long/string/Guid),不需要额外配置WithKeepBinary也能正常读取键,不会产生额外开销
  • 键量级特别大时不要把所有键都存入客户端集合,边遍历边处理,处理完及时释放内存
  • 遍历游标时不要执行长时间阻塞操作,避免长时间占用集群连接资源

内容的提问来源于stack exchange,提问作者Justin Mathew

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 04:51:25