转自:http://www.cnblogs.com/LBSer/p/4119841.html

1 lucene字典

使用lucene进行查询不可避免都会使用到其提供的字典功能,即根据给定的term找到该term所对应的倒排文档id列表等信息。实际上lucene索引文件后缀名为tim和tip的文件实现的就是lucene的字典功能。

怎么实现一个字典呢?我们马上想到排序数组,即term字典是一个已经按字母顺序排序好的数组,数组每一项存放着term和对应的倒排文档id列表。每次载入索引的时候只要将term数组载入内存,通过二分查找即可。这种方法查询时间复杂度为Log(N),N指的是term数目,占用的空间大小是O(N*str(term))。排序数组的缺点是消耗内存,即需要完整存储每一个term,当term数目多达上千万时,占用的内存将不可接受。

2 常用字典数据结构

很多数据结构均能完成字典功能,总结如下。

数据结构 优缺点
排序列表Array/List 使用二分法查找,不平衡
HashMap/TreeMap 性能高,内存消耗大,几乎是原始数据的三倍
Skip List 跳跃表,可快速查找词语,在lucene、redis、Hbase等均有实现。相对于TreeMap等结构,特别适合高并发场景(Skip List介绍
Trie 适合英文词典,如果系统中存在大量字符串且这些字符串基本没有公共前缀,则相应的trie树将非常消耗内存(数据结构之trie树
Double Array Trie 适合做中文词典,内存占用小,很多分词工具均采用此种算法(深入双数组Trie
Ternary Search Tree 三叉树,每一个node有3个节点,兼具省空间和查询快的优点(Ternary Search Tree
Finite State Transducers (FST) 一种有限状态转移机,Lucene 4有开源实现,并大量使用

3 FST原理简析

lucene从4开始大量使用的数据结构是FST(Finite State Transducer)。FST有两个优点:1)空间占用小。通过对词典中单词前缀和后缀的重复利用,压缩了存储空间;2)查询速度快。O(len(str))的查询时间复杂度。

下面简单描述下FST的构造过程(工具演示:http://examples.mikemccandless.com/fst.py?terms=&cmd=Build+it%21)。我们对“cat”、 “deep”、 “do”、 “dog” 、“dogs”这5个单词进行插入构建FST(注:必须已排序)。

1)插入“cat”

插入cat,每个字母形成一条边,其中t边指向终点。

2)插入“deep”

与前一个单词“cat”进行最大前缀匹配,发现没有匹配则直接插入,P边指向终点。

3)插入“do”

与前一个单词“deep”进行最大前缀匹配,发现是d,则在d边后增加新边o,o边指向终点。

4)插入“dog”

与前一个单词“do”进行最大前缀匹配,发现是do,则在o边后增加新边g,g边指向终点。

5)插入“dogs”

与前一个单词“dog”进行最大前缀匹配,发现是dog,则在g后增加新边s,s边指向终点。

最终我们得到了如上一个有向无环图。利用该结构可以很方便的进行查询,如给定一个term “dog”,我们可以通过上述结构很方便的查询存不存在,甚至我们在构建过程中可以将单词与某一数字、单词进行关联,从而实现key-value的映射。

4 FST使用与性能评测

我们可以将FST当做Key-Value数据结构来进行使用,特别在对内存开销要求少的应用场景。Lucene已经为我们提供了开源的FST工具,下面的代码是使用说明。

 1 public static void main(String[] args) {
2 try {
3 String inputValues[] = {"cat", "deep", "do", "dog", "dogs"};
4 long outputValues[] = {5, 7, 17, 18, 21};
5 PositiveIntOutputs outputs = PositiveIntOutputs.getSingleton(true);
6 Builder<Long> builder = new Builder<Long>(FST.INPUT_TYPE.BYTE1, outputs);
7 BytesRef scratchBytes = new BytesRef();
8 IntsRef scratchInts = new IntsRef();
9 for (int i = 0; i < inputValues.length; i++) {
10 scratchBytes.copyChars(inputValues[i]);
11 builder.add(Util.toIntsRef(scratchBytes, scratchInts), outputValues[i]);
12 }
13 FST<Long> fst = builder.finish();
14 Long value = Util.get(fst, new BytesRef("dog"));
15 System.out.println(value); // 18
16 } catch (Exception e) {
17 ;
18 }
19 }

FST压缩率一般在3倍~20倍之间,相对于TreeMap/HashMap的膨胀3倍,内存节省就有9倍到60倍!(摘自:把自动机用作 Key-Value 存储),那FST在性能方面真的能满足要求吗?

下面是我在苹果笔记本(i7处理器)进行的简单测试,性能虽不如TreeMap和HashMap,但也算良好,能够满足大部分应用的需求。

参考文献

http://sbp810050504.blog.51cto.com/2799422/1361551

http://blog.sina.com.cn/s/blog_4bec92980101hvdd.html

http://blog.mikemccandless.com/2013/06/build-your-own-finite-state-transducer.html

