垃圾回收
关于垃圾回收的详细信息
垃圾回收的必要性
- 当用户删除文档时,Redis 只会在全局文档表中将它们标记为已删除,而不会直接彻底删除。这样做是为了提高效率。根据文档的长度,删除操作可能耗时较长。
- 这意味着已删除的文档不再分配内部数字 ID。遍历索引时会检查删除标记。
- 所有属于已删除文档 ID 的倒排索引条目都是垃圾数据。
- 更新文档基本上等同于先删除它,然后使用新的递增内部 ID 再次添加。不执行差异比较,而是追加到索引中,因此 ID 保持递增,更新速度快。
上述所有情况意味着,如果存在大量更新和删除操作,我们的倒排索引很大一部分将变成垃圾数据,这既会降低速度,也会消耗不必要的内存。
您希望优化索引,但又不想干扰正常操作。这意味着优化或垃圾回收应该是一个非侵入性的后台进程。它只需要在足够长的时间内快于删除速率,这样您就不会产生比能回收的更多的垃圾数据。
单词索引的垃圾回收
单词倒排索引是块的数组,每个块包含编码后的记录列表;例如,文档 ID 增量加上取决于索引编码方案的其他数据。当其中一些记录指向已删除的文档时,这被称为垃圾数据。
算法很简单
- 为每个块创建一个读取器和写入器。
- 逐个读取每个块的记录。
- 如果没有记录无效,则不做任何操作。
- 当找到垃圾记录时,读取器前进,但写入器不前进。
- 当至少找到一个垃圾记录时,将后续记录编码到写入器,并重新计算增量。
伪代码
foreach index_block as block:
reader = new_reader(block)
writer = new_write(block)
garbage = 0
while not reader.end():
record = reader.decode_next()
if record.is_valid():
if garbage != 0:
# Write the record at the writer's tip with a newly calculated delta
writer.write_record(record)
else:
writer.advance(record.length)
else:
garbage += record.length
数字索引上的垃圾回收
数字索引是倒排索引的树,具有特殊的编码格式(文档 ID 增量,值)。这意味着可以将相同的算法应用于它们,只需遍历树中的每个倒排索引对象。
FORK GC
关于 FORK GC 的信息可以在此博客中找到。
自 v1.6 版本以来,FORK GC 是默认的垃圾回收策略,已被证明在清理索引和不降低查询与索引性能方面非常高效,即使对于写操作非常密集的使用场景也是如此。