探究:Elasticsearch 文档的 _id 是 Lucene 的 docid 吗?

1、前言

之前在与研发进行 ES 使用优化的过程中,研发的同事饶有兴致的在会议后问了我这么一个问题:我们写入 ES 的 _id 字段和 lucene 中使用的 docid 是一个内容么?

两者有什么关联么?

当时对 Lucene 没有太多了解的我只能实话实说:两者应该不是一个概念,但是具体是否有关联我这边也没有梳理清楚,后面有结论了可以再进行沟通。

现在,我们针对这个问题梳理一下吧。

2、Lucene 的 docid

首先来看看 Lucene 官方对 docid 的定义。

go 复制代码
Lucene's index is composed of segments, each of which contains a subset of all the documents in the index, and is a complete searchable index in itself, over that subset.  As documents are written to the index, new segments are created and flushed to directory storage.  Segments are immutable;  updates and deletions may only create new segments and do not modify existing ones.  Over time, the writer merges groups of smaller segments into single larger ones in order to maintain an index that is efficient to search, and to reclaim dead space left behind by deleted (and updated) documents.

Each document is identified by a 32-bit number, its "docid," and is composed of a collection of Field values of diverse types (postings, stored fields, doc values, and points).  Docids come in two flavors: global and per-segment.  A document's global docid is just the sum of its per-segment docid and that segment's base docid offset.  External, high-level APIs only handle global docids, but internal APIs that reference a LeafReader, which is a reader for a single segment, deal in per-segment docids.

Docids are assigned sequentially within each segment (starting at 0). Thus the number of documents in a segment is the same as its maximum docid;  some may be deleted, but their docids are retained until the segment is merged.  When segments merge, their documents are assigned new sequential docids.  Accordingly, docid values must always be treated as internal implementation, not exposed as part of an application, nor stored or referenced outside of Lucene's internal APIs.

最直接的定义:

  1. docid 是 32 位的数字,只是用于标记文档;
  1. docid 由不同类型的Field值(发布、存储字段、文档值和点)的集合组成;
  1. docid 有两种类型:全局的和每段的。文档的全局文档值只是其每段文档值和该段的 docid 偏移量的总和。
  1. docid 在每个段内按顺序分配(从 0 开始)。
  1. docid 只在 segment merge 的时候回收,然后被重新分配。

最后,官方强调 docid 只能作为 Lucene 的内部实现,并不适合外部应用调用,也不能在 Lucene 的内部 api 之外存储或引用。

虽然官方对 docid 的生成和使用都介绍的很详细,但是最后一句概括了所有,docid 只是 Lucene 内部对文档进行标记的实现方式,与外部无关。

3、ES 的 _id

下面我们看看 ES 的 _id 字段。

go 复制代码
Each document has an _id that uniquely identifies it, which is indexed so that documents can be looked up either with the GET API or the ids query. The _id can either be assigned at indexing time, or a unique _id can be generated by Elasticsearch. This field is not configurable in the mappings.

这里 ES 对 _id 字段的定义就很清晰,它是文档唯一性标记,它可以在写入的时候可以被指定值,如果不指定 ES 会自己生成一个值。即 _id 是 ES 文档数据的唯一性主键,即 uuid。

ES 这么做的原因是 Lucene 不强要求写入的数据需要带 uuid,这在 Lucene 的主要开发者 Michael McCandless 博客里得到了印证,Lucene 并不要求写入的数据具有唯一性主键。原文如下

go 复制代码
Most search applications using Apache Lucene assign a unique id, or primary key, to each indexed document. While Lucene itself does not require this (it could care less!), the application usually needs it to later replace, delete or retrieve that one document by its external id.

而从 Lucene 的角度看,像 _id 这样的元信息字段,只是 ES 对 Lucene 定制的一个字段,和其他字段没有本质的区别。

go 复制代码
public AbstractIdFieldType() {super(NAME, isIndexed:true, isStored:true, hasDocValue:false, TextSearchInfo.SIMPLE_MATCH_ONLY, Collections.emptyMap());}

从 ES 源码可以看到,_id 字段是一个被倒排索引,被 store 了,但是没有生成列存 docvalue 的特殊字段。

而我们日常用 _id 在 ES 进行 GET 操作获取文档数据,与使用 ES search API 进行 _id 查询获取数据,对于 Lucene 来说是一样的,都是对 _id 字段进行检索。

go 复制代码
# get 操作
GET my-index-000001/_doc/2

# search 操作
GET my-index-000001/_search
{
  "query": {
    "terms": {
      "_id": [ "2" ] 
    }
  }
}

4、小结

综上,我们不难看出,虽然 _id 字段和 docid 都是用与标记文档的,但两者并无耦合性关系

_id 字段是 ES 用来做数据唯一性主键的特殊字段。docid 则是 Lucene 的内部实现,服务于 Lucene 字段查找的过程。

当然,ES 实现数据多版本的唯一性,不仅仅依靠 _id 字段,更多的信息在

《Elasticsearch内核解析 - 数据模型篇》:

https://zhuanlan.zhihu.com/p/34680841,

而对 _id 字段优化使用有兴趣的同学可以参考

《Choosing a fast unique identifier (UUID) for Lucene》:

https://blog.mikemccandless.com/2014/05/choosing-fast-unique-identifier-uuid.html

作者介绍

金多安,Elastic 认证专家,Elastic 资深运维工程师,死磕 Elasticsearch 知识星球嘉宾,星球Top活跃技术专家,搜索客社区日报责任编辑

铭毅天下审稿并做了部分微调。

更多推荐

  1. Elasticsearch 使用误区之一------将 Elasticsearch 视为关系数据库!

  2. Elasticsearch 使用误区之二------频繁更新文档

  3. Elasticsearch 使用误区之三------分片设置不合理

  4. Elasticsearch 使用误区之四------不合理的使用 track_total_hits

  5. Elasticsearch Filter 缓存加速检索的细节,你知道吗?

  6. 深入解析 Elasticsearch IK 分词器:ik_smart 和 ik_max_word 的区别与应用场景

  7. 图解 Elasticsearch 的 Fielddata Cache 使用与优化

  8. 《一本书讲透 Elasticsearch》读者群的创新之路

更短时间更快习得更多干货!

和全球2000+ Elastic 爱好者一起精进!

elastic6.cn------ElasticStack进阶助手

抢先一步学习进阶干货!

相关推荐
weixin_460443562 分钟前
企业考试系统如何对接OA、钉钉和企业微信?SSO单点登录、组织同步与权限一致性设计
java·开发语言·数据库
Sylvia33.4 分钟前
篮球数据API的技术架构与工程实践:基于火星数据WebSocket实时推送体系
java·python·websocket·网络协议·架构
Hi李耶18 分钟前
【LeetCode】15.三数之和
java·算法·leetcode
字节跳动数据平台43 分钟前
Lance Meetup 2026 · 上海站|正式开启报名!
大数据
蔬菜_1 小时前
前端转全栈-day6(构造函数与继承)
java
程序员雷欧1 小时前
Java 反射深度解析:从原理到源码的全面剖析
java·开发语言·python
登登登__1 小时前
春秋招笔试题总结
java·开发语言
汉拓3D数字化1 小时前
PDM到ERP的数据通道:四种方案的真实成本和落地难度
大数据·科技·系统·软件
rannn_1111 小时前
【力扣hot100】链表专题|21、2、19、24、92、25
java·数据结构·算法·leetcode·链表·开发
GlobalInfo2 小时前
AI与另类数据融合,市场研究正在从“经验驱动”走向“数据智能”
大数据·网络·人工智能·ai