Skip to content

DelayRemove 延迟删除

本文档说明 Zeze 的 DelayRemove 延迟删除机制:被删除的记录先加入延迟队列,过一段时间才真正删除,主要用于解决基于记录的队列(如 LinkedMap)在并发遍历与删除时的索引一致性问题。

DelayRemove 把删除操作分成两步:先把记录加入延迟队列过一段时间才真正删除。它是一个通用的延迟垃圾回收机制,LinkedMap 的「记录锁外并发遍历和删除」问题是它解决的典型场景之一,但并非只为 LinkedMap 设计。

特性说明
行为删除的记录先加入延迟队列,延迟一段后才真删
初始化需要 start() 启动(后台清扫任务)
解决问题并发遍历与删除时的索引一致性

DelayRemove.remove实例方法(非静态方法),通常通过 Table.delayRemove 间接使用:

// 实例方法(DelayRemove 单例,非静态调用)
delayRemove.remove(table, key);
// 通过 Table(推荐,内部转调上面的实例方法)
table.delayRemove(key);
API说明
DelayRemove.remove(TableX<K,?> table, K key)实例方法,把记录加入延迟队列
Table.delayRemove(key)Table 实例方法,等价调用

设计动机:LinkedMap 并发遍历问题

Section titled “设计动机:LinkedMap 并发遍历问题”

Zeze.Collections.LinkedMap 用记录存储 Node:每个 Node 记录前、后 Node 的 Key,每个 Node 存一定量 Item(可存很大 Item 量)。

问题:遍历 LinkedMap 时,不能在一个事务内访问所有记录,须挨个处理。当下一个记录刚被删时:

当前 Node.next → 指向已删除的记录
→ 获取下一个得 null
→ 不能继续遍历

DelayRemove 的解法:先更新前后记录的索引,更新后遍历只遍历新记录,并发遍历能继续;一定时间后才真正删除。

删除 Node X:
1. 先更新 X.prev.next = X.next 和 X.next.prev = X.prev (索引更新)
2. 此时并发遍历只走新索引,不再访问 X (遍历可继续)
3. 延迟一段时间后才真正删除 X (垃圾回收)

这相当于一个简单的垃圾回收机制:保证遍历过程中正在使用的数据不会被立即回收。

配置项默认值说明
DelayRemoveHourStart3每天执行真删的起始小时
DelayRemoveHourEnd7每天执行真删的结束小时
DelayRemoveDays7保留天数
<!-- 每天凌晨 3 点到 7 点之间执行真删,记录最多保留 7 天 -->
<DelayRemoveHourStart>3</DelayRemoveHourStart>
<DelayRemoveHourEnd>7</DelayRemoveHourEnd>
<DelayRemoveDays>7</DelayRemoveDays>