最近换了工作,因为工作的需要,也正好自己想好好研究一下Java这门牛逼的语言,看了一下ElasticSearch和Lucene的源码,之前从来没有写过也没有看过Java的东西,所以也算是恶补了一下Java吧,由于是从C程序员开始的,所以对这种带虚拟机的语言总有一些偏见,老觉得内存不好控制,所以一直以来都没有怎么碰过Java,最近静下心来好好看了一下Java和相关的源码,除了感觉语言本身啰嗦了一点,还是不错的,但是有一点比较受不了就是基本上用vi很难做Java开发,要是没有IDE的话,感觉写Java有些蛋疼啊。

接下来一段时间会多聊一聊ElasticSearch和lucene相关的,因为最近也在研究这个,先看了Lucene的底层代码,确实写得简洁明了,后面有机会会好好写写这方面的东西。

好了,不闲扯了,今天想说一说搜索引擎或者数据库中索引(主要是倒排索引)的字典结构,一个好的高效的字典结构直接影响到索引的效果,而索引的构建其实并不是完全追求速度,还有磁盘空间,内存空间等各个因素,所以在一个索引系统中,需要权衡各个关系,找到一种适合你当前业务的数据结构进行存储。这样才能发挥索引最大的能效,一般情况下,对于索引来说(主要是倒排索引)的字典来说,有跳跃表,B+树,前缀树,后缀树,自动状态机,哈希表这么几种数据结构,其实只要是一个快速的查找型的数据结构就可以用来做索引的字典。

我们从简单的开始,一个一个来说说,今天先说说跳跃表,跳跃表结构非常非常简单,但是,你真的了解它么?

跳跃表

跳跃表是一种简单,高效的快速查找结构,实现起来成本最小,并且速度也很快,只需要一个图就可以完美的解释跳跃表的样子,而且对于编程人员来说,要实现一个跳跃表看着图就能实现,以下就是跳跃表的结构图,没有什么难度。

跳跃表有几个特点,这种特点对于某些类型的查询是有相当的效率提升的。

它是有序的,跳跃表的特点就是有序的,所以对于一些有序类型的数据,比如整数,日期这种,用跳跃表可以进行范围查找。

在构建跳跃表和查询跳跃的复杂度一致,所以也比较适合于在线的实时索引,可以来一个文档,一边查找就一边知道要如何进行插入操作了。

保存到磁盘和从磁盘载入也比较简单,因为本质上是几个链表,所以保存的时候可以按照数组的方式分别保存几个数组就可以了。

在lucene中,跳跃表并没有用来存储字典,而是用来存储docid链,这里后面我们说lucene底层和Elasticsearch的时候再说具体结构吧,这篇我们仅用来讨论用跳跃表存字典的情况。

对于跳跃表,我们看看有一些什么样的优化方式可以让其更加适应一些场景。优化的话,我们一般从空间和时间两个方面来考虑一个优化,对于空间的话,又分成内存空间优化和磁盘空间优化,当然一般首先考虑内存的优化,对于时间来说,也分成构建时间和查询时间两个方面来优化,空间和时间是两个相互矛盾的优化,具体到实际操作上如何取舍就要看具体的场景了。

空间优化

如果我们的内存空间不够或者说跳跃表存储的序列太长了,那么我们可以把跳跃表的最底层的链表存储在磁盘上,这是一次优化情况,那么检索的时候需要一次到多次磁盘才能检索到数据,相当于用一部分性能来获得更大的数据加载能力。

如果还需要继续优化的话,那么可以把上面几层的节点的数据项变成指针,都指向磁盘的偏移地址上,那么就更加的节省空间了,这样又牺牲了一部分检索性能,因为每一次读取一个节点,不管是不是底层节点,都需要读取一次磁盘来获得数据,对于上面两个优化方式,对应的数据结构的图如下,可以看到这样优化下去,内存的使用量会变得很小了。

但是上图这种存储方式不适合动态的增加或者删除节点,因为一次这样的更新操作需要操作好几次磁盘,并且会导致磁盘上各个节点是不连续的,非常影响效率,所以比较适合那种写入以后就不会变化的跳跃表的情况。

时间优化

最简单的时间优化,那就是把数据全部加载到内存,直接查询速度就快起来了,这个没什么难度,当然也可用用LRU这种缓存算法来折中一下,不消耗太大的空间并且也比直接放磁盘要快一些,或者用mmap让操作系统来帮你做这个事情也可以,不过使用LRU或者mmap的话,编程的难度和数据结构的设计难度就会要变难不少,得看你实现出来的成本了。

还有一种方式就是在查找算法上优化一下,用二分查找代替直接遍历,这也只适合静态的情况,需要修改一下数据结构,将每一层的链表变成数组载入到内存中,这样查找的时候可以通过二分快速的定位到节点上。

跳跃表的层级的增加,一般情况下是通过一个概率来计算是否要增加层级节点的,但是对于一些特殊的类型,其实在构建跳跃表的时候是可以特殊处理的,比如跳跃表用来存储时间序列,那么我们其实可以每当时间过去了一分钟或者一小时或者一天就增加一个层级,假设最小的时间维度是秒,如果一分钟和一小时增加层级的话,那么一天的数据就是三层,而且第一层最多24个节点,第二层最多1440个节点,最底层86400个节点,把第一层和第二层完全载入内存的话应该说没有任何压力,甚至为了查询速度,第一层和第二层节点数固定下来,就是24和1440,这样查询的话都不用遍历链表了,直接可以通过运算就能求出下标然后直接跳到最底层上面来了。这是个典型的用了一定的内存空间来交换出更快的查询时间。

上图中的底层表示秒,第二层表示分钟,第一层表示小时,那个红色的节点表示那一分钟其实是没数据的,为了把节点数固定下来虚拟出来的节点,这样可以提高查询的效率。

