大多数使用Apache Lucene的搜索应用程序都会为每个索引文档分配唯一的ID(即主键)。 尽管Lucene本身不需要这样做(它可能不太在乎!),但应用程序通常需要它以后通过其外部ID替换,删除或检索该文档。 大多数在Lucene之上构建的服务器,例如Elasticsearch和Solr ,都需要一个唯一的ID,如果不提供,则可以自动生成一个ID。

有时,您的ID值已经预先定义,例如,如果外部数据库或内容管理系统分配了ID ,或者您必须使用URI ,但是如果您可以自由分配自己的ID,那么哪种方法最适合Lucene?

一个明显的选择是Java的UUID类,该类生成版本4的通用唯一标识符 ,但事实证明,这是性能上最糟糕的选择:它比最快的速度慢4倍。 要了解原因,需要对Lucene如何找到术语有所了解。

BlockTree术语词典

术语词典的目的是存储在索引期间看到的所有唯一术语,并将每个术语映射到其元数据( docFreqtotalTermFreq等 )以及totalTermFreq (文档,偏移量,投递和有效载荷)。 当请求一个术语时,术语词典必须在磁盘索引中找到它并返回其元数据。

默认编解码器使用BlockTree术语词典 ,该词典以排序的二进制顺序存储每个字段的所有术语,并将这些术语分配到共享公共前缀的块中。 默认情况下,每个块包含25到48个词。 它使用内存中的前缀三叉戟索引结构( FST )将每个前缀快速映射到相应的磁盘块,并在查找时首先根据请求的术语的前缀检查索引,然后在-disk块并扫描以查找术语。

在某些情况下,当段中的术语具有可预测的模式时,术语索引可以知道请求的术语不能存在于磁盘上。 这种快速匹配测试可以带来可观的性能提升,尤其是当索引很冷(操作系统的IO缓存不缓存页面)时,因为它避免了昂贵的磁盘搜寻。 由于Lucene是基于段的,因此单个id查找必须访问每个段直到找到匹配项,因此快速排除一个或多个段可能是一个大胜利。 保持细分受众群的数量尽可能少也很重要!

鉴于此,完全随机的id(例如UUID V4 )应该会表现最差,因为它们击败了术语索引快速匹配测试,并且需要对每个段进行磁盘搜索。 具有可预测的每段模式的ID(例如顺序分配的值或时间戳)应发挥最佳作用,因为它们将使术语索引快速匹配测试的收益最大化。

测试性能

我创建了一个简单的性能测试器来验证这一点。 完整的源代码在这里 。 该测试首先将1亿个ID索引到具有7/7/8段结构(7个大段,7个中段,8个小段)的索引中,然后搜索200万个ID的随机子集,记录最佳时间5次。 我在Ubuntu 14.04上使用Java 1.7.0_55,以及3.5 GHz Ivy Bridge Core i7 3770K。

由于Lucene的术语从4.0开始是完全二进制的 ,因此存储任何值的最紧凑的方法是二进制形式,其中每个字节使用所有256个值。 然后,一个128位ID值需要16个字节。

我测试了以下标识符来源:

  • 顺序ID(0、1、2,...),采用二进制编码。
  • 零填充的顺序ID(00000000、00000001等),采用二进制编码。
  • 纳米时间,二进制编码。 但是请记住, 纳米时间是棘手的 。
  • 使用此实现从时间戳,nodeID和序列计数器派生的UUID V1 。
  • UUID V4 ,使用Java的UUID.randomUUID()随机生成。
  • 片状ID ,使用此实现 。

对于UUID和Flake ID,除了标准(16或36基)编码之外,我还测试了二进制编码。 请注意,我仅使用一个线程测试了查找速度,但是在添加线程时,结果应该线性扩展(在足够并行的硬件上)。

用二进制编码的零填充顺序ID最快,比非零填充顺序ID快很多。 UUID V4(使用Java的UUID.randomUUID() )慢了约4倍。

但是对于大多数应用程序来说,顺序编号是不实际的。 第二快的是UUID V1 ,以二进制编码。 令我惊讶的是,这比Flake ID快得多,因为Flake ID使用相同的原始信息源(时间,节点ID,序列),但是以不同的方式对位进行混洗以保留总体顺序。 我怀疑问题在于,在获取不同文档之间的数字之前,必须在片状ID中遍历的通用前导数字的数目,因为64位时间戳的高位在先,而UUID V1则在低位位首先是64位时间戳。 当一个字段中的所有术语共享一个公共前缀时,术语索引也许可以优化这种情况。

我还分别测试了10、16、36、64、256的基数,通常对于非随机ID,较高的基数更快。 我对此感到惊讶,因为我希望与BlockTree块大小(25到48)匹配的基数最好。

此测试有一些重要警告(欢迎补丁)! 一个真正的应用程序显然比简单地查找id还要做更多的工作,并且结果可能会有所不同,因为热点必须编译更多活动的代码。 在我的测试中,该索引非常热(有大量RAM可以容纳整个索引); 对于冷索引,我希望结果会更加鲜明,因为避免磁盘搜索变得非常重要。 在实际应用中,使用时间戳的id在时间上会更加分散; 我可以通过伪造更大范围的时间戳来“模拟”自己。 也许这会缩小UUID V1和Flake ID之间的差距? 我在索引编制过程中只使用了一个线程,但是具有多个索引编制线程的实际应用程序会将ID一次分散到多个段中。

