ES会默认将所有的输入内容当作字符串来理解,对于字段类型是keyword或者text的数据比较友好。但是如果输入的类型是数字,ES还会把数字当作字符串吗?排序问题还有范围查询问题怎么解决呢
Elasticsearch专为字符串搜索而生,在建立索引的时候针对字符串进行了非常多的优化,在对字符串进行准确匹配或者前缀匹配等匹配的时候效率是很高的。ES底层把所有的数据都会当成字符串,其中就包括数字——所有的数字在ES底层都是会以字符串的形式保存。
在MySQL中,索引是以B+树的形式存储,每次查询某一个数字都要在树中把整个数字和分隔节点进行比较操作,直到找到最后的目标数据节点。
可以看到直接使用int的本身的结构来存储的优势就是直接比较大小的效率非常高(空间消耗小也是另外一个优势),但是如果进行范围查询,就会有问题了。比如在MySQL的组合查询中如果出现范围查询,那么很有可能出现范围查询后面的索引是不生效的(具体的MySQL的组合查询的原理可以在网上看看),也就是说范围查询可能会降低查询性能;在编程语言中的集合的范围查询就只能遍历所有的元素,一一比较大小,没有优化。
ES的出现为解决范围查询提供了一个新的思路——为不同精度范围内的数据直接建立索引,把符合范围查询要求的数据聚合到一个索引上面,在搜索的时候把大的搜索范围拆分成很多小的范围索引,直接用term搜索就可以找到符合要求的所有文档。
数字直接变成字符串的问题
ES并没有直接把数字变成字符串,也没有对每个数字建立简单的索引,因为这两种做法可能会带来一些问题。
字符串比较
首先最大的问题是数字变成字符串之后如何进行比较,如果直接是把十进制的数字变成字符串,排序按照字典序(lexicographic)比较(默认所有的term都是按照字典序比较大小),会有不同位数比较的问题。比如搜索[423,642]内的文档,5也会被算在内,因为字典序”5”比”423”大,比”642”小。
这个问题的一个解决方案是在每个数字变成字符串的时候在前面填充0,把5变成005,这样就能正确比较大小了,这也是旧版本的ES采用的解决方案。但是每次把int转化成string的时候要填充多少个0呢?太多了占空间,太少了又可能因为数字太长影响比较,比如最多只填充2个0,对于1000以下的数字没有问题,当数字大于1000了,个位数填充2个0就不够用了
ES是怎么把数字变成字符串
先来解决第一个问题,数字怎么变成字符串。
十进制的数字有填充问题,如果变成了二进制,再进行词典序比较,不就没有问题了吗?Perfect,似乎问题完美的解决了。
二进制的int保证是32位,对于正数和正数的比较或者负数和负数的比较是没有问题的,可是正数和负数的比较就不行了。正数的最高位是0,负数的最高位是1,直接比较,负数永远大于正数。这个时候ES采用的方法是把正数最高位变成1,负数最高位变成0,这样正数用于大于负数,问题就解决了。
数字的索引是什么样子
上面说到的另外一个问题是查询term数量太多的问题,解决方案就是用空间换时间,通过前缀聚合部分的term来达到。
这里的聚合的实现方式是采用trie的数据结构,比如445、446和448这个三个term,可以聚合到44这个term的下面,节点44包含的文档的id列表应该是所有子节点的并集,这样原先需要的11个term就可以减少2个。同理对于其他的term也进行合并,合并之后[423,642]查询就只需要6个term,效率提高了一倍!
然而聚合也是要讲道理的,把445、446和448聚合到44以及把44聚合到4相当于是把数字除以10,精度就是10。但是并不是一直都希望这个精度是10,也可以设置为100(精度相对应的降低,节约索引空间)等等。ES提供了precisionStep来定制化这个精度,不过不是针对十进制,而是二进制的位数。比如precisionStep设置为4,那么在二进制位里面每隔4位(相当于十进制的16)就建立一个前缀聚合索引。
Elasticsearch对于数字类型数据的索引和搜索不同于传统的MySQL或者Java等编程语言,采用了独特的字符串存储以及Trie数据结构保存索引的方式。
ES先将输入的数字进行预处理,把float和double分别映射成int和long,原来是int和long类型的则保持不变
然后把输入的整型数字切分成许多组由最长7位二进制数字组成的二进制串,每组二进制数字都是一个Unicode字符,整体连起来变成一个Unicode字符串
接下来根据precisionStep把这个字符串数字分词成很多term,并附上前缀shift
根据这些term建立索引词典,词典的结构类似于一个trie
范围查询的时候根据trie把所谓的范围区间划分成离散的term字符串,这些term指向的文档的并集就是范围查询的结果
ES使用的搜索库Lucene在6.0版本以及以后为了解决多维空间位置搜索问题,改用新的数据结构——BKD树来实现位置搜索,带来了很大的性能提升。开发者发现这种实现也能用于一维数据搜索,于是用新的数据结构代替了现在的字符串的实现
https://www.cnblogs.com/youngdeng/p/12868817.html