优化的取舍

上面两个大类型的优化,其实很多地方是矛盾的,具体取舍的时候就要看你的业务场景了,假设需要用跳跃表来存储你的主键,你的业务场景是更新操作很少,查询操作主要针对其他字段而非主键的话,那么底层存磁盘上,上面几层的数据项也存磁盘上,并且通过LRU或者mmap交换内存和磁盘空间的跳跃表比较适合你。如果用来存储分词后的关键字的话,因为中文分词以后关键词的量级一般在几十万这个级别,那么直接载入内存的话也能接受,所以直接加载到内存的方式可能更适合你。

好了,今天先写这么多,后面还有很多字典结构可以优化的,慢慢来说,正好最近自己也在研究索引的优化,可以留言讨论哈,有说得不对的,随便拍。

如果你觉得不错,欢迎转发给更多人看到,也欢迎关注我的公众号,主要聊聊搜索,推荐,广告技术,还有瞎扯。。文章会在这里首先发出来:)扫描或者搜索微信号XJJ267或者搜索西加加语言就行

java 跳跃表_你真的了解跳跃表吗相关推荐

  1. excel链接隐藏工作表_自动隐藏Excel工作表

    excel链接隐藏工作表 When you build a workbook for other people to use, there might be worksheets that can s ...

  2. excel数据透视表_来自多个工作表的Excel数据透视表更新

    excel数据透视表 If you have similar data on two or more worksheets, you might want to combine that data i ...

  3. mmse评估量表_简易精神状态评价量表(mmse量表) 打印版.doc

    简易精神状态评价量表(mmse量表)15016 简易精神状态评价量表(MMSE) 项目 积分 定向力 (10分) 1.今年是哪一年 现在是什么季节? 现在是几月份? 今天是几号? 今天是星期几? 1 ...

  4. arcengine遍历属性表_记录一次Hive表清理过程

    记录一次Hive表清理过程 背景 时间:2020-07-17 在用spark+hive做数仓的过程中往往会产生很多表,过多历史表会很快消耗掉有限的hdfs资源,并且时间过于久远的表一般不会具有利用价值 ...

  5. mysql修改表结构大表_在线修改MySQL大表的表结构

    由于某个临时需求,需要给在线MySQL的某个超过千万的表增加一个字段.此表在设计之时完全按照需求实现,并没有多余的保留字段. 我们知道在MySQL中如果要执行ALTER TABLE操作,MySQL会通 ...

  6. db2如何锁定一张表_办公必备的保护工作表技巧,你会了么?

    为避免表格数据被非法修改,可以对含有数据的单元格区域进行锁定保护,这需要结合工作表保护才能实现,今天我们就来看看如何保护工作表. 按[Ctrl+A]组合键选择工作表中的所有单元格,在[开始]→[单元格 ...

  7. mysql创建复杂表_数据库_MySQL_复杂的表结构创建

    本例示例下复杂的表结构创建, 大致分为两张表 user表 和 authority(权限表) 每个用户对应一种权限,默认为1(普通用户)  灵感来源:(仿照 进程的优先级, 优先级越高的进程,数字越小) ...

  8. mysql怎么创建信息表_怎么在MySQL创建表

    1打开数据库. 想要创建一个表,你必须先有一个存储表的数据库.你可以在MySQL命令行键入 USE 库名 ,打开数据库. 如果你不记得数据库的名称,可以键入 SHOW DATABASES; ,在MyS ...

  9. mysql inserted表_触发器中的inserted表和deleted表

    触发器语句中使用了两种特殊的表:deleted 表和 inserted 表.Microsoft? SQL Server 2000 自动创建和管理这些表.可以使用这两个临时的驻留内存的表测试某些数据修改 ...

  10. java中引用类型_您真的了解Java中的4种引用类型吗?

    Java中提供了四个级别的引用:SoftReference,FinalReference,WeakReference和PhantomReference.在四种引用类型中,只有FinalReferenc ...

最新文章

  1. ZJU-java进阶笔记 第三周(对象容器)
  2. OpenStack环境下nova resize报ssh连接错误
  3. A Wasserstein Distance[贪心/模拟]
  4. Cloud for Customer里的Shell menu manager
  5. eclipse 取消自动括号补全
  6. 分布式事务 - 梁飞的博客 - ITeye博客
  7. 一本通1594涂抹果酱
  8. python为什么没有真正意义上的多线程_Python之threading多线程 存在的意义
  9. IIS 配置 PHP 环境搭建:web 文件管理器
  10. poj2373 Dividing the Path (单调队列+dp)
  11. 软考中级网络工程师知识目录
  12. Linux查看硬盘挂载
  13. 政务内网、政务外网、政务专网
  14. 一次手机木马的清除记录(手机刷机)
  15. 推荐一些有趣的在线编程游戏
  16. electron对接dll
  17. 颜值爆表!Redis 官方客户端工具,功能强大,且支持可视化监控!
  18. oracle- 审计日志
  19. 对标yaahp的python工具包
  20. matlab求解器是什么,Matlab理解ode求解器

热门文章

  1. 举个栗子!Tableau 技巧(183):快速实现部分类别的排序
  2. Mysql 8.0.27 免安装配置教程(windows)
  3. 移动端html5广告的优势,移动端H5广告的互动类型探析
  4. 计算机应用高级教程,计算机应用高级教程(本科)
  5. 拿到offer后 还应该继续去面试?
  6. Unity打包篇:关于MMD模型贴画在安卓手机上无法显示的问题。
  7. php 姓氏表,php 根据姓氏笔画排序怎么做
  8. Windows驱动程序开发 - 初识驱动
  9. android 文本框,Android实现简单的圆形文本框
  10. 6.4.2.时序逻辑测试模块