前言 源码分析文章比较难以组织,推荐大家直接看大量注释的源码: 传送门 上一篇文章讲了bkd树的基本原理,这次看一下Lucene对BKD树的实现. bkd树在lucene中的实现,都在org.apache.lucene.util.bkd 树写入 BKD树的写入过程,是在BKDWriter中实现的. 核心是做了两件事,对应两个方法 构建整颗BKD树,对应了build方法. 由于基本上是性能差异, 而这片文章主要想讲lucene如何实现一个BKD树,暂时就不深究性能了. 总结 简单总结一下对一堆多维数据点,构建BKD树的过程. 说实话, 对于BKD树的实现,我目前没有做到100%完全了然于心, 但是经过一番努力,仍旧差点意思,因此只能放到之后了. 希望随着看的越来越多,对于BKD的理解能够更加透彻,回头来润色这篇文章.
bkd树 bkd树用来解决空间问题和插入的效率问题. bkd树由多个修改后的kd树和独特的插入方法构成的. 下图是一个bkd树的基础结构. ? 一个更大的因素,也是bkd最令人感兴趣的地方时,这些内部节点不会被更改,bkd树使用了一个聪明的办法来添加新的节点. 给定一个有N个节点的bkd树, 就有 log2(n/m)个改良的kd树. 每一个树的节点是上一个树的两倍. 对于bkd的插入,新节点直接进入缓冲区. 如果缓冲区满了,找到第一个不存在的kd树. 下面是评估结果. bkd树性能评估. ? 对于插入操作, bkd树比kdb树快两个数量级. 对于一个120亿数字的集合来说,插入消耗的时间平均是50微秒. 如果范围的性能是你最关心的点,那么bkd树可能不是你最应该选择的数据结构. 最后,来看看空间利用率的问题,我们期待bkd树接近于完美. 这个是说明了空间效率的真实数据. ?
2.1 BKD-Tree简介 Point索引基于BKD-Tree(Block K-Dimension Balanced Tree)实现。 上述是对BKD-Tree的简要介绍,方便读者建立对BKD-Tree的直观印象,如果希望了解更多BKD-Tree、KDB-Tree相关内容,可参考相应论文。 由于Lucene未对BKD-Tree和KDB-Tree进行明确的概念区分,为了和源码一致,本文在后续介绍中会统一使用名词BKD-Tree。 2.2 数值索引的基本原理 数值索引依赖于BKD-Tree加速范围查询,基本原理是利用BKD-Tree类似B+Tree的索引功能。 当用户对某字段进行条件查询时,可以先通过.dii获取该字段的Point索引(BKD-Tree)偏移,然后在.dim中定位BKD-Tree的非叶子节点(packed index),按照切分维度信息遍历BKD-Tree
它通过有限状态转换器实现了用于全文检索的倒排索引、用于存储数值数据和地理位置数据的 BKD 树,以及用于分析的列存储。 Bkd 树的空间利用率高达 99%,而 KDB 树空间利用率只有 30%~50% 左右不等。 Bkd 树中的插入比 KDB 树中的插入快 100 倍左右,查询等性能也表现更好。 它通过有限状态转换器实现了用于全文检索的倒排索引、用于存储数值数据和地理位置数据的 BKD 树,以及用于分析的列存储。 treeb-k-d树 原理 图文解析_stevewongbuaa的博客-CSDN博客_bkd tree
2.1 BKD-Tree简介 Point索引基于BKD-Tree(Block K-Dimension Balanced Tree)实现。 上述是对BKD-Tree的简要介绍,方便读者建立对BKD-Tree的直观印象,如果希望了解更多BKD-Tree、KDB-Tree相关内容,可参考相应论文。 由于Lucene未对BKD-Tree和KDB-Tree进行明确的概念区分,为了和源码一致,本文在后续介绍中会统一使用名词BKD-Tree。 2.2 数值索引的基本原理 数值索引依赖于BKD-Tree加速范围查询,基本原理是利用BKD-Tree类似B+Tree的索引功能。 当用户对某字段进行条件查询时,可以先通过.dii获取该字段的Point索引(BKD-Tree)偏移,然后在.dim中定位BKD-Tree的非叶子节点(packed index),按照切分维度信息遍历BKD-Tree
另外一个反过来,记录v是由u得到的,这个map叫做bkd(backward)。 当我们遍历得到一个新的操作(u, v)时,我们首先判断bkd[u]是否存在,如果不存在,说明这个操作和之前的操作没有任何关系,我们直接记录:fwd[u]=v; bdk[v] = u。 如果存在,那么将fwd[bkd[u]] = v,并且更新bkd:bdk[v] = bkd[u],最后删除bdk[u]即可。 ; for (auto &op : operations) { int u = op[0], v = op[1]; if (bkd.count int pu = bkd[u]; fwd[pu] = v; bkd[v] = pu; bkd.erase(
2.1 BKD-Tree简介 Point索引基于BKD-Tree(Block K-Dimension Balanced Tree)实现。 上述是对BKD-Tree的简要介绍,方便读者建立对BKD-Tree的直观印象,如果希望了解更多BKD-Tree、KDB-Tree相关内容,可参考相应论文。 由于Lucene未对BKD-Tree和KDB-Tree进行明确的概念区分,为了和源码一致,本文在后续介绍中会统一使用名词BKD-Tree。 2.2 数值索引的基本原理 数值索引依赖于BKD-Tree加速范围查询,基本原理是利用BKD-Tree类似B+Tree的索引功能。 当用户对某字段进行条件查询时,可以先通过.dii获取该字段的Point索引(BKD-Tree)偏移,然后在.dim中定位BKD-Tree的非叶子节点(packed index),按照切分维度信息遍历BKD-Tree
CommonPrefix: 所有值的公共前缀 PackedValue: 这个叶子节点上的所有点的实际值 相关写入代码分析 除了Header及Footer等内容,其他写入都在org.apache.lucene.util.bkd.BKDWriter.build (int, int, org.apache.lucene.util.bkd.BKDRadixSelector.PathSlice, org.apache.lucene.store.IndexOutput , org.apache.lucene.util.bkd.BKDRadixSelector, byte[], byte[], int[], byte[], byte[], long[], int[]) 该方法递归调用,将BKD树最底层的叶子节点,按照从左到右的顺序逐个叶子写入磁盘. 其中对应图中DocIDs和PackedValue的写入如下图所示: ? 参考文章 逻辑较简单,具体写入策略挺复杂.
如果不满足条件,很简单,左区间的fwd就是全区间的fwd,右区间的bkd就是全区间的bkd,左右区间的meg的最大值就是全区间meg的最大值。 如果满足条件,依然有几种情况,首先对于meg来说,它多了一个取值:L.bkd + R.fwd,因为左右区间可以连接,所以左区间的bkd和右区间的fwd可以连接在一起,连接之后的长度就是两者之和。 其次要考虑一下左区间完全一样或者是右区间完全一样的情况,如果左区间完全一样,那么左区间的fwd就可以连接上右区间的fwd,同理,如果右区间完全一样,那么右区间的bkd就可以连接上左区间的bkd。 完整代码如下: int n; // 结构体,分别存储fwd、bkd和meg struct P { int fwd, bkd, meg; P(){} P(int f, int b l, int r, int m, int k) { tree[k].fwd = tree[lef].fwd; tree[k].bkd = tree[rig].bkd; tree
本文使用 Lucene 代码版本:8.7.0 前言 本文学习kdm文件格式. kdm,kdi,kdd 三个文件共同存储了Lucene中Point类型的数据及索引,Point存储及搜索过程中使用BKD树数据结构 ,因此这三个文件和BKD关系比较密切, 建议在阅读前了解相关的BKD树理论. BKD树简单理论介绍 Lucene中对于BKD树的实现源码 .kdm 文件整体结构 ? DataFilePointer: 数据文件的指针 IndexFilePointer: 索引文件的指针 相关写入代码分析 kdm文件的写入比较集中,大部分在org.apache.lucene.util.bkd.BKDWriter.writeIndex
2.1 BKD-Tree简介 Point索引基于BKD-Tree(Block K-Dimension Balanced Tree)实现。 上述是对BKD-Tree的简要介绍,方便读者建立对BKD-Tree的直观印象,如果希望了解更多BKD-Tree、KDB-Tree相关内容,可参考相应论文。 由于Lucene未对BKD-Tree和KDB-Tree进行明确的概念区分,为了和源码一致,本文在后续介绍中会统一使用名词BKD-Tree。 2.2 数值索引的基本原理 数值索引依赖于BKD-Tree加速范围查询,基本原理是利用BKD-Tree类似B+Tree的索引功能。 当用户对某字段进行条件查询时,可以先通过.dii获取该字段的Point索引(BKD-Tree)偏移,然后在.dim中定位BKD-Tree的非叶子节点(packed index),按照切分维度信息遍历BKD-Tree
相关写入代码分析 对于文件的写入,在org.apache.lucene.util.bkd.BKDWriter#writeIndex(org.apache.lucene.store.IndexOutput 而事实上对于索引(也就是树的整体结构的生成代码)在org.apache.lucene.util.bkd.BKDWriter.packIndex中, ? 其中递归构造二叉树的逻辑在org.apache.lucene.util.bkd.BKDWriter.recursePackIndex中,由于代码过长, 且是比较经典的构造二叉树代码,这里就不贴了。
Lucene 将数字进行特殊处理,内部使用 BKD-Tree 来组织数字键。 BKD 树也是一个较为复杂的数据结构,简单理解就是将数值比较接近的一些数字联合起来作为一个叶节点共享一个 PostingList,同时在 PostingList 的元素需要存储数字键的值,在查询时会额外多出一个过滤步骤 粗略来看,BKD 树会将一个大的数值范围进行二分,然后再继续二分,一直分到几层之后发现关联的文档数量小于设定的阈值,就不再继续拆分了。BKD 树还可以索引多维数值,这样它就可以应用于地理位置查询。 关于 BKD 树的更多细节,我们后面再继续讨论。
每个文档都是一个字段的集合 text fields are stored in inverted indices, and numeric and geo fields are stored in BKD 文档存储在倒排索引中,数字和地理字段存储在BKD trees中 When you have multiple Elasticsearch nodes in a cluster, stored documents optimized data structure 默认情况下,ES对每个字段的所有数据都进行建立索引,并且每个索引字段都具有专用的优化数据结构 例如: 数据类型数据结构text/keyword倒排索引数字/地理位置BKD
url=9eUd8l50tLb7QB669NuSWyyiWeD5bKd7y9Cu_LjwizDmOij-zjDpWB3QKVAtLWglL1A0whxNAdPbj34L1DRNUIpFDPXrcOOmxF7OeFcG2be
: A Dynamic Scalable kd-Tree》启发,基于BKD tree再次升级了地理位置数据索引建模和查询功能。 关于bkd-tree的原理,其大体思路如下。 【优化外存(硬盘)查询】:B-tree > K-D-B-tree > BKD tree。kd-tree其实就是多维的BST。 例如:【数据存储】:BKD tree的核心思路是非常简单的,将N维点集合形成的矩形空间(southWest,northEast)递归分割成更小的矩形空间。 query和bkd-tree形成的区域有三种关系。
点数据).kdi / .dim — Points Index(点索引).kdm — Points Metadata(点元数据)这组文件用于存储数值型字段(整数、浮点数、日期)和地理坐标的索引,底层用的是 BKD dvdDocValues文档值(列存)✓.dvmDocValues Metadata文档值元数据✗.nvdNorms相关性规范化因子✓.nvmNorms MetadataNorms 元数据✗.kddPoints(旧格式)BKD 树数据✓.kdiPoints Index(旧格式)BKD 树索引✓.kdmPoints MetadataPoints 元数据✗.diiPoints Index(新格式)BKD 树索引✗.dimPoints (新格式)BKD 树数据✓.tvdTerm Vector Docs词向量文档✗.tvfTerm Vector Fields词向量字段✗.tvxTerm Vector Index词向量索引✗.tvmTerm 搜索用倒排索引,排序聚合用 Doc Values,原始数据用压缩存储,数值范围查询用 BKD Tree……每一块都是独立的,互不干扰。
等效的注解方式,可以参考下面的写法: 1 @Id 2 @SequenceGenerator(name="bkdex_seq_generator",sequenceName="SQ_BKD_EX
这部分的开销是O(BKd)。 所以ETA预测一次请求的时间复杂度为O(BL+BKd) 美团的SDIM 美团的Sampling-based Deep Interest Modeling(SDIM),沿着ETA的思路,更进一步: target 注意与ETA的区别, ETA中,GSU筛选出来的SBS还要再交给ESU做Attention,还要再耗费O(BKd)的时间 美团SDIM这里是一步到位,CTR server从buckets中提取出来的基本上就是
192.168.43.100:5060>User-Agent: SIP UAS V3.0.0.799029Via: SIP/2.0/UDP 192.168.43.100:5060;rport;branch=z9hG4bKd777f4045f493970ce9fef7683ac1f0bSkeyeVSS 401 UnauthorizedVia: SIP/2.0/UDP 192.168.43.100:5060;rport=18378;received=182.151.20.169;branch=z9hG4bKd777f4045f493970ce9fef7683ac1f0bFrom