0

我正在尝试实现 Mondrian SegmentCache。缓存将由运行 Mondrian 库的多个 JVM 共享。我们使用 Redis 作为后备存储,但是就这个问题而言,任何持久键值存储都应该没问题。

stackoverflow 社区会帮助完成这个实现吗?文档和 Google 搜索没有提供足够详细的信息。开始了:

new SegmentCache {

    private val logger = Logger("my-segment-cache")
    import logger._

    import com.redis.serialization.Parse
    import Parse.Implicits.parseByteArray
    private def redis = new RedisClient("localhost", 6379)

    def get(header: SegmentHeader): SegmentBody = {
        val result = redis.get[Array[Byte]](header.getUniqueID) map { bytes ⇒
            val st = new ByteArrayInputStream(bytes)
            val o = new ObjectInputStream(st)
            o.readObject.asInstanceOf[SegmentBody]
        }
        info(s"cache get\nHEADER $header\nRESULT $result")
        result.orNull
    }

    def getSegmentHeaders: util.List[SegmentHeader] = ???

    def put(header: SegmentHeader, body: SegmentBody): Boolean = {
        info(s"cache put\nHEADER $header\nBODY $body")
        val s = new ByteArrayOutputStream
        val o = new ObjectOutputStream(s)
        o.writeObject(body)
        redis.set(header.getUniqueID, s.toByteArray)
        true
    }

    def remove(header: SegmentHeader): Boolean = ???

    def tearDown() {}

    def addListener(listener: SegmentCacheListener) {}

    def removeListener(listener: SegmentCacheListener) {}

    def supportsRichIndex(): Boolean = true
}

一些直接的问题:

  • SegmentHeader.getUniqueID在缓存中使用的适当密钥吗?
  • 应该如何getSegmentHeaders实施?上面的当前实现只是抛出一个异常,蒙德里安似乎从未调用过。我们如何让 SegmentCache 在启动时重用已有的缓存记录?
  • 如何使用addListener以及如何removeListener使用?我认为它们与协调共享缓存的节点之间的缓存更改有关。但是怎么做?
  • 应该supportsRichIndex返回什么?一般来说,实现 a 的人如何SegmentCache知道要返回什么值?

我觉得这些是文档中应该涵盖的基本问题,但它们不是(据我所知)。也许我们可以纠正这里缺少可用信息的问题。谢谢!

4

1 回答 1

2

SegmentHeader.getUniqueID 是在缓存中使用的适当键吗?

是和不是。UUID 在像 memcached 这样的系统上很方便,一切都归结为键/值匹配。如果使用 UUID,则需要将 supportsRichIndex() 实现为 false。原因是排除区域不是 UUID 的一部分。这是有充分理由的设计。

我们推荐的是一种序列化 SegmentHeader 的实现(它实现了 Serializable 和 hashCode() 和 equals())并将其直接用作您传播的二进制密钥,这样它将保留无效区域并保持一切良好同步。

你应该看看我们是如何在默认内存缓存中实现它的。

还有一个使用 Hazelcast 的实现

我们 Pentaho 也使用 Infinispan 取得了巨大成功。

getSegmentHeaders 应该如何实现?

同样,看一下默认的内存实现。您只需要返回所有当前已知的 SegmentHeader 的列表。如果您出于某种原因无法提供该列表,或者因为您仅使用了 UUID,或者因为您的存储后端不支持获取列表(如 memcached),那么您将返回一个空列表。Mondrian 将无法使用内存中汇总,也无法共享这些段,除非它在缓存中命中正确的 UUID。

addListener 和 removeListener 是如何使用的?

当新元素出现在缓存中时需要通知蒙德里安。这些可以由其他节点创建。Mondrian 维护它应该知道的所有段的索引(从而启用内存操作),因此这是传播更新的一种方式。您需要在此处将后端与 Mondrian 实例连接起来。看看Hazelcast 实现是如何做到的

这背后的想法是,蒙德里安维护当前已知单元格的空间索引,并且只有在绝对需要时才会从 SQL 中查询必要/缺失的单元格。这对于实现更大的可扩展性是必要的。与我们在内存数据网格中维护的对象相比,从 SQL 中获取单元格非常慢。

我们如何让 SegmentCache 在启动时重用现有的缓存记录

这是一个警告。目前,这可以通过应用此补丁来实现。它没有被移植到主代码线,因为它是一团糟,并且与另一个案例的修复程序纠缠在一起。据报道它可以工作,但我们没有在内部进行测试。相关代码在这里。如果您有时间对此进行测试,我们始终欢迎您的贡献。如果您对邮件列表感兴趣,请告诉我们。有很多人会很乐意提供帮助。

一种解决方法是在缓存实现开始时通过侦听器更新本地索引。

于 2013-07-09T02:08:09.180 回答