1. 理解Term Index
在《Elasticsearch倒排索引(一)简介》中我们讲到,需要一种高效的数据结构对映射关系中的词语集合也就是Term Dictionary进行索引,这种结构叫作Term Index。那么,Term Index是以怎样的形式进行组织的呢?
最简单的理解,Term Index 就像一本词典的目录一样:
A ……………. xx 页
C ……………. xx 页
E ……………. xx 页
但是实际上Term可以是任意的 byte 数组,而不仅仅是英文字符,因此实际的 Term Index 是一棵 Trie Tree:
如图所示是一棵 Trie Tree。这棵 Trie Tree 不会包含所有的 term,它包含的是 term 的一些前缀。通过 Term Index 可以快速地定位到 Term Dictionary 的某个 offset,然后从这个位置再往后顺序查找。再加上一些压缩技术 e.g. FST,Term Index 需要的存储空间非常小,可能只有所有 term 占用空间的几十分之一,因此 Term Index 被完全缓存在内存中是没有问题的。
3.使用了 Term Index 后,ES的倒排索引整体效果如下:
现在我们可以很清楚地知道为什么ES的检索要比MySQL快得多了。在MySQL中,即使建立了索引,但索引是以B+ Tree的形式存储在磁盘上的,在数据量比较大的情况下,想要检索到一条记录,需要若干次的random access的磁盘操作。而ES在Term Dictionary(相当于MySQL中的索引)的基础上,又添加了Term Index这一结构,并且直接以Trie Tree的形式缓存在内存中。在检索一个term时,只需要从Term Index查找到对应的Term Dictionary的block的位置,再到磁盘上去访问对应的数据,大大减少了对磁盘的random access操作的次数。
2. 减小Term Index存储空间 —— FST
1.在前一小节我们说到,Term Index要被直接缓存在内存中以提升查找速度,那么就必然要采用某种结构来压缩Term Index。Term Index在内存中就是以FST(Finite State Transducers,有限状态转换机)的形式来存储的。
2.说到FST,就不得不提FSM(Finite State Machines,有限状态机):表示有限个状态集合以及这些状态之间转移和动作的数学模型。其中一个状态被标记为开始状态,0个或多个状态被标记为final状态。一个FSM同一时间只处于一个状态。
在图中,椭圆形中代表的是状态,而箭头则代表了转移。图中这个FSM代表了对人的一天之内活动的一个抽象,但是未标注开始状态与结束状态。人在同一时间只能处于一种状态,并且从一个状态转移到下一个状态需要一个输入。如果在这个FSM中,人的初始状态为“睡觉”,然后接收的输入依次为:睡醒了、吃饱了、累了、有新电影上映、困了,那么这个人的状态就会依次变化:睡觉 → 吃饭 → 工作 → 逛街 → 睡觉。
- FST是一种类Trie Tree,使用FSM作为数据结构,可以理解为key -> value形式。
- FST具有两大优点:
- 空间占用小:通过对前缀和后缀的重复利用,压缩了存储空间;
- 查询速度快:时间复杂度为O(len(str));
FST的构造过程如下:
我们假设创建以下一组映射:
Key → Value
“cat” → 5
“deep” → 10
“do” → 15
“dog” → 2
“dogs” → 8
由于在处理大数据的情况下,不太可能把整个FST都同时放在内存中,而是要边建立FST边将建好的部分存储在外部文件中以便节省内存,所以经典FST算法要求key必须按字典序从小到大加入到FST中。所以首先要对所有key按照字典序进行排序(本例已按照字典序排列)。
根据此例的输入,可以建立下图所示的FST:
从上图可以看出,每条边有两个属性,Label(key)和Out(value)。注意Out不一定是数字,也有可能是字符串,但要求必须满足叠加性,比如:
2 + 8 = 10;
a + bc = abc;
建立FST之后,就可以很轻松地根据一个key找到对应的value了。例如:查找dog,我们查找的路径为:0 → 4 → 8 → 9。 其权值和为: 2 + 0 + 0 + 0 = 2。其中最后一个零表示 node[9].finalOut = 0。所以“dog”对应的value为2。