想象你是一家大型图书馆的管理员。图书馆里有一本巨大的《全书索引》——这就是所谓的静态索引。它按字母顺序记录着每一本书的位置,查找速度极快。但问题来了:每天都有新书入库、旧书下架、图书被读者归还后改变了摆放位置。如果每次变化都重新印刷这本大厚书,成本高得惊人;但如果不去更新,读者按照索引去找书,就会扑空。 聪明的你想到一个办法:保持这本大索引不变,同时在旁边放一叠便利贴。新书来了,先在便利贴上记录;旧书下架了,在便利贴上标注“已作废”。读者查书时,先查大索引,再翻便利贴,两者结合就能找到正确的书。等到便利贴积攒得太多、翻起来太麻烦时,就重新印刷一次大索引,把便利贴的内容全部合并进去,然后清空便利贴,开始新一轮循环。 这就是倒排索引动态更新的核心思想——双索引架构。搜索引擎每天要处理海量的文档增删改,如果每次更新都重建整个索引,系统会瞬间瘫痪;如果完全不更新,用户搜索到的全是过期信息。动态索引更新与增量合并,就是为了在“实时性”和“性能”之间找到最佳平衡。这套机制,是所有现代搜索引擎(如 Elasticsearch、Lucene)能够支撑亿级数据实时检索的基石。
掌握动态索引更新与增量合并,是搜索引擎从业者的必修课。它决定了你能否在数据持续涌入时,依然让用户获得秒级搜索体验——这是从“能搜”到“搜得好、搜得快”的关键一跃。
可视化演示
演示双索引架构、段合并策略、以及删除位图管理机制。
本节配有交互动效(进入视口后自动加载,约 3KB)。
双索引架构 内存索引 磁盘索引 写入→内存→刷盘 实时更新
静态索引的困境
图书馆的《全书索引》是一本精装大厚书,每一条记录都清楚地写着书名、作者和书架位置。对于读者来说,这本大厚书是“确定性”的——只要索引上写着什么,就一定能找到对应的书,查询速度极快。这种索引有一个专业名称:静态索引——它一旦建好,就不会轻易改变,因为改变的成本实在太高了。
但图书馆是活的。每天都有新书入库,旧书被送进仓库,甚至偶尔有图书被修补后换到另一个书架。如果每次变化都重新印刷这本大厚书,会发生什么?假设印刷需要整整一天,那么在印刷期间,图书馆的查询服务完全停摆。更糟糕的是,即使印刷完成了,一觉醒来又来了十本新书,一切又过时了。全量重建索引,代价高昂且永无止境。
在实际的搜索引擎中,全量重建索引意味着需要重新读取磁盘上所有文档,重新分词、排序、构建倒排链表。对于一个拥有亿级文档的搜索引擎来说,这个过程可能需要几个小时。在这几个小时里,新文档无法被搜索到,旧文档的删除也无法生效,用户看到的永远是“昨天”的数据。
明白了全量重建之痛,你就会理解为什么搜索引擎工程师们不愿频繁“重印大厚书”。但实时更新又是刚需——用户刚发布了一篇博客,立刻就想在搜索引擎里搜到它。这个看似矛盾的需求,催生了下一节的解决方案:双索引架构。
- 求和符号的含义将集合 \(\mathcal{D}\) 中所有文档的开销逐一累加
- 读取开销从磁盘把文档数据读入内存的时间
- 解析开销对文档进行分词、去停用词等预处理
- 索引开销将解析结果写入倒排索引结构
双索引架构协作
既然重印大厚书不可行,聪明的图书馆管理员决定:保留大厚书作为主索引,再加一叠便利贴作为辅助索引。大厚书继续稳定服务绝大多数查询,便利贴则负责记录所有新增、修改、删除的信息。读者每次查书时,先查大厚书,再翻便利贴,把两处的结果合并起来——这就是双索引架构(Dual Index Architecture)。
这套架构的关键在于分工明确。主索引(Main Index)是一份长期稳定、已经优化好的索引结构,它只接受定期的“批量合并”,平时绝不轻易变动。辅助索引(Auxiliary Index)则是一个轻量级的临时索引,存放自上次合并以来所有新增或修改的文档。当用户发起查询时,搜索引擎需要同时搜索主索引和辅助索引,然后将两部分结果按相关度合并、去重,再返回给用户。
双索引架构的另一个精妙之处在于内存缓冲。新文档不会直接写入磁盘上的索引文件,而是先进入内存中的一个缓冲区。当缓冲区积累到一定规模后,一次性批量写入辅助索引。这样做的好处是显而易见的:批量写入远比逐条写入高效,而且可以减少磁盘的随机 I/O 次数。这就像管理员先把便利贴攒成一叠,再一次性贴到公告板上,而不是每来一本书就跑去贴一次。
双索引架构的精髓在于“变化隔离”——让稳定的继续稳定,让变化的快速变化。主索引的查询效率不会因为频繁更新而下降,辅助索引的写入也足够轻量。但便利贴越积越多,查询时需要翻越的“纸张”也越来越多,性能不可避免地开始下降。这时候就需要一项专门的机制来“清理桌面”,这就是下一节要讲的段合并策略。
- 查询请求用户提交的关键词 \(q\)
- 主索引搜索在大厚书中查找关键词对应的文档集合
- 辅助索引搜索在便利贴中查找关键词对应的文档集合
- 合并结果将两路结果取并集,确保完整性
段合并策略解析
便利贴用久了,会积成一摞。初期翻翻还挺快,但攒到几百张时,找一张特定的便签变得费时费力。解决方案是什么?把便利贴的内容合并进大厚书,然后将便利贴清空——这个过程在搜索引擎中称为段合并(Segment Merge)。
要理解段合并,首先得理解“段”的概念。在 Lucene 等搜索引擎中,索引并不是一个单一的大文件,而是由多个段(Segment)组成的集合。每个段本身就是一个完整的小型倒排索引,包含了一部分文档的索引信息。段是索引的最小独立单元:它可以独立地被搜索,也可以独立地被合并。
段合并策略遵循一个朴素的思路:小段合并成大段,大段合并成更大的段。新文档不断产生新段,这些段越来越小、越来越多。查询时,系统需要依次搜索每一个段并汇总结果——段越多,查询开销越大。合并就是将多个小段读入内存,重新排序、去重、构建成一个更紧凑的大段,然后删除原来的小段。合并完成后,索引的文件数大幅减少,查询变得更快。
段合并的核心是减少文件句柄占用。每个段对应磁盘上的一个文件,而操作系统对进程打开的文件句柄数量是有限制的。如果段的数量过多,文件句柄会耗尽,导致索引无法继续写入。合并不仅提升了查询性能,还是一种必要的基础资源管理手段。
段合并是典型的“空间换时间”策略——用合并时的一次性开销,换取后续查询时的持续性能提升。但合并还有一个容易被忽视的重要任务:真正删除那些已被标记为废弃的文档。这时候就需要“删除位图”出场了。
- 待合并的段集合所有参与本次合并的小段
- 读入开销将段从磁盘读入内存
- 排序开销对段内的词项进行归并排序
- 写出开销将合并后的结果写为新的大段
删除位图与回收
图书馆的便利贴上写着“这本书已经下架了”。但“这本书”其实还安静地躺在书架上,只是不再对读者开放。为什么管理员不直接把它扔掉?因为下架可能只是暂时的,说不定过几天又恢复上架了;更重要的是,直接从大厚书中划掉一条记录成本太高,而标记一下几乎不花任何成本。
这个思路对应到搜索引擎,就是标记删除(Mark-and-Sweep)策略。当用户删除一篇文档时,索引并不立即将该文档从倒排链表中移除,而是在一个删除位图(Deletion Bitmap)上将该文档对应的位置标记为“已删除”。查询时,系统检索到这条记录后会检查删除位图,如果发现被标记,就跳过它。
删除位图本质上是一个位数组,每一位对应一个文档 ID,1 表示已删除,0 表示未删除。它的优势在于极小的存储开销和 O(1) 的检查复杂度。一个拥有百万级文档的索引,删除位图只需几十万字节,查询时的检查也只是一个简单的位操作,几乎不产生任何性能损失。
那么,被标记删除的文档什么时候被物理清理?答案就是段合并时。合并的过程中,参与合并的段会被完整地读入内存,这时候检查删除位图,将被标记的文档从合并结果中直接剔除。这样一来,合并后的新段中根本不包含已删除的文档,磁盘空间也随之释放。删除位图 + 段合并,构成了“标记-清理-回收”的完整闭环。
删除位图解决了“如何知道哪些文档已删除”的问题,段合并解决了“何时真正回收空间”的问题。但还有一个工程问题悬而未决:什么时候触发合并? 这个问题看似简单,却在很大程度上决定了整个系统的稳定性。
- 删除位图中的集合所有被标记为已删除的文档
- 单个文档占用的空间该文档在索引中所占的存储大小
- 加总所有待回收空间所有被删文档的空间累加
合并触发机制
便利贴积累到多少张才值得重新印刷大厚书?如果积累 10 张就重印,那和每来一本书就重印没有区别,成本太高;如果积累 1000 张才重印,查询性能早已严重下降,用户体验受损。寻找合适的触发时机,是动态索引系统的核心工程问题之一。
常见的触发机制有三种。第一种是阈值触发:当辅助索引的文档数量或占用空间超过预设阈值时,触发一次合并。例如,当辅助索引中的文档数达到主索引的 5% 时,就执行合并。这种方式的优点是实现简单、效果可控,但缺点是在流量高峰时,阈值可能很快被突破,导致合并频繁发生。
第二种是定期合并:每隔固定时间间隔(如每分钟、每小时)执行一次合并。这种方式的优点是系统行为可预测,运维人员可以预估合并开销;缺点是如果流量波动大,定期合并可能“不赶趟”——两个合并周期之间,辅助索引可能已经积累了大量文档。
第三种是自适应调控:系统根据当前查询负载、辅助索引增长速度、合并线程占用率等指标,动态调整合并频率。例如,在查询低谷期加大合并力度,在高峰期推迟甚至暂停合并。这是多数生产级搜索引擎采用的方案,但实现复杂度也最高。
触发机制就像图书馆管理员定下的规则:便利贴满 100 张就重印,或者每周固定重印一次。有了规则还不够,还需要考虑一个更实际的问题——合并时系统还要不要正常服务?
- 条件判断的第一种情况辅助索引规模占比超过阈值
- 阈值的作用\(\theta\) 越小,合并越频繁;越大,合并越少
- 条件判断的第二种情况距离上次合并超过预设间隔
- 条件判断的默认情况两者都未达到,不触发合并
工程稳定性保障
想象一下:管理员正在把几百张便利贴的内容逐一抄写进大厚书,这时有读者来问询。如果管理员只顾着抄写,读者可能要等很久;如果管理员先服务读者,抄写工作又会变慢。合并操作和查询操作同时竞争 CPU、磁盘 I/O 和内存资源,如何平衡?
答案是合并限流(Merge Throttling)。搜索引擎在合并时不会“全力冲刺”,而是将合并线程的资源占用控制在预设范围内。例如,将合并线程的最大 I/O 带宽限制为系统总带宽的 20%,或者将合并线程数限制为 2 个。这样,即使在合并期间,查询请求的响应时间也不会出现剧烈波动。Elasticsearch 就提供了 indices.store.throttle.type 和 indices.store.throttle.max_bytes_per_sec 等配置项来控制合并限流。
另一个重要手段是冷热数据分级存储。热数据(最近写入、频繁访问的文档)所在的段常驻内存或高性能 SSD,冷数据(旧文档、访问频率低的文档)则存放在普通磁盘。合并时优先合并热段,冷段的合并频率降低。这种分级策略既保证了热点数据的查询性能,又降低了合并的整体开销。
故障恢复也不容忽视。合并进行到一半,系统崩溃了怎么办?如果直接丢弃未完成的新段,老段已经被删除,数据岂不丢失?生产系统通常采用“先写新段,再删旧段”的策略:合并先构建完整的新段并落盘,确认无误后再删除被合并的旧段。这样,即使合并中途崩溃,旧段依然完好,系统可以回滚到合并前的状态。
合并限流、冷热分级、故障恢复,这些措施共同保障了系统的稳定性。别忘了,在这一切机制的背后,还有一个始终贯穿的思想——增量更新是常态,全量重建是例外。图书馆管理员不会每天重印大厚书,但会每隔一段时间做一次彻底的整理。这份从容,正是双索引架构带来的力量。
- 查询延迟的基线水平没有合并发生时,搜索引擎的常规查询延迟
- 合并产生的额外 I/O 开销合并操作消耗的磁盘带宽
- 限流带宽上限允许合并操作使用的最大带宽
- 影响系数不同存储层级和查询模式下的调节因子
实时查询的完整流程
现在,让我们跟随一篇新发布的博客,走完它在搜索引擎中的完整旅程。这篇博客被用户点击发布后,它的文本内容首先要经过分词、去停用词等预处理,提取出关键词——这个过程叫文档解析。解析完成后,文档并不会立即出现在搜索结果中,而是先进入内存缓冲区。
缓冲区的文档积累到一定数量后,会被批量写入辅助索引。辅助索引收到这批文档,为每个关键词建立倒排链表,将其追加到辅助索引已有的倒排结构上。此刻,这篇博客已经可以被搜索到了——用户搜索博客中的某个关键词时,查询会同时命中主索引和辅助索引。
当辅助索引的规模超过了预设阈值,合并进程被唤醒,将辅助索引的所有文档与主索引中相关的段进行归并。归并的结果是一个新的大段,它既包含了老的文档,也包含了新发布的博客。老段被删除,新段上线服务。此时,这篇博客正式“晋升”为老数据,由主索引全权负责。
回顾整个流程,你会发现“实时性”和“稳定性”之间是一场精妙的舞蹈。缓冲是为了批量写入的效率,辅助索引是为了提供近实时的搜索能力,合并是为了保持长久的查询性能,而限流和故障恢复则是保证这场舞蹈不会在压力下崩溃。这套机制环环相扣,构成了搜索引擎动态索引的完整蓝图。
- 文档进入缓冲区新文档先落在内存缓冲区等待批量处理
- 写入辅助索引缓冲区满后一次性写入辅助索引,并立即可搜索
- 升级为热数据高访问频率的文档段进入热存储层级
- 合并入主索引触发合并机制,将辅助段归并进主索引
AI 应用场景
电商商品检索的实时更新
阶段:数据索引与实时检索
解决的问题:电商平台每秒都有新商品上架、老商品下架、价格修改。若使用静态索引,商家上新后消费者几分钟甚至几小时内都搜不到新商品;若每次更新都重建索引,高峰期系统会拥塞崩溃。动态索引的高效更新写入和渐进合并,让新商品在秒级时间内对消费者可见,同时合并限流保证搜索服务的稳定。
具体案例:某头部电商平台的大促活动中,使用了“内存缓冲 + 辅助索引 + 分段合并”的方案。上新商品先写入内存缓冲区,达到 1 万条后批量刷入辅助索引,消费者立即可检索。每 5 分钟触发一次段合并,将辅助索引归并进主索引,既保证了实时性,又控制住了合并对查询延迟的影响。
企业知识库的文档管理
阶段:文档增量索引
解决的问题:企业内部知识库每天有大量新文档上传、旧文档更新。传统方案需要全量重建索引,耗时数小时且严重影响使用体验。动态索引方案通过删除位图标记过期内容,通过段合并回收存储空间,实现了文档的持续可搜索性。
具体案例:字节跳动的内部知识库使用基于 Lucene 的动态索引方案。文档更新时,新版本文档被索引到辅助索引,旧版本文档在删除位图上被标记。定时合并时,旧版本被物理清除,索引空间得到释放。
新闻资讯平台的实时搜索
阶段:实时数据接入与检索
解决的问题:新闻资讯平台要求新闻发布后立即可以被搜索到。动态索引的辅助索引保证了新文档的即时可见性,而主索引的稳定结构保证了高并发查询下的低延迟。
具体案例:今日头条的资讯搜索系统,新稿件经 10 秒 NRT(近实时)流程即可被搜索到。系统每天处理的段合并超过 5000 次,通过自适应合并调控策略将在高峰期合并线程数调整为 0,确保突发流量下搜索服务不受影响。
Python 演示
import mathfrom collections import defaultdict class DynamicIndex: def __init__(self, merge_threshold=5): self.main_index = defaultdict(list) # 主索引 self.aux_index = defaultdict(list) # 辅助索引 self.deleted = set() # 删除位图(模拟) self.doc_count = 0 self.merge_threshold = merge_threshold self.gc_count = 0 def index_doc(self, doc_id, tokens): """索引一篇文档,先写入辅助索引""" for term in set(tokens): self.aux_index[term].append(doc_id) self.doc_count += 1 # 检查是否触发合并 if self.doc_count % self.merge_threshold == 0: self.merge() def delete_doc(self, doc_id): """标记删除文档""" self.deleted.add(doc_id) def search(self, query_term): """查询:合并主索引与辅助索引的结果""" results = set(self.main_index.get(query_term, [])) results.update(self.aux_index.get(query_term, [])) # 过滤已删除的文档 return [doc_id for doc_id in results if doc_id not in self.deleted] def merge(self): """将辅助索引合并进主索引""" for term, doc_ids in self.aux_index.items(): old_docs = self.main_index[term] merged = list(set(old_docs + doc_ids)) self.main_index[term] = merged # 合并完成后清空辅助索引 self.aux_index.clear() self.gc_count += 1 # 模拟物理回收已删除文档 self.main_index = { term: [doc_id for doc_id in doc_ids if doc_id not in self.deleted] for term, doc_ids in self.main_index.items() } self.deleted.clear() def stats(self): main_docs = len({doc_id for doc_ids in self.main_index.values() for doc_id in doc_ids}) aux_docs = len({doc_id for doc_ids in self.aux_index.values() for doc_id in doc_ids}) return { "main_terms": len(self.main_index), "aux_terms": len(self.aux_index), "main_docs": main_docs, "aux_docs": aux_docs, "merge_count": self.gc_count, } if __name__ == "__main__": index = DynamicIndex(merge_threshold=3) # 模拟新增文档 index.index_doc(1, ["人工智能", "机器学习"]) index.index_doc(2, ["大数据", "人工智能"]) index.index_doc(3, ["机器学习", "深度学习"]) print("初次索引完成:", index.stats()) # 模拟查询 print("搜索'人工智能':", index.search("人工智能")) # 删除一篇文档 index.delete_doc(2) print("删除文档2后搜索'人工智能':", index.search("人工智能")) # 继续添加文档触发合并 index.index_doc(4, ["人工智能", "自然语言处理"]) index.index_doc(5, ["搜索引擎", "机器学习"]) print("触发合并后:", index.stats())
self.aux_index[term].append(doc_id)新文档的每个词项都写入辅助索引,这就是“先记在便利贴上”if self.doc_count % self.merge_threshold == 0: self.merge()每索引 3 篇文档就触发一次合并,模拟阈值触发机制results.update(self.aux_index.get(query_term, []))查询时把主索引和辅助索引的结果合并,确保新文档也能被搜到if doc_id not in self.deleted过滤掉删除位图上标记的文档,实现“标记删除”self.aux_index.clear()合并完成后清空辅助索引,开始新一轮的积累
常见误区
误区既然合并能减少段数量、提升查询性能,那合并得越频繁越好,最好每来一个文档就合并一次。
正解合并本身是一项开销很大的操作——它需要读取大量段文件、在内存中排序、再写回磁盘。如果合并过于频繁,系统的大量资源会消耗在合并上,查询性能反而会大幅下降。就像便利贴刚贴上一张就重新印书,那还不如一开始就用大厚书记录每一条变化。合理的策略是“攒一批、合并一次”,用合并频率去换系统的整体吞吐。
为何常见:初学者往往只看到了合并带来的收益(段变少、查询变快),却忽略了合并本身的成本。这就像只看到锻炼身体的好处,却没有意识到过度训练也会造成损伤。
误区调用删除接口后,索引的磁盘占用应该马上减少,和文档被彻底移除一样。
正解动态索引中的删除是“标记删除”——先在删除位图上将文档 ID 标记为 1,查询时跳过该文档。物理空间真正释放要等到下次段合并时,被标记的文档才会从合并结果中被剔除。这意味着删除后磁盘空间不会立即释放,这是设计上的取舍:为了删除操作的实时性,接受空间回收的延迟。
为何常见:大家对“删除”的直觉来自文件系统,删除文件后磁盘空间立即可用。而索引系统为了追求删除操作的 O(1) 复杂度,牺牲了空间回收的即时性。
误区辅助索引能提供实时搜索能力,所以让辅助索引无限增长,主索引进不进行合并都行。
正解辅助索引越大,查询时需要扫描的额外数据就越多,查询性能下降越明显。查询本来只需要搜索一个主索引,现在还必须同时搜索辅助索引并合并结果,辅助索引的规模直接影响到每次查询的额外开销。工程上,辅助索引的合理阈值通常是主索引的 5%-10%,超过这个比例就应该触发合并。
为何常见:只关注了“能搜到新文档”的好处,忽略了“所有查询都要多搜一份数据”的代价。这是只看到局部优化、忽略全局成本的典型思维误区。
误区合并是在后台执行的,和查询互不影响,用户根本感知不到。
正解合并和查询在底层资源上有激烈竞争——两者都要用 CPU、都要读磁盘、都要占内存。如果不做任何限流,一次大规模合并可能让查询延迟飙升数倍,甚至拖垮整个系统。生产环境必须对合并进行限流(如限制合并线程数、限制 I/O 带宽),让合并“温柔地”进行,才不至于影响在线服务的稳定性。
为何常见:初学者将“后台运行”误解为“不影响运行”,而忽略了资源竞争的本质。就像后台下载电影时,你依然会感觉到网页加载变慢了。
面试题
Q1. 双索引架构中,辅助索引的主要作用是什么?
- A.备份主索引的数据
- B.接收增量文档并提供近实时搜索← 正确答案
- C.替代主索引执行全部查询
- D.压缩主索引的存储空间
答案:B。辅助索引的设计目的是接收新写入的文档,让这些文档能立即被搜索到,实现近实时搜索。它不是备份(A错)、不替代主索引(C错)、不压缩存储(D错)。就像便利贴是记录新增图书的临时位置,而不是替代大厚书。
Q2. 删除位图的核心思想是什么?
- A.直接删除文档对应的索引项
- B.重建整个索引以移除文档
- C.标记文档为已删除,查询时跳过← 正确答案
- D.压缩文档内容以减少占用
答案:C。删除位图采取“标记删除”策略——为每个文档 ID 在位上标记 1 表示已删除,查询时跳过这些文档。不需要立即物理删除(A错),不需要重建索引(B错),压缩内容也与删除无关(D错)。就像在便利贴上标注“下架”,但书还留在书架上。
Q3. 段合并操作中,被合并的小段将经历什么?
- A.永久保留在原位置
- B.被合并成一个大段后删除← 正确答案
- C.被移动到冷存储中
- D.被压缩成更小的段
答案:B。段合并将多个小段读入内存、去重排序后写出成一个新的大段,再删除原来的小段。小段不会永久保留(A错)、不会转到冷存储(C错)、更不会被压成更小的段(D错)。这就像把多张便利贴的内容抄进大厚书,然后撕掉便利贴。
Q4. 辅助索引触发合并的常见条件是什么?
- A.主索引空闲时
- B.辅助索引规模超过阈值或超过时间间隔← 正确答案
- C.用户发起查询请求时
- D.系统发生故障后
答案:B。合并通常在两种条件下触发:辅助索引的文档数与主索引的比例超过预设阈值,或距上次合并的时间超过设定间隔。靠主索引是否空闲(A错)、靠用户查询触发(C错)、靠故障触发(D错)都不是工程实践中的主流方案。
Q5. 为什么合并前要先写新段、再删旧段?
- A.因为新段占用空间更小
- B.为了避免合并中途崩溃导致数据丢失← 正确答案
- C.因为旧段已经不再需要了
- D.因为系统只支持顺序写
答案:B。“先写新、再删旧”的策略是为了故障恢复——如果合并进行到一半系统崩溃,新段可能未写完,但旧段依然完好,系统可以回滚到合并前状态。若先删旧段,崩溃会导致数据永久丢失。这就像先抄好新书再撕旧笔记,而不是先把笔记撕了再抄。
Q6. 合并限流的主要目的是什么?
- A.减少合并操作的总开销
- B.控制合并对查询服务的影响← 正确答案
- C.防止合并过程中出现数据丢失
- D.提高合并操作的执行速度
答案:B。合并限流限制合并操作占用的 I/O 带宽和 CPU 资源,核心目的是避免合并与查询竞争资源,保证查询延迟稳定。限流不是减少合并总开销(A错),不是防止数据丢失(C错),更不能提高合并速度(D错)。
Q7. 关于双索引架构,以下哪些说法是正确的?
- A.主索引负责服务大部分查询请求← 正确答案
- B.辅助索引接收文档增量的写入← 正确答案
- C.查询时需要将两个索引的结果合并← 正确答案
- D.两个索引之间不需要任何协调机制
答案:ABC。双索引架构中,主索引承担主要查询服务(A对),辅助索引负责接收增量文档(B对),查询时合并两路结果(C对)。两索引之间需要协调——合并、删除位图同步等都是协调机制(D错)。就像大厚书和便利贴需要配合使用,缺一不可。
Q8. 以下哪些方式可以控制合并对系统稳定性的影响?
- A.限制合并线程的数量← 正确答案
- B.限制合并使用的 I/O 带宽← 正确答案
- C.在查询高峰期暂停合并← 正确答案
- D.让合并与查询同时全力运行
答案:ABC。限制合并线程数(A对)、限制 I/O 带宽(B对)、在高峰期暂停合并(C对)都是常用的稳定性控制手段。让合并与查询同时全力运行(D错)必然导致资源竞争和性能抖动,是不可取的。
Q9. 删除位图中的文档 ID 何时被物理清除?
- A.删除操作执行后立即
- B.删除位图被清空时
- C.文档所在的段参与合并时← 正确答案
- D.主索引重新构建时
答案:C。被删除位图标记的文档,在包含它的段参与合并时才会被物理清除——合并过程中读取段内容,遇到被标记的文档直接跳过,从而在合并结果中剔除。删除操作本身只是位图标记(A错),位图清空不代表文档被清除(B错),主索引重建不是常规操作(D错)。
Q10. 关于段合并的开销,以下哪些说法符合实际?
- A.合并需要将段文件读入内存← 正确答案
- B.合并过程中需要对词项进行排序← 正确答案
- C.合并后的新段需写回磁盘← 正确答案
- D.合并的开销与段大小无关
答案:ABC。段合并的流程是:读入小段(A对)、内存中归并排序(B对)、写出新的大段(C对)。合并开销与参与合并的段大小直接相关——段越大,读入、排序、写出的时间都越长(D错)。这就像整理便利贴,张数越多,粘贴工作越耗时。