理想的日志处理系统应该支持:
- 高吞吐量实时数据摄取:它应该能够批量写入博客,并使它们立即可见。
- 低成本存储:它应该能够存储大量的日志而不需要花费太多的资源。
- 实时文本搜索:它应该能够快速搜索文本。
业界常见的日志处理方案有两种,分别是Elasticsearch和Grafana Loki。
- 倒排索引(Elasticsearch):由于支持全文搜索和高性能而广受欢迎。缺点是实时写入吞吐量低,索引创建资源消耗大。
- 轻量级索引/无索引(Grafana Loki):它与倒排索引相反,它具有高实时写入吞吐量和低存储成本但提供慢查询。
Elasticsearch 和 Grafana Loki 代表了高写入吞吐量、低存储成本和快速查询性能之间的不同权衡。
那么能不能同时拥有高写入吞吐量、低存储成本和快速查询?
Apache Doris(实时分析数据库)开源社区在2.0.0版本引入了倒排索引,并进一步优化,以其1/5的存储空间实现了比Elasticsearch快两倍的日志查询性能。这两个因素结合起来,这是一个好 10 倍的解决方案。
倒排索引Inverted index简介
Elasticsearch 在日志处理方面的一个突出优势是在海量日志中快速搜索关键词。这是由倒排索引启用的。
倒排索引最初用于检索文本中的单词或短语。下图说明了它是如何工作的:
在写入数据时,系统将文本标记为术语,并将这些术语存储在一个发布列表中,该列表将术语映射到它们所在行的 ID。在文本查询中,数据库在posting list中找到关键字(term)对应的行ID,根据行ID获取目标行。通过这样做,系统将不必遍历整个数据集,从而将查询速度提高几个数量级。
在Elasticsearch的倒排索引中,快速检索是以写入速度、写入吞吐量和存储空间为代价的。为什么?首先,tokenization、字典排序、倒排索引创建都是CPU和内存密集型操作。其次,Elasticsearch 必须存储原始数据,倒排索引,以及存储在列中的额外数据副本以用于查询加速。那是三重冗余。
但是没有倒排索引,比如Grafana Loki,查询速度慢,影响用户体验,是日志分析工程师最大的痛点。
Apache Doris 中的倒排索引
索引的实现方式一般有两种:外部索引系统或者内置索引。
外部索引系统:您将外部索引系统连接到您的数据库。在数据摄取中,数据被导入到两个系统中。索引系统创建索引后,会删除自己内部的原始数据。当数据用户输入查询时,索引系统提供相关数据的ID,然后数据库根据ID查找目标数据。
构建外部索引系统更容易,对数据库的干扰更小,但它有一些恼人的缺陷:
- 需要将数据写入两个系统会导致数据不一致和存储冗余。
- 数据库和索引系统的交互会带来开销,所以当目标数据很大时,跨两个系统的查询会很慢。
- 维护两个系统很累。
在Apache Doris中,我们选择了另一种方式。内置倒排索引制作起来比较困难,但是一旦完成,速度更快,更人性化,维护起来也更省事。
我们以非侵入式的方式实现倒排索引:
- Data ingestion & compaction : 当一个段文件被写入 Doris 时,一个倒排索引文件也会被写入。索引文件路径由段ID和索引ID决定。段中的行对应索引中的文档,RowID 和 DocID 也是如此。
- 查询:如果where子句中包含倒排索引的列,系统会在索引文件中查找,返回一个DocID列表,并将DocID列表转换成RowID Bitmap。在 Apache Doris 的 RowID 过滤机制下,只会读取目标行。这就是加速查询的方式。
这种非侵入式的方法将索引文件与数据文件分开,因此您可以对倒排索引进行任何更改,而不必担心影响数据文件本身或其他索引。
倒排索引的优化
1、一般优化
C++ 实现和向量化
与使用 Java 的 Elasticsearch 不同,Apache Doris 在其存储模块、查询执行引擎和倒排索引中实现了 C++。与 Java 相比,C++ 提供更好的性能,允许更容易的矢量化,并且不会产生 JVM GC 开销。我们对 Apache Doris 中倒排索引的每一步都进行了矢量化,例如标记化、索引创建和查询。给大家提供一个视角,在倒排索引中,Apache Doris 以每核 20MB/s 的速度写入数据,是 Elasticsearch(5MB/s)的四倍。
2、列存储和压缩
Apache Lucene 为 Elasticsearch 中的倒排索引奠定了基础。由于 Lucene 本身是为支持文件存储而构建的,因此它以面向行的格式存储数据。
在Apache Doris中,不同列的倒排索引相互隔离,倒排索引文件采用列式存储,便于向量化和数据压缩。
Apache Doris 通过使用 Zstandard 压缩,实现了5:1到10:1 的压缩比,压缩速度更快,空间占用比 GZIP 压缩减少 50%。
3、数字/日期时间列的 BKD 树
Apache Doris 为数字和日期时间列实现 BKD 树。这不仅提高了范围查询的性能,而且比将这些列转换为固定长度的字符串更节省空间。它的其他好处包括:
- 高效的范围查询:能够在numeric和datetime列中快速定位到目标数据范围。
- 更少的存储空间:聚合和压缩相邻的数据块以降低存储成本。
- 支持多维数据:BKD 树具有可扩展性和适应多维数据类型的能力,例如 GEO 点和范围。
除了 BKD 树,我们还进一步优化了对数字和日期时间列的查询。
- 针对低基数场景的优化:我们对低基数场景的压缩算法进行了微调,因此对大量倒排列表进行解压反序列化会消耗更少的CPU资源。
- 预取:对于命中率高的场景,我们采用预取。如果命中率超过某个阈值,Doris 将跳过索引过程并开始数据过滤。
3、针对 OLAP 的定制优化
日志分析是一种简单的查询,不需要高级功能(例如 Apache Lucene 中的相关性评分)。日志处理工具的主要功能是快速查询和低存储成本。因此,在Apache Doris中,我们精简了倒排索引结构来满足OLAP数据库的需求。
- 在数据摄取中,我们防止多个线程将数据写入同一个索引,从而避免锁争用带来的开销。
- 我们丢弃正向索引文件和规范文件以清理存储空间并减少 I/O 开销。
- 我们简化了相关性评分和排名的计算逻辑,以进一步减少开销并提高性能。
鉴于日志是按时间范围分区的,历史日志访问频率较低。我们计划在 Apache Doris 的未来版本中提供更细化和灵活的索引管理:
- 为指定数据分区创建倒排索引:为过去7天的日志等创建索引。
- 删除 指定数据分区的倒排索引:删除一个多月前日志的索引等(以清理索引空间)。
测试
测试Apache Doris 的结果:
- 写入速度:550MB/s,是Elasticsearch的4.2倍
- 压缩比:10:1
- 存储使用:Elasticsearch 的 20%
- 响应时间:Elasticsearch 的 43%
Apache Doris VS ClickHouse
由于 ClickHouse 在 v23.1 中推出了倒排索引作为实验特性,我们使用 ClickHouse博客中描述的相同数据集和 SQL 对 Apache Doris 进行了测试,并比较了两者在相同测试资源、案例和工具下的性能。
结果:Apache Doris在三个查询中分别比 ClickHouse 快4.7 倍、12 倍、18.5 倍。
用法和示例
数据集:来自 Hacker News 的一百万条评论记录
第一步:建表时给数据表指定倒排索引。
参数:
- INDEX idx_comment( comment):为“评论”列创建一个名为“idx_comment”评论的索引
- USING INVERTED:为表指定倒排索引
- PROPERTIES(“parser” = “english”): 指定分词语言为英语
CREATE TABLE hackernews_1m |
注意:你可以通过ADD INDEX idx_comment ON hackernews_1m(comment) USING INVERTED PROPERTIES("parser" = "english")向现有表添加索引。与智能索引和二级索引不同,倒置索引的创建只涉及到对评论列的读取,所以它可以快得多。
第二步:用MATCH_ALL检索评论栏中的 "OLAP "和 "OLTP "两个词。这里的响应时间是用like硬匹配时的1/10。(随着数据量的增加,性能差距也在扩大)。
mysql> SELECT count() FROM hackernews_1m WHERE comment LIKE '%OLAP%' AND comment LIKE '%OLTP%'; |
总结
倒置或倒排索引在日志分析中是一种有用的技术,但它的瓶颈在于数据写入速度慢,存储成本巨大。Apache Doris一直在努力为数据工程师解决这个问题。它的成本效益比市场上常见的解决方案高出10倍,其原因是它为倒置索引提供了OLAP定制的优化,并由其列式存储引擎、大规模并行处理框架、矢量查询引擎和基于成本的优化器支持。