1

对于任何感兴趣的人:我已经为我正在寻找的行为实现了代码,并在 google-code 上开源了它。在这里得到它!pojo-mvcc

--

嗨,大家好,

我正在尝试编写一个框架,其中包含许多从长期缓存创建的短期缓存。这些短期缓存需要能够返回它们的完整内容,这是原始长期缓存的克隆。

实际上,我正在尝试构建的是短期缓存的事务隔离级别。用户应该能够修改短期缓存的内容,但不应该传播对长期缓存的更改(根据缓存类型,也存在应该推送更改的情况)。

我会尽力解释:

主缓存包含:[A,B,C,D,E,F] 使用状态 [A,B,C,D,E,F] 创建的临时缓存

1) 临时缓存添加项目 G: [A,B,C,D,E,F] 2) 临时缓存删除项目 B: [A,C,D,E,F]

主缓存包含:[A,B,C,D,E,F]

3) master-cache 添加项 [X,Y,Z]: [A,B,C,D,E,F,X,Y,Z]

临时缓存包含:[A,C,D,E,F]

当项目中的值可以更改并且不应该总是更新时,事情变得更加困难(所以我什至不能共享底层对象实例,我需要使用克隆)。

我已经实现了一种简单的方法,即使用 ArrayList 上的标准 Collection 构造函数创建一个新的 List 实例,但是当您获得大约 200,000 个项目时,系统就会耗尽内存。我知道 200,000 的值对于迭代来说太过分了,但我想稍微强调一下我的代码。

我曾认为它可能能够以某种方式“代理”列表,因此临时缓存使用主缓存,并存储所有更改(实际上是更改的备忘录),但是当你想要迭代临时缓存,或在特定索引处检索项目。还考虑到我希望对列表的内容进行一些修改(取决于临时缓存的类型,是否是“自动更新”)并且我完全超出了我的深度。

任何指向技术或数据结构的指针或只是尝试和研究的一般概念将不胜感激。

干杯,

艾多斯

4

1 回答 1

2

这就是你想要做的。类似于所谓的 MVCC,多版本货币控制。

简单地说,您需要将事务 ID 关联到您的缓存元素。

所以缓存条目看起来像这样:

public class CacheEntry {
    long transactionId;
    boolean deleted;
    Object value;
}

您的缓存条目以相反的 transactionid 顺序存储在列表中。

当你去获取一个缓存元素时,你会查找列表(在你的哈希图中)。然后,您搜索具有最高交易 ID(小于或等于您的交易的交易 ID)的值。

因此,让我们考虑 DELETE 问题。

您有事务 10,正在寻找“ABC”。我们假设 ABC 已经在缓存中,并且它是由事务 5 放入的。

因此,T10 获取 ABC 的条目列表,向下搜索列表并发现在 T5 处有值“123”。T5是小于或等于T10的最高交易。T10 将 ABC 的值从 123 更改为 456。

现在 T12 出现并寻找 ABC。它将从 T10 中找到值为 456。T12 决定删除 ABC,因此 T12 的“已删除”缓存条目被放置在缓存条目列表中。如果 T10 再次尝试查找 ABC,它会找到 456,因为 12 > 10,并且最高的事务 <= 10 是 T10,所以它看不到删除。

T14 出现,搜索 ABC,找不到它(因为它已“删除”),并插入一个新值 789。如果要查找 T12,它仍然会被删除,如果 T10 是,它仍然是 456。

因此,最后,您的缓存列表如下所示:

{tid: 14 deleted: false value: 789}
{tid: 12 deleted: true  value: nul}
{tid: 10 deleted: false value: 456}
{tid:  5 deleted: false value: 123}

您遇到的下一个问题是处理未结交易的可见性。即另一个事务可以看到来自另一个未提交的打开事务的数据。但这并不太难,因为它只是在扫描版本列表以寻找合适的候选人时调整标准。您可以保留事务 ID 列表及其状态(打开、提交、回滚)。

最后,您必须想出一种机制来清理松散的末端。提交两个事务后,没有其他打开的事务,可以删除较旧的记录。

例如,如果您有来自 T5 和 T10 的数据,如果它们都已提交,那么没有人将能够再次“看到”T5 的数据,因为 T10 现在是“当前”状态。因此,可以删除 T5 行。

这可能最好通过简单地遍历缓存并删除过时的事务条目来完成。

这就是它的要点,显然魔鬼在细节中。

于 2010-04-10T15:48:54.843 回答