Lucene 段文件(segment, si)格式分析

以下均是基于lucene 8.1.0

1. 文件格式

段元数据

解释:

  1. 1到8节是一个整型CODEC_MAGIC数值为: 0x3f d7 6c 17
  2. 接下来0x08是一个退格, 看代码其实没有这个,但不知为啥有这个
  3. 接下来为'segment' 这个都是可见字符,如右图
  4. 接下来是一个版本号,可以知道版本号为0x00 00 00 09, 即9, 目前来说要求该版本号在VERSION_70(7)到VERSION_74(9)之间
  5. 接着是16字节的id字段,即 0x88 b000 65f0 baed e64b feb6 9c0b 9f3c 98
  6. 接着是一个字节的IndexHeaderSuffix, 值为0x01, 即1
  7. 然后读取7的IndexHeaderSuffix字节数,即 0x33, 对应数据3, 即后缀为3
  8. 接下来读取3个变长的int数, 即 0x08, 0x01, 0x00 即8、1、0, 代表lucene主版本号、次版本号、bugfix版本号
  9. 接下来读取一个可变Int的 indexCreatedVersion, 如图会读取到0x08, 即8
  10. 接下来会读取一个8字节的Long值,该值代码SegmentInfos的版本号,这个版本号代表着index 被修改了多少次, 如图会读到0x0000 0000 0000 000a 即10
  11. 如果在步中读到的值 大于 VERSION_70(7)的话,即接下读一个可变long值, 否则的话一个int值,由于是基于lucene 8测试,此时肯定读一个long, 即0x03 即3, 这个counter用于命名新的segments
  12. 接下来读一个int的 segments数量,读到00 00 00 03即3
  13. 接下下读3个可变int, 类似于9中, 用于指名segmentInfors的minSegmentLuceneVersion的值, 如图,会依次读到0x08, 0x01, 0x00, 即8,1,0 对应lucene 版本为 8.1.0
  14. 现在开始一次读取这三个段的内容, 此部分以一个循环的行为读取
  • 14.1 首先读取段的名称segmentName, lucence的 DataInput存储字符串的方式是: 用一个vint 存储长度,接下来是实际的字符串内容。首先读vint会读到0x02即2, 长度为2,接下来读取0x5f30即'_0', 即segmentName是_0
  • 14.2 接下来读到16个字节的id 0x2372 32a1 0510 310f 9913 0f21 8aec fd1f
  • 14.3 接下来读到一个字符串, 会读取Lucene80(0x08 4c 7563 656e 6538 30), 接下来会通过SPI反射拿到CodeC, 类名org.apache.lucene.codecs.lucene80.Lucene80Codec
    , 接下来会调用CodeC的segmentInfoFormat的read方法读取segment信息。 这里需要仔细展开。
    • 14.3.1 首先得到段信息文件_0.si 打开文件,_0.si的内容如下:
