不同數(shù)據(jù)庫(kù)處理高基數(shù)數(shù)據(jù)的方式,你會(huì)嗎?
了解不同數(shù)據(jù)庫(kù)如何處理高基數(shù)數(shù)據(jù),并了解選擇正確的索引方案為何如此重要。
譯自How Different Databases Handle High-Cardinality Data,作者 Team Timescale。
時(shí)間序列數(shù)據(jù)、物聯(lián)網(wǎng)傳感器讀數(shù)、用戶(hù)行為日志——這些只是現(xiàn)代系統(tǒng)必須處理的數(shù)據(jù)流的幾個(gè)例子。它們的共同點(diǎn)是都傾向于高基數(shù),這給數(shù)據(jù)存儲(chǔ)和分析帶來(lái)了獨(dú)特的挑戰(zhàn)。隨著組織越來(lái)越依賴(lài)數(shù)據(jù)驅(qū)動(dòng)的決策,了解不同數(shù)據(jù)庫(kù)如何處理高基數(shù)數(shù)據(jù)對(duì)于構(gòu)建高效且可擴(kuò)展的系統(tǒng)至關(guān)重要。
本文將探討高基數(shù)數(shù)據(jù)帶來(lái)的挑戰(zhàn),檢查旨在處理高基數(shù)數(shù)據(jù)的各種數(shù)據(jù)庫(kù)工具,并比較各種方法,以幫助您做出關(guān)于數(shù)據(jù)架構(gòu)的明智決策。
高基數(shù)的挑戰(zhàn)
高基數(shù)指的是數(shù)據(jù)集中唯一元素的數(shù)量,當(dāng)我們查看現(xiàn)實(shí)世界的例子時(shí),這是一個(gè)特別具體的概念。想象一下一個(gè)跟蹤熱門(mén)網(wǎng)站上用戶(hù)交互的系統(tǒng)——每個(gè)用戶(hù)可能都有一個(gè)唯一的標(biāo)識(shí)符,每個(gè)會(huì)話都會(huì)生成一個(gè)唯一的ID,每個(gè)交互都會(huì)創(chuàng)建一個(gè)唯一的事件ID。在大規(guī)模應(yīng)用中,這些唯一值可以迅速達(dá)到數(shù)百萬(wàn)甚至數(shù)十億。
這種大量唯一值會(huì)給數(shù)據(jù)庫(kù)系統(tǒng)帶來(lái)重大挑戰(zhàn)。當(dāng)在具有高基數(shù)列的表之間執(zhí)行連接時(shí),潛在的組合會(huì)呈指數(shù)級(jí)增長(zhǎng)。例如,將用戶(hù)交互數(shù)據(jù)與會(huì)話數(shù)據(jù)連接可能需要將數(shù)百萬(wàn)個(gè)唯一的用戶(hù)ID與數(shù)百萬(wàn)個(gè)唯一的會(huì)話ID進(jìn)行匹配。由于數(shù)據(jù)庫(kù)必須維護(hù)和處理這些海量獨(dú)特的組合,因此生成的運(yùn)算會(huì)迅速壓垮系統(tǒng)資源。
在需要完全表掃描的操作中,性能下降尤其嚴(yán)重。當(dāng)數(shù)據(jù)庫(kù)需要跨高基數(shù)列分析或聚合數(shù)據(jù)時(shí),它必須在內(nèi)存中為每個(gè)唯一值維護(hù)不同的計(jì)數(shù)器或聚合。這會(huì)迅速耗盡可用的內(nèi)存資源,導(dǎo)致查詢(xún)執(zhí)行時(shí)間變慢,或者在極端情況下導(dǎo)致系統(tǒng)故障。
閱讀本文以了解更多關(guān)于高基數(shù)的信息。
數(shù)據(jù)庫(kù)解決方案:時(shí)間序列數(shù)據(jù)庫(kù)InfluxDB和TimescaleDB如何處理高基數(shù)
鑒于高基數(shù)數(shù)據(jù)集在時(shí)間序列中有多么常見(jiàn),讓我們來(lái)看看兩個(gè)時(shí)間序列數(shù)據(jù)庫(kù)InfluxDB和TimescaleDB是如何處理這個(gè)問(wèn)題的。
InfluxDB是一個(gè)NoSQL數(shù)據(jù)庫(kù),其創(chuàng)建者選擇從頭開(kāi)始重建所有內(nèi)容。相比之下,TimescaleDB是一個(gè)SQL數(shù)據(jù)庫(kù),其創(chuàng)建者(即本文作者)選擇擁抱并構(gòu)建在PostgreSQL和已驗(yàn)證的數(shù)據(jù)結(jié)構(gòu)之上,然后進(jìn)一步擴(kuò)展它以用于時(shí)間序列、事件和實(shí)時(shí)分析問(wèn)題。(順便說(shuō)一句,使用正確的擴(kuò)展,它還可以推動(dòng)您的AI應(yīng)用程序開(kāi)發(fā)。)
首先,以下是這兩個(gè)數(shù)據(jù)庫(kù)在數(shù)據(jù)集基數(shù)增加時(shí)插入性能的比較。
對(duì)于以下比較,我們使用了以下設(shè)置:
- TimescaleDB版本1.2.2,InfluxDB版本1.7.6
- 1臺(tái)遠(yuǎn)程客戶(hù)端機(jī)器和1臺(tái)數(shù)據(jù)庫(kù)服務(wù)器,兩者都在同一個(gè)云數(shù)據(jù)中心
- AWS EC2實(shí)例:i3.xlarge(4個(gè)vCPU,30 GB內(nèi)存)
- 4個(gè)1 TB磁盤(pán),采用raid0配置(EXT4文件系統(tǒng))
- 兩個(gè)數(shù)據(jù)庫(kù)都獲得了所有可用內(nèi)存
- 數(shù)據(jù)集:100-1,000,000個(gè)模擬設(shè)備每10秒生成1-10個(gè)CPU指標(biāo),約1億個(gè)讀取間隔,約10億個(gè)指標(biāo)(100個(gè)設(shè)備一個(gè)月間隔;4000個(gè)設(shè)備三天;100,000個(gè)設(shè)備三個(gè)小時(shí);1,000,000個(gè)設(shè)備三分鐘),使用時(shí)間序列基準(zhǔn)套件 (TSBS) 生成- 用于TimescaleDB (1)和InfluxDB (2)的模式
- 插入時(shí)兩個(gè)數(shù)據(jù)庫(kù)都使用了10K批大小
- 對(duì)于TimescaleDB,我們根據(jù)數(shù)據(jù)量設(shè)置塊大小,目標(biāo)是10-15個(gè)塊(更多信息)
- 對(duì)于InfluxDB,我們啟用了TSI(時(shí)間序列索引)
(1)TimescaleDB schema:Tablecpu(time timestamp, tags_id integer, usage_user double, usage_system double, usage_idle double, usage_nice double, usage_iowait double, usage_irq double, usage_softirq double, usage_steal double, usage_guest double, usage_guest_nice double, additional_tags jsonb); indexes (tags_id, time) and (time, tags_id); Tabletags(id integer, hostname text, region text, datacenter text, rack text, os text, arch text, team text, service text, service_version text, service_environment text); unique index on all columns
(2)InfluxDB schema:Field keys (usage_guest integer, usage_guest_nice integer, usage_idle integer, usage_iowait integer, usage_irq integer, usage_nice integer, usage_softirq integer, usage_steal integer, usage_system integer, usage_user integer), Tag keys (arch, datacenter, hostname, os, rack, region, service, service_environment, service_version, team)
圖片
注意:這里可以找到這兩個(gè)數(shù)據(jù)庫(kù)更詳細(xì)的總體比較。
正如你所看到的,在低基數(shù)情況下,兩個(gè)數(shù)據(jù)庫(kù)是可比的(盡管 TimescaleDB 的性能高出 30%)。但隨著基數(shù)的增加,差異變得相當(dāng)顯著,因?yàn)?TimescaleDB 的插入性能下降速度遠(yuǎn)遠(yuǎn)慢于 InfluxDB,而 InfluxDB 的性能則急劇下降。在高基數(shù)情況下,TimescaleDB 的性能比 InfluxDB 高出 11 倍以上。
這些結(jié)果對(duì)一些人來(lái)說(shuō)可能并不令人驚訝,因?yàn)楦呋鶖?shù)是 InfluxDB 的一個(gè)眾所周知的弱點(diǎn)(來(lái)源:GitHub、論壇)。
但為什么會(huì)這樣呢?讓我們更仔細(xì)地看看這兩種數(shù)據(jù)庫(kù)的開(kāi)發(fā)情況。
B-Trees 與TSI:處理高基數(shù)的兩種不同方法
我們可以將高基數(shù)性能的差異追溯到InfluxDB與TimescaleDB在工程決策上的根本不同。
InfluxDB 和 TSI
由于高基數(shù)一直是InfluxDB的一個(gè)眾所周知的挑戰(zhàn),他們的團(tuán)隊(duì)一直在研究一種稱(chēng)為“時(shí)間序列索引”(TSI)的東西來(lái)解決這個(gè)問(wèn)題。
與他們?cè)谄渌I(lǐng)域的做法一致,InfluxDB TSI 是一個(gè)基于本地日志結(jié)構(gòu)合并樹(shù)的系統(tǒng),由各種數(shù)據(jù)結(jié)構(gòu)組成,包括哈希映射和位集。這包括一個(gè)內(nèi)存中的日志(“LogFile”),當(dāng)其超過(guò)閾值(5 MB)時(shí)會(huì)定期刷新到磁盤(pán),并且被壓縮到一個(gè)磁盤(pán)上的內(nèi)存映射索引(“IndexFile”);一個(gè)文件(“SeriesFile”),包含了整個(gè)數(shù)據(jù)庫(kù)中所有序列鍵的集合。(在他們的文檔中有描述。)
TSI 的性能取決于所有這些數(shù)據(jù)結(jié)構(gòu)的相互作用。然而,由于 TSI 是定制構(gòu)建的,理解其在各種高基數(shù)工作負(fù)載下的表現(xiàn)變得難以理解。
TSI 的設(shè)計(jì)決策也導(dǎo)致了一些具有性能影響的限制:
- 根據(jù)InfluxDB的文檔,該總基數(shù)限制大約為3,000萬(wàn)(盡管根據(jù)上面的圖表,InfluxDB在達(dá)到該限制之前就已經(jīng)開(kāi)始表現(xiàn)不佳),或者遠(yuǎn)低于物聯(lián)網(wǎng)(包括我們上面的示例)等時(shí)間序列用例中通常所需的數(shù)量。
- InfluxDB索引標(biāo)簽但不索引字段,這意味著某些查詢(xún)無(wú)法比全表掃描表現(xiàn)得更好。因此,以我們之前提到的物聯(lián)網(wǎng)數(shù)據(jù)集為例,如果想要搜索所有沒(méi)有空閑內(nèi)存的行(例如,類(lèi)似于SELECT * FROM sensor_data WHERE mem_free = 0的查詢(xún)),就無(wú)法比全表線性掃描(即O(n)時(shí)間)做得更好來(lái)識(shí)別相關(guān)數(shù)據(jù)點(diǎn)。
- 索引中包含的列集是完全固定且不可變的。更改數(shù)據(jù)中哪些列被索引(標(biāo)記)以及哪些沒(méi)有,需要完全重寫(xiě)數(shù)據(jù)。
- 由于依賴(lài)哈希映射,InfluxDB 只能索引離散值而不能索引連續(xù)值。例如,要搜索所有溫度高于 90 度的行(例如,類(lèi)似于 SELECT * FROM sensor_data WHERE temperature > 90 的查詢(xún)),則需要再次完全掃描整個(gè)數(shù)據(jù)集。
- InfluxDB 的基數(shù)受到所有時(shí)間范圍內(nèi)基數(shù)的影響,即使某些字段/值不再存在于數(shù)據(jù)集中也是如此。這是因?yàn)?SeriesFile 存儲(chǔ)了整個(gè)數(shù)據(jù)集的所有系列鍵。
TimescaleDB 和 B-trees
相比之下,TimescaleDB是一個(gè)關(guān)系型數(shù)據(jù)庫(kù),它依賴(lài)于久經(jīng)考驗(yàn)的用于索引數(shù)據(jù)的結(jié)構(gòu):B-tree。這一決定使其能夠擴(kuò)展到高基數(shù)。
首先,TimescaleDB按時(shí)間對(duì)您的數(shù)據(jù)進(jìn)行分區(qū),一個(gè)B-tree將時(shí)間段映射到相應(yīng)的分區(qū)(“chunk”)。所有這些分區(qū)都在后臺(tái)進(jìn)行,對(duì)用戶(hù)隱藏,用戶(hù)能夠訪問(wèn)一個(gè)虛擬表(“hypertable”),該表跨越所有分區(qū)中的所有數(shù)據(jù)。
接下來(lái),TimescaleDB允許在您的數(shù)據(jù)集上創(chuàng)建多個(gè)索引(例如,對(duì)于equipment_id、sensor_id、firmware_version、site_id)。默認(rèn)情況下,這些索引以B-tree的形式在每個(gè)chunk上創(chuàng)建。
也可以使用任何內(nèi)置的PostgreSQL索引類(lèi)型創(chuàng)建索引:Hash、GiST、SP-GiST、GIN和BRIN。您可以閱讀這篇文章以了解有關(guān)索引的更多信息以及如何使用它們來(lái)優(yōu)化PostgreSQL數(shù)據(jù)庫(kù)性能。
這種方法對(duì)高基數(shù)數(shù)據(jù)集有一些好處:
- 更簡(jiǎn)單的方法可以更清晰地了解數(shù)據(jù)庫(kù)的性能。只要我們要查詢(xún)的數(shù)據(jù)集的索引和數(shù)據(jù)適合內(nèi)存(這是可以調(diào)整的),基數(shù)就成為一個(gè)非問(wèn)題。
- 此外,由于輔助索引的范圍在chunk級(jí)別,因此索引本身的大小僅與該時(shí)間范圍的數(shù)據(jù)集的基數(shù)一樣大。
- 您可以控制要索引的列,包括能夠在多列上創(chuàng)建復(fù)合索引。您也可以隨時(shí)添加或刪除索引,例如,如果您的查詢(xún)工作負(fù)載發(fā)生變化。與InfluxDB不同,在TimescaleDB中更改索引結(jié)構(gòu)不需要重寫(xiě)數(shù)據(jù)的整個(gè)歷史記錄。
- 您可以對(duì)離散字段和連續(xù)字段創(chuàng)建索引,特別是由于B-tree非常適合使用以下任何運(yùn)算符進(jìn)行比較:<、<=、=、>=、>、BETWEEN、IN、IS NULL、IS NOT NULL。我們上面示例查詢(xún)(SELECT * FROM sensor_data WHERE mem_free = 0和SELECT * FROM sensor_data WHERE temperature > 90)將在對(duì)數(shù)時(shí)間或O(log n)時(shí)間內(nèi)運(yùn)行。
- 其他受支持的索引類(lèi)型在其他場(chǎng)景中可能派上用場(chǎng),例如,用于“最近鄰”搜索的GIST索引。
結(jié)論
現(xiàn)代數(shù)據(jù)庫(kù)系統(tǒng)中高基數(shù)數(shù)據(jù)帶來(lái)的挑戰(zhàn)需要復(fù)雜的索引解決方案來(lái)克服連接操作和全表掃描的固有障礙。InfluxDB和Timescale都具有獨(dú)特的策略來(lái)有效地管理高基數(shù)數(shù)據(jù)。
Timescale的方法利用了B-tree數(shù)據(jù)結(jié)構(gòu)的強(qiáng)大功能,為處理高基數(shù)數(shù)據(jù)集提供了強(qiáng)大的基礎(chǔ)。這種結(jié)構(gòu)不僅能夠?qū)崿F(xiàn)卓越的查詢(xún)性能,而且還提供了滿(mǎn)足各種索引需求所需的靈活性。B-tree架構(gòu)允許高效的范圍查詢(xún)和點(diǎn)查找,使其特別適合時(shí)間序列應(yīng)用程序,在這些應(yīng)用程序中,歷史分析和實(shí)時(shí)數(shù)據(jù)訪問(wèn)都至關(guān)重要。