ES为什么那么牛——Elasticsearch存储解析

文章目录

  • Elasticsearch
    • Elasticsearch 架构
      • 文档型数据库
      • 索引结构
        • 时间复杂度
        • 空间复杂度
    • Q&A
    • 附录

本文仅针对ES存储部分进行解析,其他部分请关注后续博文,欢迎技术交流!
了解更多,请关注 公众号 “ code 杂坛 “!

Elasticsearch

监控全家桶 ELK/EFK,其中的 E 就是 ES(Elasticsearch)。Elasticsearch「开发语言 Java」最早于 2010 年 2 月发布,是一款分布式可扩展的实时检索分析组件。它不仅是一款文档型数据库,也是基于全文搜索引擎 Apache Lucene 之上的一款检索引擎。

Elasticsearch 架构

ES为什么那么牛——Elasticsearch存储解析_第1张图片

文档型数据库

从存储单元角度讲,和 Graphite 的原生存储 Whisper 一致,都属于文档型数据库,但存储机制及数据结构迥然不同。

在 Elasticsearch 中,每条数据是一个以 Json 格式序列化的文档。

{
    "scene" : "wax",
    "product" :  "mainfeed",
    "status": "200"
}
{
    "scene" : "wax",
    "product" :  "mainfeed",
    "status": "404"
}
{
    "scene" : "wax",
    "product" :  "mainfeed",
    "status": "502"
}

ES为什么那么牛——Elasticsearch存储解析_第2张图片
每个文档都归属于某个 Type,相互关联的多 Type 属于某个 Index。
类比于常见关系型数据库的话,对应关系:

Doc <=> Row;
Type <=> Table;
Index <=> DataBase;

每个数据项最终和索引关联,这样的关联关系设计的初衷就是为了,利用索引能够获得优秀的检索能力。

那么问题来了,检索能力优秀依赖的索引是怎样设计的呢/?

索引结构

针对上述的 Case,会建立如下索引结构:

{
    "httpCode":{
        "properties":{
            "scene":{
                "type":"keyword"
            },
            "product":{
                "type":"keyword"
            },
            "status":{
                "type":"keyword"
            }
        }
    }
}

Elasticsearch 会根据这个索引结构建立倒排索引,如下:
ES为什么那么牛——Elasticsearch存储解析_第3张图片
Flasticsearch 会对每个列进行转换,形成一个映射表 Row Index。当我们进行查找“status=200“的时候,会根据 Row Data 全表便利查找“status”,之后同样找到“200”的 Posting List,从而定位到目标数据的文档 ID,进行 I/O 操作。

大家看到这里,我们需要思考一下,上述过程存在些性能问题:

操作的时间复杂度和存储占用率,都会随数据规模的膨胀而线性增长;
当数据规模极大时,性能存在问题。

时间复杂度

1、当 Term Data 数量规模极大时,全表遍历的时间复杂度为 N,并非最优!

了解数据结构的同学知道,场景下查找算法中最佳的是对数据进行排序之后,使用二分查找,时间复杂度可为 LogN。「Hash 算法不适合做索引<不支持顺序、联合、冲突等原因,收藏关注博主,后续开贴讲解,本文不做讲解>,不符合设定,故排除」
ES为什么那么牛——Elasticsearch存储解析_第4张图片
故对 Term 进行排序,构成 Term Dictionary 结构,进行读取优化。

这样就是最优了嘛/?

不,这样的时间复杂度和关系型数据库 Mysql 中的索引 B+树一致 LogN,且都是在磁盘中 I/O,平均一次磁盘的随机读操作耗时在 10ms 左右;所以为了减少 I/O 操作,充分利用内存的速度优势,把 Term 关系放置在内存中,目标位置定位由内存完成。

这样问题又来了,内存虽然速度完胜,但空间大小是被动的,怎么把随数据规模线性增长的 Term 放置在内存中呢/?

Elasticsearch 引入了 Term Index。Term Index 是 Term 的索引,通俗的讲,就是字典中查找汉字用的首字母目录。道理是这样,举个例子:

要找“言”字,首先找 Y,之后在找 A,最后找到 N 的位置,那么就完成检索。
按照这个道理,如果 Term 全部都是英文单词,那么我们只需包含 26 个字母的 Tire 树就行了,所需存储空间大大缩小。