段信息
  • 14.3.2 检查文件头部。
    首先读到一个Int的CODEC_MAGIC (0x3f d7 6c 17), 这里会针对这个作判断以验证文件是否已被破坏。接着读取一个19字节的actualCodec字段,如图会读取Lucene70SegmentInfo, 接下来会读取一个Int的actualVersion 这会读到0。 接下来读取16个字段的id 0x2372 32a1 0510 310f 9913 0f21 8aec fd1f, 再接着读到一个字节以作为IndexHeaderSuffix的长度,会读到0, 再接着会读0字节的IndexHeaderSuffix的值;再接着会连续读3个Int值代码的主、次、bugfix版本号。依次读到8, 1,0
    接下来会读一个字节的minVersion, 从而会读到0x01 即1, 如果 minVersion = 1的话接下来会依次读三个int, 即会读到 0x0000 0008, 0x0000 0001, 0x00000000

  • 14.3.3 读取文件实际内容
    接上,读完minVersion后,会读一个int的docCount, 代表在这个段内doc的数量,会读到0x0000 0001 即1,接着会读一个字节的的标识, 会读到0x01, 如果该值与SegmentInfo.YES的值相等,则代表这是一个复合文件,见isCompoundFile = true. 接着会读取Map<String, String>, Set<String>, Map<String, String>

    读取第一个Map逻辑(详细请参考DataInput.readMapOfStrings()): 首先读取一个可变int值代表map的size, 如图会读到0x0a, 即10,接着依次读取10个keyValue对, 会读到。

    key length key(0x) key(char) value length value(0x) value(char)
    0x02 0x6f73 os 0x08 0x4d61 6320 4f53 2058 Mac OS X
    0x0b 0x6a6176612e76656e646f72 java.vendor 0x12 0x4f 7261 636c 6520 436f 7270 6f72 6174 696f 6e Oracle Corporation
    0x0c 0x 6a61 7661 2e76 6572 7369 6f6e java.version 0x09 0x31 2e38 2e30 5f31 3531 1.8.0_151
    0x0f 0x 6a 6176 612e 766d 2e76 6572 7369 6f6e java.vm.version 0x0a 0x32 352e 3135 312d 6231 32 25.151-b12
    0x0e 0x6c75 6365 6e65 2e76 6572 7369 6f6e lucene.version 0x05 38 2e31 2e30 8.1.0
    0x07 0x6f 732e 6172 6368 os.arch 0x06 0x 78 3836 5f36 34 x86_64
    0x14 0x6a61 7661 2e72 756e 7469 6d65 2e76 6572 7369 6f6e java.runtime.version 0x0d 0x 31 2e38 2e30 5f31 3531 2d62 3132 1.8.0_151-b12
    0x06 0x73 6f75 7263 65 source 0x05 666c 7573 68 flush
    0x0a 0x 6f73 2e76 6572 7369 6f6e os.version 0x07 31 302e 3133 2e31 10.13.1
    0x09 0x 74 696d 6573 7461 6d70 timestamp 0x0d 0x31 3536 3337 3033 3136 3834 3132 1563703168412

    读Set: 首先读取一个可变Int做为Set的size, 会读到0x03, 接着会依次读到

    length(0x) value(0x) value(char)
    0x06 0x5f30 2e63 6665 _0.cfe
    0x05 0x5f 303e 7369 _0.si
    0x06 0x5f30 2e63 6673 _0.cfs

再次读Map,首先读取map的size = 0x01, 接着

key length key(0x) key(char) value length value(0x) value(char)
0x1f 0x 4c 7563 656e 6535 3053 746f 7265 6446 6965 6c64 7346 6f72 6d61 742e 6d6f 6465 Lucene50StoredFieldsFormat.mode 0a 0x4553 545f 5350 4545 44 BEST_SPEED

后续: 读一个可变Int 以作为 numSortFields, 如图会读到0x00, 如果此值不为0, 会对这一字段做进一步解析()。 后面会是文件尾。文件尾长度为16个字节, 依次为 尾Magic, 值为CODEC_MAGIC的反码(0xc028 93e8) 接着会读一个int的algorithmID0x0000 0000 。 接下来会读一个long类型的checksum 0x0000 0000 e440 e97d 最后会剩下一个0x0a

回到segment_3文件。

接下来会依次读:
一个Long的delGen, 值0x ff ffff ffff ffff ff, 再读一个Int的delCount,值为0x00 0000 00
一个Long的fieldInfosGen 值为0xff ffff ffff ffff ff. 一个Long的 dvGen 值为0x ff ffff ffff ffff ff
一个Int的 softDelCount, 值为00 0000 00
后会就是文件尾,和_0.si文件格式一致,请参考此块文件内容。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,014评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,796评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,484评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,830评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,946评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,114评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,182评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,927评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,369评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,678评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,832评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,533评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,166评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,885评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,128评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,659评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,738评论 2 351

推荐阅读更多精彩内容