时长16:48大小15.39M
作者回复: 综合你前一条一起回复,你说到了两个点上:
1.倒排索引和kv不一样,posting list元素很多,每个元素都加标记代价太大。
2.一个文档可能会影响多个key,因此每个文档都要修改标记的话,读写操作会很频繁,加锁性能下降。
此外,还有一点是,加上标记也没啥用,在posting list求交并的过程中,依然要全部留下来,等着最后和全量索引合并时才能真正删除。这样的话不如直接用一个delete list存着,最后求交集更高效。
作者回复: 是这样,双缓存机制的设计理念是读写分离,一个索引只负责写,另一个索引只负责读。因此,不会存在你说的两个索引同时被写的情况。
作者回复: 滚动合并机制的确是最复杂的一种。它的核心思想是“解决小索引和大索引合并的效率问题,避免大索引产生大量无谓的复制操作”。而解决方案则是“在小索引和大索引中间加入中索引进行过渡”。
这个设计方案其实会很常见。比如说在lsm树那一课,我说了“假设只有c0树和c1树”,而实际情况是c1树会非常大,合并效率会很低,因此lsm树的设计中就有着多棵不同大小的树。包括leveldb的实现,也会有着多层索引。因此,这是一个值得我们学习和掌握的方法。
至于你举的这个例子,结合文中的内容,使用滚动合并的流程是这样的:
1.今天增加的网页会先存在内存的增量索引中。
2.增量索引满了,要开始合并。
3.增量索引和当天的天级索引合并(天级索引不大,所以合并代价小)。
4.当天级索引达到了7天时,可以将多个天级索引合并,变成一个新的周级索引。
5.当有多个周级索引的时候,全量索引会和多个周级索引合并,生成一份新的全量索引。(不过,一般这一步会用重新生成全量索引来代替,你可以理解为为了保证系统的稳定性,需要定期进行索引重建。就像系统要进行定期重启一样)。
作者回复: 你的想法很好,其实是有可能的。本质上,你是复用了正排表,让它承载了删除列表的功能。在最后posting list合并的时候,通过查正排表完成过滤(其实就是加餐一中说的哈希表法:将删除列表变成了哈希表)。
在系统比较简单的时候,这样使用是OK的。不过当系统足够复杂的时候,我们需要将不同功能和数据进行合理的划分,倒排检索和正排查询有可能是两个不同的环节和模块(包括中间可能还有其他环节,比如抽取特征,打分计算等)。因此从这个角度出发,复杂系统才会抽象出删除列表这个对象,这样就可以不依赖于正排表,从而完成了系统架构的解耦设计。
作者回复: 总结得很认真。相信你学完这一课后,再去看es中的segment的处理就会很轻松了,比如segment的生成和合并,还有.del文件存储删除列表等。
此外,你还可以思考索引更新这一块,你们当前系统的实现方案是否合理,是否有优化空间等。
作者回复: 这些是可以并行查找的。而不是串行。
而且一般来说,以现在的机器处理能力,周级索引其实也可以不用的,这样也能减少系统复杂度。
因此一般系统实现就是:
增量索引+ 天级索引+全量索引 三个索引并行检索,再合并结果。
作者回复: 在另一条下面统一回复你了。
作者回复: 哈哈,的确说得没那么透,只说了“怎么做”,但没说“为什么”。因此才在课后讨论题让大家想想为什么。
你说到了很重要的一点,kv只有一个值,但倒排表是一整个posting list,所以修改代价会大。
另一方面,即便是使用double buffer技术对增量索引做无锁更新,但增量索引检索过程中,依然要把所有被删除的文档保留到最后,再和全量索引做合并。
那既然所有的标记都要保留到最后一步,不如直接在最后一步用一个delete list来求交集更快,逻辑也清晰。
作者回复: 这两点都很好。
的确posting list里每个元素都加标记,这个代价会远大于lsm这种只存一个元素kv的场景。
此外,一个文档被删除,它可能会影响很多key和posting list,这个读写加锁代价不小。
还有,即使使用double buffer实现增量索引,但是这个标记也没什么用。我们在增量索引中求交集和并集时,依然要保留所有的元素,这样和全量索引的结果合并时才不会出错。因此提前打上标记并不能加快检索效率。不如最后记录一个delete list,然后快速求交集处理掉。
作者回复: 你很好地吸收了读写分离的思想。全量索引上肯定是不能加删除项的。不过可读写的增量索引上面能否加上删除标记呢?你可以想一想。
提示:
1.加的话是否有性能损失。
2.不管有没有性能损失,加上后,求检索结果的过程是怎么样的?加上这个标记和单独记录一个删除列表相比有帮助么?
你在思考以后,可以再看看我的回复。
作者回复: 这一步我没有画,不过你可以沿着前面天级索引和周级索引合并的思路思考一下,包括总结时我强调的“读写分离”去想想,我相信,你应该可以得到“不要加锁”这个结论的。
这里我也补充一点,就是周级到全量索引的合并,其实由于隔的时间已经很久了,因此,很多时候我们会直接完全重建全量索引,一方面,重建时间是足够的,另一方面,也等于定期给系统“重启”,保证系统的稳定性和正确性。
作者回复: 避免加锁操作的确是一个考虑因素。新删除一个文档,这个文档里可能有很多key,如果要打删除标记,就意味着这些key和posting list都要执行加锁操作,这个代价的确会比较大。
而且,即使我们使用double buffer,对于增量索引不加锁,那么你可以想想处理过程,如果对于增量索引的posting list中的文档打上删除标记,在进行交并操作的时候,所有的文档都依然要被留下!因为增量索引的结果需要和全量索引结果合并,如果增量索引的结果没有保留删除标记,那么合并时会出错。
既然删除标记要在增量索引处理过程中一直保留,那不如单独记录来得方便。