我使用了Lucene的默认TieredMergePolicy ,但是有一种更聪明的合并策略可能会支持那些ID更加“相似”的合并段,从而可能会产生更好的结果。 该测试不执行任何删除/更新操作,这将在查找期间需要进行更多工作,因为如果给定的ID已被更新(除其中一个以外的所有其他ID),则该ID可能位于多个段中。

最后,我使用了Lucene的默认编解码器,但是当您愿意将RAM换成更快的查询时,我们有不错的发布格式针对主键查找进行了优化,例如去年的Google夏季代码项目和MemoryPostingsFormat 。 这些可能会提供可观的性能提升!

翻译自: https://www.javacodegeeks.com/2014/05/choosing-a-fast-unique-identifier-uuid-for-lucene.html

为Lucene选择快速唯一标识符(UUID)相关推荐

  1. lucene快速入门_为Lucene选择快速唯一标识符(UUID)

    lucene快速入门 大多数使用Apache Lucene的搜索应用程序都会为每个索引文档分配一个唯一的ID(即主键). 尽管Lucene本身不需要这样做(它可能不太在乎!),但应用程序通常需要它以后 ...

  2. 蓝牙 16 位通用唯一标识符 (UUID)

    https://www.bluetooth.com/specifications/assigned-numbers/ 16 位的通用唯一标识符 (UUID)分类 分配类型 分配UUID段 作用 GAT ...

  3. js如何生成唯一标识符UUID

    原文链接:js如何生成唯一标识符UUID 百度百科中对uuid的解释 UUID 是 通用唯一识别码(Universally Unique Identifier)的缩写,是一种软件建构的标准,亦为开放软 ...

  4. python函数的唯一标识_python基础教程Python通用唯一标识符uuid模块使用案例

    1. 背景知识: UUID: 通用唯一标识符 ( Universally Unique Identifier ), 对于所有的UUID它可以保证在空间和时间上的唯一性. 它是通过MAC地址, 时间戳, ...

  5. UUID是128位的全局唯一标识符

    UUID是128位的全局唯一标识符,通常由32字节的字符串表示. 它通过MAC地址.时间戳.命名空间.随机数.伪随机数来保证生成ID的唯一性.UUID主要有五个算法,也就是五种方法来实现:1.uuid ...

  6. uuid:128位的全局唯一标识符(univeral unique identifier)

    转:https://www.cnblogs.com/franknihao/p/7307224.html uuid是128位的全局唯一标识符(univeral unique identifier),通常 ...

  7. linux 文件唯一标识符,详解Linux中获取全球唯一标示符UUID的方法

    UUID(Universally Unique IDentifiers),全球唯一标示符.它是一个标识系统中的存储设备的字符串,使其确定系统中的所有存储设备. 为什么要使用UUID?因为系统自动分配的 ...

  8. iOS设备唯一标识符探讨

    iOS设备唯一标识符探讨 一.现有方案 1.UDID(Unique Device Identifier) 获取方法:[[UIDevice currentDevice] uniqueIdentfier] ...

  9. iOS设备唯一标识符解决方案

    iOS设备唯一标识符解决方案 最近在公司的项目中有记录设备唯一标识符的需求,通过唯一标识符去识别设备的注册类别从而进行角色的切换,在这个过程中查找了一些资料,在此稍作总结,留下一些痕迹,当然能给有同类 ...

最新文章

  1. Java中山脉的绘制---递归方法
  2. 完全分布式部署Hadoop
  3. Python 开发面试题
  4. linux avd 界面,Android Studio创建AVD
  5. 不是keys,记一次因 redis 使用不当导致应用卡死 的过程
  6. 通过Net Manager 配置Oracle 11g本地监听服务(listener service)
  7. “iQOO”怎么读?vivo官宣“发音”视频
  8. ssas表格模型 权限控制_如何在SQL Server 2016中自动执行SSAS表格模型处理
  9. 透过IL看C# (1)——switch语句(上)
  10. 记Angular与Django REST框架的一次合作(2):前端组件化——Angular
  11. 机器学习时会发生什么
  12. epsonl360打印机连接电脑_epsonl360打印机脱机如何解决
  13. Qt6.2.2+libCef Demo
  14. 计算机密码学论文,密码学论文写作范例论文
  15. 天盾linux数据恢复,天盾Linux数据恢复软件
  16. python测试开发实战_自动化平台测试开发:Python测试开发实战
  17. Linux命令:Nginx的启动、停止与重启
  18. [转载]我的时间管理与方法论
  19. Maven加速编译技巧
  20. 最近真的被折磨的很伤

热门文章

  1. 2017蓝桥杯省赛---java---C---9(青蛙跳杯子)
  2. JDBC8.0 URL配置
  3. tar (child): .tgz\r:无法 open: 没有那个文件或目录
  4. import javax.servlet.ServletRequest 关于IDEA javax.servlet.http.HttpServletRequest 不存在 解决方案
  5. 图论——Dijkstra+prim算法涉及到的优先队列(二叉堆)
  6. 局域网物理机怎么访问虚拟机
  7. SQL编程---存储过程和存储函数
  8. java技术专家学习路线图_向Java最佳专家的全球专家学习Java
  9. eclipse开发jsf_在Eclipse上创建JSF / CDI Maven项目
  10. lock.lock_HibernateCascadeType.LOCK陷阱