http://examples.mikemccandless.com/fst.py?terms=mop%2F0%0D%0Amoth%2F1%0D%0Apop%2F2%0D%0Astar%2F3%0D%0Astop%2F4%0D%0Atop%2F5%0D%0Atqqq%2F6&cmd=Build+it%21

lucene字典实现原理——FST的更多相关文章

  1. lucene字典实现原理

    http://www.cnblogs.com/LBSer/p/4119841.html 1 lucene字典 使用lucene进行查询不可避免都会使用到其提供的字典功能,即根据给定的term找到该te ...

  2. lucene字典实现原理(转)

    原文:https://www.cnblogs.com/LBSer/p/4119841.html 1 lucene字典 使用lucene进行查询不可避免都会使用到其提供的字典功能,即根据给定的term找 ...

  3. Elasticsearch Lucene 数据写入原理 | ES 核心篇

    前言 最近 TL 分享了下 <Elasticsearch基础整理>https://www.jianshu.com/p/e8226138485d ,蹭着这个机会.写个小文巩固下,本文主要讲 ...

  4. iOS 字典实现原理

    在目前的开发中,NSDictionary是经常被使用,不过很少人会研究字典NSDictionary底层的实现,下面我们来一起看一下NSDictionary的实现原理. 一.字典原理 字典通过使用- ( ...

  5. 03.什么是Lucene全文检索的原理01

    全文检索的原理:查询速度快,精准度高,可以根据相关度进行排序.它的原理是:先把内容分词,分词之后建索引. Lucene是apache下的一个开放源代码的全文检索引擎工具包. 提供了完整的查询引擎和索引 ...

  6. 42 (OC)* 字典实现原理--哈希原理

    一.NSDictionary使用原理 1.NSDictionary(字典)是使用 hash表来实现key和value之间的映射和存储的,hash函数设计的好坏影响着数据的查找访问效率. - (void ...

  7. Elasticsearch原理入门

    这是一篇拼接贴,我是缝合怪 项目中用到了es,使用方法是挺简单的,封装了基本api以后,把查询条件封装一下传给client执行就可,但是光使用比较肤浅,研究一下原理和本质,更利于以后开发使用 扫盲贴 ...

  8. lucene索引文件大小优化小结

    http://www.cnblogs.com/LBSer/p/4068864.html 随着业务快速发展,基于lucene的索引文件zip压缩后也接近了GB量级,而保持索引文件大小为一个可以接受的范围 ...

  9. lucene join解决父子关系索引

    http://www.cnblogs.com/LBSer/p/4417074.html 1 背景 以商家(Poi)维度来展示各种服务(比如团购(deal).直连)正变得越来越流行(图1a), 比如目前 ...

随机推荐

  1. Linux基础※※※※访问Windows共享文件夹

    参考Linux公社链接:http://www.linuxidc.com/Linux/2014-06/103749.htm 实际上,可以直接用sambaclient程序访问共享资源. 列出共享主机的列表 ...

  2. git fork

    http://help.github.com/fork-a-repo/ 概要: 克隆别人的代码库到自己的项目中,可以作为子模块的形式使用,或二次开发 操作流程: 在开源项目中点击fork按钮,稍等一会 ...

  3. C++大数类模板

    友情提示:使用该模板的注意了,在大数减法里有一个小错误,导致减法可能会出错 // 原来的写法,将t1.len错写成了len ] == && t1.len > ) { t1.len ...

  4. DDL和DML的定义和区别

    DML(Data Manipulation Language)数据操纵语言: 适用范围:对数据库中的数据进行一些简单操作,如insert,delete,update,select等. DDL(Data ...

  5. Linux命令之乐--awk

    1.脚本参数传值 #/bin/bash awk '"} {if(($1==a)) print $2;}' /etc/hosts 执行结果:

  6. POJ 2385 Apple Catching

    比起之前一直在刷的背包题,这道题可以算是最纯粹的dp了,写下简单题解. 题意是说cows在1树和2树下来回移动取苹果,有移动次数限制,问最后能拿到的最多苹果数,含有最优子结构性质,大致的状态转移也不难 ...

  7. 发现easyui-accordion一个bug,在ie6、ie7不兼容性问题

    当设置全局css文件单元格样式为下面时 td{ word-break: break-all; word-wrap: break-word;} easyui-accordion在ie6.ie7上面会出现 ...

  8. 【linux命令】grep

    1.作用Linux系统中grep命令是一种强大的文本搜索工具,它能使用正则表达式搜索文本,并把匹 配的行打印出来.grep全称是Global Regular Expression Print,表示全局 ...

  9. hdu5787(数位dp)

    基础的数位dp,才发现今天才终于彻底搞懂了数位dp... // // main.cpp // hdu5787.1 // // Created by New_Life on 16/8/10. // Co ...

  10. 最大堆的插入/删除/调整/排序操作(图解+程序)(JAVA)

    堆有最大堆和最小堆之分,最大堆就是每个节点的值都>=其左右孩子(如果有的话)值的完全二叉树.最小堆便是每个节点的值都<=其左右孩子值的完全二叉树. 设有n个元素的序列{k1,k2,..., ...