對(duì)比Elasticsearch,使用Doris進(jìn)行高效日志分析
作為公司數(shù)據(jù)資產(chǎn)的重要組成部分,日志在系統(tǒng)的可觀察性、網(wǎng)絡(luò)安全和數(shù)據(jù)分析方面扮演著關(guān)鍵角色。日志記錄是故障排除的首選工具,也是提升系統(tǒng)安全性的重要參考。日志還是一個(gè)寶貴的數(shù)據(jù)源,通過(guò)對(duì)其進(jìn)行分析,可以獲取指導(dǎo)業(yè)務(wù)增長(zhǎng)的有價(jià)值信息。
日志是計(jì)算機(jī)系統(tǒng)中事件的順序記錄。一個(gè)理想的日志分析系統(tǒng)應(yīng)該是:
- 具備無(wú)模式支持。 原始日志是非結(jié)構(gòu)化的自由文本,基本無(wú)法直接進(jìn)行聚合和計(jì)算,因此,在將日志用于數(shù)據(jù)庫(kù)或數(shù)據(jù)倉(cāng)庫(kù)進(jìn)行分析之前,需要將其轉(zhuǎn)化為結(jié)構(gòu)化的表格形式(這個(gè)過(guò)程稱為“ETL”)。如果發(fā)生日志模式更改,需要在ETL流程和結(jié)構(gòu)化表中進(jìn)行一系列復(fù)雜的調(diào)整。為了應(yīng)對(duì)此情況,可以使用半結(jié)構(gòu)化日志,主要采用JSON格式進(jìn)行記錄。在這種格式的日志中,可以相對(duì)容易地添加或刪除字段,而日志存儲(chǔ)系統(tǒng)會(huì)相應(yīng)地調(diào)整其模式。
- 低成本。 日志數(shù)據(jù)龐大且持續(xù)不斷生成。一個(gè)相當(dāng)大的公司每年會(huì)產(chǎn)生10~100 TB的日志數(shù)據(jù)?;跇I(yè)務(wù)或合規(guī)要求,應(yīng)該保留半年或更長(zhǎng)時(shí)間的日志。這意味著需要存儲(chǔ)以PB為單位的日志大小,成本相當(dāng)可觀。
- 具備實(shí)時(shí)處理能力。 日志應(yīng)該實(shí)時(shí)寫入,否則工程師將無(wú)法及時(shí)捕捉故障排查和安全追蹤中的最新事件。此外,良好的日志系統(tǒng)應(yīng)該提供全文搜索功能,并能快速響應(yīng)交互式查詢。
1 基于Elasticsearch的日志分析解決方案
數(shù)據(jù)行業(yè)內(nèi)常用的日志處理解決方案是ELK技術(shù)棧:Elasticsearch、Logstash和Kibana。該流程可分為五個(gè)模塊:
- 日志收集:Filebeat收集本地日志文件并將其寫入Kafka消息隊(duì)列。
- 日志傳輸:Kafka消息隊(duì)列收集和緩存日志。
- 日志轉(zhuǎn)換:Logstash過(guò)濾和轉(zhuǎn)換Kafka中的日志數(shù)據(jù)。
- 日志存儲(chǔ):Logstash以JSON格式將日志寫入Elasticsearch進(jìn)行存儲(chǔ)。
- 日志查詢:用戶通過(guò)Kibana可視化搜索日志或通過(guò)Elasticsearch DSL API發(fā)送查詢請(qǐng)求。
圖片
ELK堆棧具有優(yōu)秀的實(shí)時(shí)處理能力,但也存在一些問(wèn)題。
1.1 缺乏無(wú)模式支持
Elasticsearch中的索引映射定義了表的結(jié)構(gòu),包括字段名稱、數(shù)據(jù)類型以及是否啟用索引創(chuàng)建。
圖片
Elasticsearch還擁有自動(dòng)根據(jù)輸入的JSON數(shù)據(jù)添加字段到映射的動(dòng)態(tài)映射機(jī)制。這提供了某種程度的無(wú)模式支持,但這還不夠,因?yàn)椋?/p>
- 動(dòng)態(tài)映射在處理臟數(shù)據(jù)時(shí)經(jīng)常會(huì)創(chuàng)建過(guò)多的字段,從而中斷整個(gè)系統(tǒng)的運(yùn)行。
- 字段的數(shù)據(jù)類型是不可變的。為了確保兼容性,用戶通常將數(shù)據(jù)類型配置為"文本",但這會(huì)導(dǎo)致比二進(jìn)制數(shù)據(jù)類型(如整數(shù))慢得多的查詢性能。
- 字段的索引也是不可變的。用戶無(wú)法為特定字段添加或刪除索引,因此經(jīng)常為所有字段創(chuàng)建索引,以便在查詢中方便地進(jìn)行數(shù)據(jù)過(guò)濾。但是太多的索引需要額外的存儲(chǔ)空間,并減慢數(shù)據(jù)攝入速度。
1.2 分析能力不足
Elasticsearch擁有獨(dú)特的領(lǐng)域特定語(yǔ)言(DSL),與大多數(shù)數(shù)據(jù)工程師和分析師熟悉的技術(shù)棧非常不同,所以存在陡峭的學(xué)習(xí)曲線。此外,Elasticsearch相對(duì)封閉的生態(tài)系統(tǒng),在與BI工具集成方面會(huì)遇到一些阻力。最重要的是,Elasticsearch僅支持單表分析,滯后于現(xiàn)代OLAP對(duì)多表連接、子查詢和視圖的需求。
圖片
1.3 高成本和低穩(wěn)定性
Elasticsearch用戶一直在抱怨計(jì)算和存儲(chǔ)成本。根本原因在于Elasticsearch的工作方式。
- 計(jì)算成本:在數(shù)據(jù)寫入過(guò)程中,Elasticsearch還執(zhí)行計(jì)算密集型操作,包括倒排索引的創(chuàng)建、分詞和倒排索引的排序。在這些情況下,數(shù)據(jù)以每個(gè)核心約2MB/s的速度寫入Elasticsearch。當(dāng)CPU資源緊張時(shí),數(shù)據(jù)寫入需求往往會(huì)在高峰時(shí)段被拒絕,進(jìn)一步導(dǎo)致更高的延遲。
- 存儲(chǔ)成本:為了加快檢索速度,Elasticsearch存儲(chǔ)原始數(shù)據(jù)的正排索引、倒排索引和文檔值,消耗了更多的存儲(chǔ)空間。單個(gè)數(shù)據(jù)副本的壓縮比僅為1.5:1,而大多數(shù)日志解決方案的壓縮比為5:1。
隨著數(shù)據(jù)量和集群規(guī)模的增長(zhǎng),保持穩(wěn)定性會(huì)成為另一個(gè)問(wèn)題:
- 在數(shù)據(jù)寫入高峰期:集群在數(shù)據(jù)寫入高峰期容易超載。
- 在查詢期間:由于所有查詢都在內(nèi)存中處理,大型查詢很容易導(dǎo)致JVM OOM(內(nèi)存溢出)。
- 恢復(fù)緩慢:對(duì)于集群故障,Elasticsearch需要重新加載索引,這對(duì)資源消耗很大,因此恢復(fù)過(guò)程可能需要幾分鐘。這對(duì)于服務(wù)可用性的保證是一個(gè)挑戰(zhàn)。
2 更具成本效益的方案
在反思基于Elasticsearch的解決方案的優(yōu)點(diǎn)和局限性后,Apache Doris開(kāi)發(fā)人員對(duì)Apache Doris進(jìn)行了日志處理的優(yōu)化。
- 增加寫入吞吐量: Elasticsearch的性能受到數(shù)據(jù)解析和倒排索引創(chuàng)建的限制,因此改進(jìn)了Apache Doris在這些方面的性能:通過(guò)SIMD指令和CPU向量指令加快了數(shù)據(jù)解析和索引創(chuàng)建的速度;然后移除了在日志分析場(chǎng)景中不必要的數(shù)據(jù)結(jié)構(gòu),例如正排索引,以簡(jiǎn)化索引創(chuàng)建過(guò)程。
- 減少存儲(chǔ)成本: 移除了正排索引,這部分?jǐn)?shù)據(jù)占據(jù)了索引數(shù)據(jù)的30%。采用了列式存儲(chǔ)和ZSTD壓縮算法,從而實(shí)現(xiàn)了5:1到10:1的壓縮比??紤]到大部分歷史日志很少被訪問(wèn),引入了分層存儲(chǔ)來(lái)分離熱數(shù)據(jù)和冷數(shù)據(jù)。超過(guò)指定時(shí)間段的日志將被移動(dòng)到存儲(chǔ)成本更低的對(duì)象存儲(chǔ)中。這可以將存儲(chǔ)成本降低約70%。
Elasticsearch的官方測(cè)試工具ES Rally進(jìn)行的基準(zhǔn)測(cè)試顯示,Apache Doris在數(shù)據(jù)寫入方面比Elasticsearch快約5倍,在查詢方面快約2.3倍,并且僅消耗Elasticsearch使用存儲(chǔ)空間的1/5。在HTTP日志的測(cè)試數(shù)據(jù)集上,它實(shí)現(xiàn)了550 MB/s的寫入速度和10:1的壓縮比。
圖片
下圖顯示了一個(gè)典型的基于Doris的日志處理系統(tǒng)的樣貌。它更加全面,從數(shù)據(jù)攝取、分析到應(yīng)用,都可以更靈活地使用:
- 數(shù)據(jù)導(dǎo)入:Apache Doris支持多種日志數(shù)據(jù)的攝入方式??梢酝ㄟ^(guò)使用Logstash的HTTP輸出將日志推送到Doris,可以在將日志寫入Doris之前使用Flink預(yù)處理日志,或者可以通過(guò)常規(guī)加載和S3加載從Flink或?qū)ο蟠鎯?chǔ)中加載日志到Doris中。
- 數(shù)據(jù)分析:可以把日志數(shù)據(jù)放入Doris,并在數(shù)據(jù)倉(cāng)庫(kù)中進(jìn)行跨日志和其他數(shù)據(jù)的聯(lián)接查詢。
- 應(yīng)用:Apache Doris兼容MySQL協(xié)議,因此可以把各種數(shù)據(jù)分析工具和客戶端集成到Doris中,例如Grafana和Tableau。還可以通過(guò)JDBC和ODBC API將應(yīng)用程序連接到Doris。這里計(jì)劃構(gòu)建一個(gè)類似于Kibana的系統(tǒng)來(lái)可視化日志。
圖片
此外,Apache Doris具有更好的無(wú)模式支持和更用戶友好的分析引擎。
2.1 原生支持半結(jié)構(gòu)化數(shù)據(jù)
首先,在數(shù)據(jù)類型上進(jìn)行優(yōu)化。通過(guò)矢量化優(yōu)化了字符串搜索和正則表達(dá)式匹配的文本性能,性能提升了2~10倍。對(duì)于JSON字符串,Apache Doris將其解析并存儲(chǔ)為更緊湊和高效的二進(jìn)制格式,可以加快查詢速度4倍。還為復(fù)雜數(shù)據(jù)添加了一種新的數(shù)據(jù)類型:Array Map。它可以將連接的字符串進(jìn)行結(jié)構(gòu)化,以實(shí)現(xiàn)更高的壓縮率和更快的查詢速度。
其次,Apache Doris支持模式演化。這意味著可以根據(jù)業(yè)務(wù)變化調(diào)整模式??梢蕴砑踊騽h除字段和索引,并更改字段的數(shù)據(jù)類型。
Apache Doris提供了輕量級(jí)的模式更改功能,因此開(kāi)發(fā)人員可以在幾毫秒內(nèi)添加或刪除字段:
-- 添加列。結(jié)果會(huì)在毫秒級(jí)返回。
ALTER TABLE lineitem ADD COLUMN l_new_column INT;
還可以僅為目標(biāo)字段添加索引,以避免不必要的索引創(chuàng)建帶來(lái)的開(kāi)銷。在添加索引后,默認(rèn)情況下,系統(tǒng)將為所有增量數(shù)據(jù)生成索引,并且可以指定需要索引的歷史數(shù)據(jù)分區(qū)。
-- 添加倒排索引。Doris會(huì)為以后的所有新數(shù)據(jù)生成倒排索引。
ALTER TABLE table_name ADD INDEX index_name(column_name) USING INVERTED;
-- 為指定的歷史數(shù)據(jù)分區(qū)構(gòu)建索引。
BUILD INDEX index_name ON table_name PARTITIONS(partition_name1, partition_name2);
2.2 基于SQL的分析引擎
基于SQL的分析引擎確保數(shù)據(jù)工程師和分析師能夠在短時(shí)間內(nèi)輕松掌握Apache Doris,并將其在SQL方面的經(jīng)驗(yàn)應(yīng)用到這個(gè)OLAP引擎中。借助SQL的豐富功能,用戶可以執(zhí)行數(shù)據(jù)檢索、聚合、多表連接、子查詢、UDF、邏輯視圖和物化視圖,以滿足自身需求。
Apache Doris具備MySQL兼容性,可以與大數(shù)據(jù)生態(tài)系統(tǒng)中的大多數(shù)GUI和BI工具集成,使用戶能夠?qū)崿F(xiàn)更復(fù)雜和多樣化的數(shù)據(jù)分析。
3 使用案例中的性能表現(xiàn)
一家游戲公司已經(jīng)從ELK技術(shù)棧轉(zhuǎn)向了Apache Doris解決方案。他們基于Doris的日志系統(tǒng)所需的存儲(chǔ)空間只有之前的1/6。
一家網(wǎng)絡(luò)安全公司利用Apache Doris中的倒排索引構(gòu)建了他們的日志分析系統(tǒng),支持每秒寫入30萬(wàn)行數(shù)據(jù),僅使用以前所需的1/5服務(wù)器資源。
4 實(shí)踐指南
現(xiàn)在按照以下三個(gè)步驟來(lái)構(gòu)建一個(gè)基于Apache Doris的日志分析系統(tǒng)。
在開(kāi)始之前,從官方網(wǎng)站下載Apache Doris 2.0或更新版本,并部署集群。
4.1 步驟1:創(chuàng)建表格
這是一個(gè)表格創(chuàng)建的示例。
對(duì)配置的解釋:
- 將DATETIMEV2時(shí)間字段指定為鍵,以加快對(duì)最新N條日志記錄的查詢速度。
- 為頻繁訪問(wèn)的字段創(chuàng)建索引,并使用解析器參數(shù)指定需要進(jìn)行全文搜索的字段。
- "PARTITION BY RANGE"意味著根據(jù)時(shí)間字段將數(shù)據(jù)按范圍進(jìn)行分區(qū),啟用動(dòng)態(tài)分區(qū)以進(jìn)行自動(dòng)管理。
- "DISTRIBUTED BY RANDOM BUCKETS AUTO"意味著將數(shù)據(jù)隨機(jī)分布到桶中,系統(tǒng)會(huì)根據(jù)集群大小和數(shù)據(jù)量自動(dòng)決定桶的數(shù)量。
- "log_policy_1day"和"log_s3"意味著將超過(guò)1天的日志移動(dòng)到S3存儲(chǔ)。
CREATE DATABASE log_db;
USE log_db;
CREATE RESOURCE "log_s3"
PROPERTIES
(
"type" = "s3",
"s3.endpoint" = "your_endpoint_url",
"s3.region" = "your_region",
"s3.bucket" = "your_bucket",
"s3.root.path" = "your_path",
"s3.access_key" = "your_ak",
"s3.secret_key" = "your_sk"
);
CREATE STORAGE POLICY log_policy_1day
PROPERTIES(
"storage_resource" = "log_s3",
"cooldown_ttl" = "86400"
);
CREATE TABLE log_table
(
``ts` DATETIMEV2,
``clientip` VARCHAR(20),
``request` TEXT,
``status` INT,
``size` INT,
INDEX idx_size (`size`) USING INVERTED,
INDEX idx_status (`status`) USING INVERTED,
INDEX idx_clientip (`clientip`) USING INVERTED,
INDEX idx_request (`request`) USING INVERTED PROPERTIES("parser" = "english")
)
ENGINE = OLAP
DUPLICATE KEY(`ts`)
PARTITION BY RANGE(`ts`) ()
DISTRIBUTED BY RANDOM BUCKETS AUTO
PROPERTIES (
"replication_num" = "1",
"storage_policy" = "log_policy_1day",
"deprecated_dynamic_schema" = "true",
"dynamic_partition.enable" = "true",
"dynamic_partition.time_unit" = "DAY",
"dynamic_partition.start" = "-3",
"dynamic_partition.end" = "7",
"dynamic_partition.prefix" = "p",
"dynamic_partition.buckets" = "AUTO",
"dynamic_partition.replication_num" = "1"
);
4.2 步驟2:導(dǎo)入日志
Apache Doris支持多種數(shù)據(jù)導(dǎo)入方法。對(duì)于實(shí)時(shí)日志,推薦以下三種方法:
- 從Kafka消息隊(duì)列中拉取日志:Routine Load
- Logstash:通過(guò)HTTP API將日志寫入Doris
- 自定義編寫程序:通過(guò)HTTP API將日志寫入Doris
使用Kafka進(jìn)行數(shù)據(jù)攝取
對(duì)于寫入Kafka消息隊(duì)列的JSON日志,創(chuàng)建常規(guī)加載(Routine Load),以便Doris從Kafka中拉取數(shù)據(jù)。以下是示例。property.*
配置為可選配置:
-- 準(zhǔn)備Kafka集群和主題("log_topic")
-- 創(chuàng)建常規(guī)加載,從Kafka的 log_topic 加載數(shù)據(jù)到 "log_table"
CREATE ROUTINE LOAD load_log_kafka ON log_db.log_table
COLUMNS(ts, clientip, request, status, size)
PROPERTIES (
"max_batch_interval" = "10",
"max_batch_rows" = "1000000",
"max_batch_size" = "109715200",
"strict_mode" = "false",
"format" = "json"
)
FROM KAFKA (
"kafka_broker_list" = "host:port",
"kafka_topic" = "log_topic",
"property.group.id" = "your_group_id",
"property.security.protocol"="SASL_PLAINTEXT",
"property.sasl.mechanism"="GSSAPI",
"property.sasl.kerberos.service.name"="kafka",
"property.sasl.kerberos.keytab"="/path/to/xxx.keytab",
"property.sasl.kerberos.principal"="xxx@yyy.com"
);
可以通過(guò)SHOW ROUTINE LOAD
命令查看常規(guī)加載的運(yùn)行情況。
通過(guò)Logstash進(jìn)行數(shù)據(jù)導(dǎo)入
配置Logstash的HTTP輸出,然后通過(guò)HTTP Stream Load將數(shù)據(jù)發(fā)送到Doris。
1) 在logstash.yml
中指定批量大小和批量延遲,以提高數(shù)據(jù)寫入性能。
pipeline.batch.size: 100000
pipeline.batch.delay: 10000
2) 在日志收集配置文件testlog.conf
中添加HTTP輸出,URL為Doris中的Stream Load地址。
- 由于Logstash不支持HTTP重定向,應(yīng)該使用后端地址而不是FE地址。
- 頭部中的授權(quán)是
http basic auth
,使用echo -n 'username:password' | base64
進(jìn)行計(jì)算。 - 頭部中的
load_to_single_tablet
可以減少數(shù)據(jù)攝取中的小文件數(shù)量。
output {
http {
follow_redirects => true
keepalive => false
http_method => "put"
url => "http://172.21.0.5:8640/api/logdb/logtable/_stream_load"
headers => [
"format", "json",
"strip_outer_array", "true",
"load_to_single_tablet", "true",
"Authorization", "Basic cm9vdDo=",
"Expect", "100-continue"
]
format => "json_batch"
}
}
通過(guò)自定義程序進(jìn)行數(shù)據(jù)攝取
以下是通過(guò)HTTP Stream Load將數(shù)據(jù)攝取到Doris的示例。
注意:
- 使用basic auth進(jìn)行HTTP授權(quán),使用 echo -n 'username:password' | base64 進(jìn)行計(jì)算。
- http header "format:json":指定數(shù)據(jù)類型為JSON。
- http header "read_json_by_line:true":每行都是一個(gè)JSON記錄。
- http header "load_to_single_tablet:true":每次寫入一個(gè)分片(tablet)。
- 對(duì)于數(shù)據(jù)寫入客戶端,建議批量大小為100MB~1GB。未來(lái)的版本將在服務(wù)器端啟用Group Commit,并減小客戶端的批量大小。
curl \
--location-trusted \
-u username:password \
-H "format:json" \
-H "read_json_by_line:true" \
-H "load_to_single_tablet:true" \
-T logfile.json \
http://fe_host:fe_http_port/api/log_db/log_table/_stream_load
4.3 步驟3:執(zhí)行查詢
Apache Doris支持標(biāo)準(zhǔn)SQL,因此可以通過(guò)MySQL客戶端或JDBC連接到Doris,然后執(zhí)行SQL查詢。
mysql -h fe_host -P fe_mysql_port -u root -Dlog_db
一些常見(jiàn)的日志分析查詢:
- 檢查最新的10條記錄。
SELECT * FROM log_table ORDER BY ts DESC LIMIT 10;
- 檢查Client IP為"8.8.8.8"的最新的10條記錄。
SELECT * FROM log_table WHERE clientip = '8.8.8.8' ORDER BY ts DESC LIMIT 10;
- 檢索在"request"字段中包含"error"或"404"的最新的10條記錄。MATCH_ANY是Doris中的通過(guò)全文搜索來(lái)查找包含指定關(guān)鍵詞中任意一個(gè)的記錄。
SELECT * FROM log_table WHERE request MATCH_ANY 'error 404' ORDER BY ts DESC LIMIT 10;
- 檢索在"request"字段中同時(shí)包含"image"和"faq"的最新的10條記錄。MATCH_ALL也是Doris中的全文搜索語(yǔ)法關(guān)鍵詞,表示查找同時(shí)包含所有指定關(guān)鍵詞的記錄。
SELECT * FROM log_table WHERE request MATCH_ALL 'image faq' ORDER BY ts DESC LIMIT 10;
5 總結(jié)
如果需要一種高效的日志分析解決方案,Apache Doris是非常友好的選擇,尤其適合那些具備SQL知識(shí)的讀者。相比ELK堆棧,使用Apache Doris可以獲得更好的無(wú)模式支持,實(shí)現(xiàn)更快的數(shù)據(jù)寫入和查詢,并且減少存儲(chǔ)負(fù)擔(dān)。