Term Index 这棵树不会包含所有的 Term,它包含的是 Term 的一些前缀。通过 Term Index 可以快速地定位到 Term Dictionary 的某个 offset,然后从这个位置再往后顺序查找。
了解更多,请关注 公众号 “ code 杂坛 “!
检索速度完胜关系型数据库 Mysql。

2、Posting List 为有序数组,数据结构满足最优算法<二分、跳表…>,无需额外进行调整

空间复杂度

1、Term Data/Index 空间复杂度会随数据规模线性增长!

内存中的 Term Index 使用 FST 压缩技术(搜索 Lucene Finite State Transducers) Term Index 的尺寸可以只有所有 Term 的尺寸的几十分之一,使得用内存缓存整个 Term Index 变成可能。

磁盘中的 Term Data 以分 block 的方式保存的,一个 block 内部利用公共前缀压缩,比如都是 S 开头的单词就可以把 Abc… 省去。这样 Term Dictionary 可以比 b-tree 更节约磁盘空间。

2、Posting List 为数组空间复杂度会随数据规模线性增长!

对 Druid 和倒排索引了解到同学,可能想到用 Bitmap 来压缩存储体积<0/1 标识数据有无>,如下:
ES为什么那么牛——Elasticsearch存储解析_第5张图片
但这种压缩方式只是在数据类型上进行了简化,复杂度依旧是线性。Elasticsearch 引入了 Roaring Bitmap结构,对数据膨胀做了结构性的优化。
了解更多,请关注 公众号 “ code 杂坛 “!
将划分为 2^16 个桶,每一个桶有一个 Container 来存放一个数值 k 的低 16 位;

在存储和查询数值的时候,我们将一个数值 k 划分为高 16 位(k % 2^16)和低 16 位(k mod 2^16)〈即转化为 16 进制之后,对半拆,前部分为高 16 位,后部分为低 16 位〉,取高 16 位找到对应的桶,然后在低 16 位存放在相应的 Container 中;

RBM Container 使用两种容器结构:

Array Container 和 Bitmap Container。

Array Container 存放稀疏的数据,Bitmap Container 存放稠密的数据。

即,若一个 Container 里面的 Integer 数量小于 4096,就用 Short 类型的有序数组来存储值。若大于 4096,就用 Bitmap 来存储值。
了解更多,请关注 公众号 “ code 杂坛 “!
那么问题又来了,为啥要分为 2^16 个桶/?

2^16 是 2 字节能容纳的最大量。在 32-bit 的范围 ([0, n))内,有十亿级规模数据,转化为二进制拆分两部分,一部分为 16bit,即 2 字节,short 类型。

为什么要用 4096 做不同 Container 的分割呢/?

假设在 bitmap container 中,16bit,可容纳 2^16 =65536 个数值,65536/8=8192byte~=8kb;
若用 short 类型存储,8192b/2b=4096byte;
也就是,当数值小于 4096 时,用 short 更省空间,根本不需要开个 8kb 的 bitmap contrainer。

Q&A

1、Elasticsearch 每个数据项是一个文档,当数据项量级膨胀时,跟随的文档数量及相应的 I/O 也会增加,怎么搞下这个问题呢/?是不是存在瓶颈/?

常见的压缩存储时间序列的方式是压缩。

一种是把多个数据点合并成一行
Opentsdb 支持海量数据就是定期把多行数据合并成一行。

另一种把多个数据点聚合为单点
Graphite 支持高精度数据通过某种规则聚合为低精度「这里的精度指时序粒度」,降低存储、I/O 成本。

Elasticsearch 提供这样一个嵌套文件的机制,可以把一段时间隔内的文档数据打包嵌套至一个文档内;且 Elasticsearch 支持分布式部署,亿级数据量轻轻松松。
了解更多,请关注 公众号 “ code 杂坛 “!
具体瓶颈的话,不是在磁盘存储的部分「磁盘从来不是什么瓶颈,除非超出部门预算…」
而是内存的部分,由于 FST( Finite State Transducer),在海量数据下,Elasticsearch 依赖于 Java 的 JVM,内存堆会爆炸…

2、如何解决Elasticsearch内存不足的痛点呢/?

请关注后续博文!

3、在做监控系统时,必须是ELK/EFK套餐吗/?如何进行技术选型呢/?

请关注后续博文!

附录

思索一件事的时候,走进去后记得出来回头再看看,实在不行退两步再看……
了解更多,请关注 公众号 “ code 杂坛 “!

你可能感兴趣的:(性能优化,架构,分布式开发,ELK,es,Lucene,索引)