Hazelcast 5中高频查询MultiMap的可行方案咨询
我之前在项目里也碰到过几乎一模一样的问题——从IMap转用MultiMap存集合类型数据后,发现没有现成的getAll和QueryCache支持,高频查询下网络往返的开销瞬间上来了,后来试了几种 workaround,都能解决问题,给你参考:
手动实现批量获取逻辑
虽然MultiMap没有原生getAll,但可以自己基于getAsync来做批量异步获取,这样能减少同步等待的时间。比如把要查询的key列表转换成多个CompletableFuture,然后用CompletableFuture.allOf()来等待所有结果完成,最后合并每个key对应的集合。举个简单的代码片段:Collection<CompletableFuture<Collection<V>>> futures = keys.stream() .map(key -> multiMap.getAsync(key)) .collect(Collectors.toList()); CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join(); // 遍历futures获取结果并合并这种方式比循环同步
get要高效很多,能把多个网络请求的等待时间并行化。在应用层加本地缓存兜底
针对高频查询的key,在应用服务本地加一层Caffeine或者Guava Cache,缓存MultiMap的查询结果。同时要监听MultiMap的EntryListener,当MultiMap里的条目新增、更新或者删除时,同步更新本地缓存的对应数据,避免缓存不一致。这种方案能把大部分查询请求留在本地,直接砍掉网络往返的开销,适合读多写少的场景。回退到IMap自定义集合容器
要是上面的方案都觉得麻烦,其实可以退回去用IMap,但自己维护值为集合的结构——比如把IMap的value设为ConcurrentHashMap或者CopyOnWriteArraySet这种线程安全的集合。这样就能复用IMap原生的getAll和QueryCache能力了。不过要注意修改集合内容时,必须用Hazelcast的EntryProcessor来操作,不能直接在本地修改集合对象(因为IMap的value是序列化后存在集群的,本地修改不会同步到集群),比如:imap.executeOnKey(key, entry -> { Collection<V> values = entry.getValue(); values.add(newValue); entry.setValue(values); return null; });这种方案相当于用IMap的原生能力来模拟MultiMap的行为,还能享受到IMap的所有查询优化。
考虑使用ReplicatedMap(适合数据量小的场景)
如果你的MultiMap数据量不大,读远多于写,可以换成ReplicatedMap——它会把数据复制到集群的每个节点上,查询时直接读本地副本,完全没有网络开销。不过要注意写操作的性能,因为每个写请求都要同步到所有节点,所以数据量大或者写频繁的场景不适合用这个。
这些方案我当时根据不同的场景试过,其中应用层本地缓存+MultiMap的组合,还有回退IMap的方案用得最多,你可以根据自己的读写比例、数据量大小来选。
备注:内容来源于stack exchange,提问作者DemonFace

