倒排索引怎么组织数据
倒排索引把「词条 → 文档列表」组织成词典与倒排表:查询词条时直接从词典找到对应的文档集合,再按布尔逻辑合并。文本字段还会存放位置信息用于短语匹配,另有一份列式存储(doc values)用于排序与聚合。因此词条级匹配极快,而前导通配、复杂正则与对分析后整串值的匹配则要遍历大量词条甚至全部文档。
基本信息
| 核心结构 | 词典(term dictionary)+ 倒排表(postings list) |
|---|---|
| 辅助结构 | 位置信息(支持短语查询)、doc values(排序聚合)、norm(长度归一化) |
| 高效查询 | term、terms、前缀匹配、match_phrase、布尔组合 |
| 低效查询 | 以通配符开头的 wildcard、复杂 regexp、对 text 字段做排序聚合 |
| 适用平台 | Elasticsearch、OpenSearch 及各类全文检索系统 |
典型使用场景
- 关键词检索:把用户输入分词后做词条匹配,比 LIKE 更可控
- 结构化过滤:状态、类型等枚举字段用 keyword 子字段做 term 查询
- 短语与位置匹配:需要「词序不能颠倒」时使用带位置信息的匹配
- 排序与聚合:基于 doc values 排序统计,避免对 text 字段直接聚合
- 日志检索:先按时间与字段过滤收敛,再做文本匹配
示例
示例
// 词条精确匹配:走倒排索引,代价与命中文档数相关
GET /my-index/_search
{ "query": { "term": { "status.keyword": "open" } } }
// 前缀匹配:从词典定位前缀区间,效率可接受
GET /my-index/_search
{ "query": { "prefix": { "name.keyword": "prod-" } } }
// 前导通配:需要遍历大量词条,数据量大后代价不可控
GET /my-index/_search
{ "query": { "wildcard": { "name.keyword": { "value": "*-prod" } } } }
// 排序与聚合应落在 keyword 或数值字段上
get /my-index/_search
{ "size": 0, "aggs": { "by_status": { "terms": { "field": "status.keyword" } } } }查询类型与代价
| 查询 | 执行方式 | 代价特征 |
|---|---|---|
| term / terms | 词典定位后取倒排表并集 | 与命中文档数成正比,最可控 |
| prefix | 在词典上定前缀区间 | 前缀越短、命中词条越多则越慢 |
| match_phrase | 取位置信息校验词序 | 比普通词条匹配多一层位置校验 |
| wildcard(前导) | 枚举大量词条做匹配 | 词条基数大时接近全量扫描 |
| regexp | 在词典上做正则匹配 | 提前收紧前缀可显著降低开销 |
| 对 text 排序聚合 | 需要加载 fielddata 到堆内存 | 易触发内存告警,应改用 keyword 子字段 |
优化的方向是「把过滤条件变成词条可命中的形式」:字段值规范化后再入库、把复合维度拆成独立字段、用 n-gram 或专用字段替代通配符查询。
把大文本字段设为允许 fielddata 做排序或聚合会占用大量堆内存,历史上是导致节点内存告警的常见原因,正确做法是加 keyword 子字段或改用 doc values。