用了Elasticsearch后,查询起飞了( 三 )


实际上ES会做的更加精细:
用了Elasticsearch后,查询起飞了

它会把所有的文档分成很多个block , 每个block正好包含256个文档 , 然后单独对每个文档进行增量编码 。
计算出存储这个block里面所有文档最多需要多少位来保存每个id , 并且把这个位数作为头信息(header)放在每个block的前面 。 这个技术叫FrameofReference 。
上图也是来自于ES官方博客中的一个示例(假设每个block只有3个文件而不是256) 。
FOR的步骤可以总结为:
用了Elasticsearch后,查询起飞了

进过最后的位压缩之后 , 整型数组的类型从固定大小(8 , 16 , 32 , 64位)4种类型 , 扩展到了[1-64]位共64种类型 。
通过以上的方式可以极大的节省postinglist的空间消耗 , 提高查询性能 。 不过ES为了提高filter过滤器查询的性能 , 还做了更多的工作 , 那就是缓存 。
RoaringBitmaps(forfiltercache):在ES中 , 可以使用filters来优化查询 , filter查询只处理文档是否匹配与否 , 不涉及文档评分操作 , 查询的结果可以被缓存 。
对于filter查询 , es提供了filtercache这种特殊的缓存 , filtercache用来存储filters得到的结果集 。
缓存filters不需要太多的内存 , 它只保留一种信息 , 即哪些文档与filter相匹配 。 同时它可以由其它的查询复用 , 极大地提升了查询的性能 。
我们上面提到的FrameOfReference压缩算法对于postingslist来说效果很好 , 但对于需要存储在内存中的filtercache等不太合适 。
filtercache会存储那些经常使用的数据 , 针对filter的缓存就是为了加速处理效率 , 对压缩算法要求更高 。
对于这类postingslist , ES采用不一样的压缩方式 。 那么让我们一步步来 。 首先我们知道postingslist是Integer数组 , 具有压缩空间 。
假设有这么一个数组 , 我们第一个压缩的思路是什么?用位的方式来表示 , 每个文档对应其中的一位 , 也就是我们常说的位图 , bitmap 。
它经常被作为索引用在数据库、查询引擎和搜索引擎中 , 并且位操作(如and求交集、or求并集)之间可以并行 , 效率更好 。
用了Elasticsearch后,查询起飞了

但是 , 位图有个很明显的缺点 , 不管业务中实际的元素基数有多少 , 它占用的内存空间都恒定不变 。
也就是说不适用于稀疏存储 。 业内对于稀疏位图也有很多成熟的压缩方案 , lucene采用的就是roaringbitmaps 。
我这里用简单的方式描述一下这个压缩过程是怎么样:
用了Elasticsearch后,查询起飞了

将docid拆成高16位 , 低16位 。 对高位进行聚合(以高位做key , value为有相同高位的所有低位数组) , 根据低位的数据量(不同高位聚合出的低位数组长度不相同) , 使用不同的container(数据结构)存储 。
len<4096ArrayContainer直接存值len>=4096BitmapContainer使用bitmap存储分界线的来源:value的最大总数是为2^16=65536.假设以bitmap方式存储需要65536bit=8kb , 而直接存值的方式 , 一个值2byte , 4K个总共需要2byte*4K=8kb 。
所以当value总量<4k时 , 使用直接存值的方式更节省空间 。
用了Elasticsearch后,查询起飞了

空间压缩主要体现在:
高位聚合(假设数据中有100w个高位相同的值 , 原先需要100w2byte , 现在只要12byte)低位压缩缺点就在于位操作的速度相对于原生的bitmap会有影响 。 这就是trade-off呀 。 平衡的艺术 。
②联合查询