close

譯者

何源(荊杭),阿里雲計算平台事業部高級產品專家

前言

本文翻譯自 Altinity 針對 ClickHouse 的系列技術文章。面向聯機分析處理(OLAP)的開源分析引擎 ClickHouse,因其優良的查詢性能,PB 級的數據規模,簡單的架構,被國內外公司廣泛採用。

阿里雲 EMR-OLAP 團隊,基於開源 ClickHouse 進行了系列優化,提供了開源 OLAP 分析引擎 ClickHouse 的雲上託管服務。EMR ClickHouse 完全兼容開源版本的產品特性,同時提供集群快速部署、集群管理、擴容、縮容和監控告警等雲上產品功能,並且在開源的基礎上優化了 ClickHouse 的讀寫性能,提升了 ClickHouse 與 EMR 其他組件快速集成的能力。訪問https://help.aliyun.com/document_detail/212195.html了解詳情。

(圖源Altinity,侵刪)

使用新的 TTL move,將數據存儲在合適的地方目錄

TTL 表達式

設置演示

在不重新啟動服務器的情況下添加存儲配置

創建使用 TTL move 的表

關於 TTL delete 的題外話

將 TTL move 添加到現有表中

一些關於方式和時機的問題

結論

後續

多卷存儲在許多用例中是至關重要的。它有助於降低存儲成本,並將最關鍵的應用數據放在最快的存儲設備上,從而提高查詢性能。監控數據是一個經典用例。隨着時間的推移,數據的價值會迅速下降。前一天、上一周、上一月和上一年的數據在訪問模式上截然不同,這又對應於各種不同的存儲需求。

因此,根據數據時間來適當放置數據就非常重要了。ClickHouse TTL move 現在提供了一種機制來實現這一點。ClickHouse 基於時間的 TTL move 的最大的優勢在於非常直觀,直接對應於人們的日曆時間概念。TTL move 大幅簡化了與業務要求相對應的多卷存儲的設置。

多卷功能大大增加了 ClickHouse 服務器的容量。它通過提供存儲策略,將磁盤排列成卷,並在它們之間建立關係,從而實現分層存儲。然而,存儲策略本身並沒有提供太多的靈活性來控制 ClickHouse 保存數據的位置。用戶要麼使用 ALTER TABLE [db.]table MOVE PART|PARTITION 命令手動操作,要麼依賴 move factor 參數來實現基於已用空間比例的卷間數據的簡單分配。

在本文中,我們將研究新表 TTL move,它允許用戶定義表達式,而這些表達式可以自動將數據移動到用戶在存儲配置中指定的特定磁盤或卷。新 TTL move 大大增強了多卷存儲能力,並提供了充分使用多卷存儲能力所需的細粒度控制。

TTL 表達式

MergeTree 是目前唯一支持 TTL 表達式的引擎系列。ClickHouse 首先增加了對 TTL 表達式的支持,以啟用自動刪除突變。TTL 表達式只是一種 SQL 表達式,它必須評估為 Date 或 DateTime 數據類型。這種表達式可以使用顯式時間間隔(用到 INTERVAL 關鍵字)或使用 toInterval 轉換函數。例如,

TTL date_time + INTERVAL 1 MONTHTTL date_time + INTERVAL 15 HOUR

或者使用 toInterval 轉換函數,可以得到以下表達式

TTL date_time + toIntervalMonth(ttl)TTL date_time + toIntervalHour(ttl)

或者只是

TTL date_time + INTERVAL ttl MONTHTTL date_time + INTERVAL ttl HOUR

我們可以把這些表達式分配給一個表,分配後可稱為 表達式。一個表只能有一個表達式用於刪除,有多個表達式用於將分片自動移動到磁盤或卷。例如,假設我們有一個存儲策略,其中定義了一個卷 slow_volume 和一個磁盤 slow_disk,那麼表 TTL 表達式可以如下所示

TTL date_time + INTERVAL 1 MONTH DELETE, date_time + INTERVAL 1 WEEK TO VOLUME 'slow_volume', date_time + INTERVAL 2 WEEK TO DISK 'slow_disk';

有一點值得注意。因為 ClickHouse 首先增加了對 delete TTL 表達式的支持,如果沒有指定 TO DISK 或 TO VOLUME 子句,則假定 DELETE 子句。因此,我們建議務必要明確地使用 DELETE 子句來確定要將哪一個 TTL 表達式用於刪除。

設置演示

如果你未研究過多卷存儲,或未使用過 TTL delete 或 move 表達式,我們建議你使用最新的 ClickHouse 版本 20.3.2.1。我們將在本文的其餘部分使用這一版本。

$ clickhouse-clientClickHouse client version 20.3.2.1 (official build).Connecting to localhost:9000 as user default.Connected to ClickHouse server version 20.3.2 revision 54433.

出於演示目的,我們將使用 OnTime 數據庫(1987 至 2018 年的美國民用飛行數據)。為了方便,我們將下載並使用預先製作的分區。

$ curl -O https://clickhouse-datasets.s3.yandex.net/ontime/partitions/ontime.tar$ tar xvf ontime.tar -C /var/lib/clickhouse # path to ClickHouse data directory$ # check permissions of unpacked data, fix if required$ sudo service clickhouse-server restart$ clickhouse-client --query "select count(*) from datasets.ontime"

當然,如果不使用多卷存儲,TTL move 表達式就沒有意義了。因此,我們將通過創建不同的文件夾來模擬多個存儲設備,這些文件夾將代表具有不同速度和容量的已安裝存儲設備。

/data/fast/data/med0/data/med1/data/slow

在不重新啟動服務器的情況下添加存儲配置

目前,我們的服務器只使用默認磁盤。我們只要查看 system.disks 表就能發現這一點。

:) select * from system.disksSELECT *FROM system.disks┌─name────┬─path─────────────────┬──free_space─┬──total_space─┬─keep_free_space─┐│ default │ /var/lib/clickhouse/ │ 37705834496 │ 468514799616 │ 0 │└─────────┴──────────────────────┴─────────────┴──────────────┴─────────────────┘

我們需要更多的存儲空間,並且可以在不重新啟動服務器的情況下添加新磁盤。我們最近在 ClickHouse 中添加了此功能。現在可以先睹為快。通過將 storage.xml 放入我們的 /etc/clickhouse-server/config.d/ 文件夾,可以定義存儲配置。

<yandex> <storage_configuration> <disks> <default> <keep_free_space_bytes>1024</keep_free_space_bytes> </default> <fast> <path>/data/fast/</path> </fast> <med0> <path>/data/med0/</path> </med0> <med1> <path>/data/med1/</path> </med1> <slow> <path>/data/slow/</path> </slow> </disks> <policies> <default> <volumes> <default> <disk>default</disk> </default> <fast> <disk>fast</disk> </fast> <med> <disk>med0</disk> <disk>med1</disk> </med> <slow> <disk>slow</disk> </slow> </volumes> </default> </policies> </storage_configuration></yandex>

如果我們使用 SYSTEM RELOAD CONFIG 命令重載該配置,那麼應該能夠在 system.disks 表中看到新磁盤。

:) SYSTEM RELOAD CONFIG:) select * from system.disks┌─name────┬─path─────────────────┬──free_space─┬──total_space─┬─keep_free_space─┐│ default │ /var/lib/clickhouse/ │ 37993152512 │ 468514799616 │ 0 ││ fast │ /data/fast/ │ 37993152512 │ 468514799616 │ 0 ││ med0 │ /data/med0/ │ 37993152512 │ 468514799616 │ 0 ││ med1 │ /data/med1/ │ 37993152512 │ 468514799616 │ 0 ││ slow │ /data/slow/ │ 37993152512 │ 468514799616 │ 0 │└─────────┴──────────────────────┴─────────────┴──────────────┴─────────────────┘我們可以進入 system.storage_policies 表檢索存儲策略。:) select * from system.storage_policies┌─policy_name─┬─volume_name─┬─volume_priority─┬─disks───────────┬─max_data_part_size─┬─move_factor─┐│ default │ default │ 1 │ ['default'] │ 0 │ 0.1 ││ default │ fast │ 2 │ ['fast'] │ 0 │ 0.1 ││ default │ med │ 3 │ ['med0','med1'] │ 0 │ 0.1 ││ default │ slow │ 4 │ ['slow'] │ 0 │ 0.1 │└─────────────┴─────────────┴─────────────────┴─────────────────┴────────────────────┴─────────────┘

創建使用 TTL move 的表

現在我們來看看在創建新表時如何定義 TTL move。我們將使用截至 2010 年的飛行數據。最後三年的數據我們將保留在 fast 卷上,3-5 年之間的數據保留在 med 卷上,5-7 年之間的數據應進入 slow 存儲,而更早的數據將刪除。我們可以通過下表定義來實現這樣的方案。

CREATE TABLE ontime_chunkENGINE = MergeTree()PARTITION BY YearORDER BY FlightDateTTL FlightDate TO VOLUME 'fast', FlightDate + INTERVAL 3 YEAR TO VOLUME 'med', FlightDate + INTERVAL 5 YEAR TO VOLUME 'slow', FlightDate + INTERVAL 7 YEAR DELETEASSELECT *FROM datasets.ontimeWHERE Year >= 2010← Progress: 55.09 million rows, 40.13 GB (472.19 thousand rows/s., 344.00 MB/s.) ██████████████████████████████████████████████████████▌ 96%Ok.0 rows in set. Elapsed: 116.659 sec. Processed 55.09 million rows, 40.13 GB (472.19 thousand rows/s., 344.00 MB/s.)

請注意,TTL move 不支持 MergeTree 表的舊函數樣式語法。

創建並填充表後,我們可以查看表分片的存儲位置。你可以通過查看 system.parts 表實現這一點。下面的查詢可以給我們一些基本的統計數據。

SELECT partition, disk_name, count(), sum(rows), max(bytes_on_disk), avg(bytes_on_disk)FROM system.partsWHERE active AND (table = 'ontime_chunk')GROUP BY partition, disk_nameORDER BY partition DESC┌─partition─┬─disk_name─┬─count()─┬─sum(rows)─┬─max(bytes_on_disk)─┬─avg(bytes_on_disk)─┐│ 2018 │ fast │ 7 │ 6033426 │ 264508145 │ 86241164.71428572 ││ 2017 │ fast │ 4 │ 5674621 │ 274419521 │ 138846953.25 ││ 2016 │ med1 │ 3 │ 2169891 │ 147475124 │ 70784017.66666667 ││ 2016 │ med0 │ 3 │ 3447767 │ 316232407 │ 112364801.66666667 ││ 2015 │ med1 │ 2 │ 5265093 │ 260304509 │ 259205244 ││ 2015 │ med0 │ 1 │ 553986 │ 54923408 │ 54923408 ││ 2014 │ slow │ 2 │ 5819811 │ 315955553 │ 288535865.5 ││ 2013 │ slow │ 7 │ 5089209 │ 266864685 │ 71928783 ││ 2012 │ slow │ 6 │ 436874 │ 9818520 │ 7203465.166666667 ││ 2011 │ slow │ 2 │ 62029 │ 5946491 │ 2973249 ││ 2010 │ slow │ 3 │ 113398 │ 8838400 │ 3741370.6666666665 │└───────────┴───────────┴─────────┴───────────┴────────────────────┴────────────────────┘

如上所示,ClickHouse 對插入內容應用了 TTL move,分片幾乎都處於我們預期的位置。為什麼說是幾乎?因為 TTL delete 表達式與眾不同。ClickHouse 在插入過程中不評估這些,因此我們在 slow 磁盤上看到的仍是我們想要刪除的 2013 到 2010 年的數據。

關於 TTL delete 的題外話

ClickHouse 對於 TTL move 和 delete 的處理方式不同。我特意在上面的示例中納入了一個 TTL delete 表達式來說明這一點。這是因為,TTL delete 會導致代價高昂的變異操作。因此,與僅在磁盤之間複製分片的 TTL move 相比,可能代價更高昂。所以在使用 TTL 時請記住這一點。

但是鑑於大多數用戶會同時用到 TTL delete 和 move,因此必須指出的是,ClickHouse 通過「merge_with_ttl_timeout」MergeTree 表設置來控制 TTL delete 的頻率。默認設置為 24 小時,並定義了可重複進行基於 TTL 的合併的最小時間(秒)。此設置實際上意味着,每 24 小時(或者發生了後台合併時)僅在一個分區上執行一次 TTL delete。那麼在最壞的情況下,現在 ClickHouse 將最多每 24 小時刪除一個與 TTL delete 表達式匹配的分區。

這種行為可能並不理想,所以如果你想讓 TTL delete 表達式更快地執行刪除,你可以修改表的 merge_with_ttl_timeout 設置。例如,我們可以將其設置為一小時,如下所示。

ALTER TABLE [db.]table MODIFY SETTING merge_with_ttl_timeout = 3600

現在你應該看到,ClickHouse 正在根據你的 TTL delete 表達式每小時刪除分片。當然,如果你的表不是太大,可以強制使用 OPTIMIZE TABLE [db.]table FINAL 語句。但是對於大表不建議使用。

將 TTL move 添加到現有表中

我們已經看過如何使用預先定義的 TTL move 創建表。但是,如果你已經有了一個表,或者想更改現有的 TTL move 表達式,則必須使用 ALTER TABLE [db.]table MODIFY TTL 命令。

請注意,在修改 TTL 表達式時,必須重新列出所有 TTL。所有 move 表達式和 delete 表達式(如果存在)。

讓我們重新使用上表並更改 TTL 表達式。我們現在希望將除了最後三年的數據外的其他數據都放在 slow 卷上,或刪除七年之前數據。

ALTER TABLE ontime_chunk MODIFY TTL FlightDate TO VOLUME 'fast', FlightDate + toIntervalYear(3) TO VOLUME 'slow', FlightDate + toIntervalYear(7)Ok.0 rows in set. Elapsed: 0.037 sec.

很快啊!我說停停,有什麼數據移動了嗎?並沒有。新的 TTL 表達式將只分配給新分片,要麼是在插入的時候,要麼是因為新分片作為後台合併操作的結果而創建。對於現有分片,可以通過使用 ALTER TABLE [db.]table MATERIALIZE TTL 語句實現 TTL 來應用新的 TTL。如果對我們的表執行它,該命令將很快返回。

ALTER TABLE ontime_chunk MATERIALIZE TTLOk.0 rows in set. Elapsed: 0.044 sec.

這只是重寫你可以在分片的文件夾內找到的 ttl.txt 文件。例如,我們可以看看隨機分片有什麼。

$ sudo cat /data/fast/data/default/ontime_chunk/2017_113_113_0/ttl.txtttl format version: 1{ "table": { "min":1710457200, "max":1710975600 }, "moves":[ {"expression":"FlightDate","min":1489532400,"max":1490050800}, {"expression":"plus(FlightDate, toIntervalYear(3))","min":1584226800,"max":1584745200}, {"expression":"plus(FlightDate, toIntervalYear(5))","min":1647298800,"max":1647817200} ]}

在你執行 MATERIALIZE TTL 命令後,ClickHouse 會在下一個後台周期開始將分片移動到新位置。在我們的示例中,這沒有花很長時間。再來看看 system.parts 表,我發現分片移動到了新位置,有些則因為後台合併而被刪除。

SELECT partition, disk_name, count(), sum(rows), max(bytes_on_disk), avg(bytes_on_disk)FROM system.partsWHERE active AND (table = 'ontime_chunk')GROUP BY partition, disk_nameORDER BY partition DESC┌─partition─┬─disk_name─┬─count()─┬─sum(rows)─┬─max(bytes_on_disk)─┬─avg(bytes_on_disk)─┐│ 2018 │ fast │ 8 │ 6033426 │ 372476291 │ 75689482.5 ││ 2017 │ fast │ 8 │ 5674621 │ 304683038 │ 69514551.875 ││ 2016 │ slow │ 3 │ 5617658 │ 396503243 │ 183260415.33333334 ││ 2015 │ slow │ 2 │ 5819079 │ 329783074 │ 286661116.5 ││ 2014 │ slow │ 7 │ 5819811 │ 244641752 │ 85566643 ││ 2013 │ slow │ 5 │ 5089209 │ 383141486 │ 102324330.2 ││ 2012 │ slow │ 1 │ 0 │ 3 │ 3 ││ 2011 │ slow │ 2 │ 0 │ 3 │ 3 ││ 2010 │ slow │ 3 │ 0 │ 3 │ 3 │└───────────┴───────────┴─────────┴───────────┴────────────────────┴────────────────────┘

一些關於方式和時機的問題

在了解如何使用 TTL move 表達式之後,讓我們看看 ClickHouse 如何以及何時評估 TTL move 表達式。

如何評估 TTL move?

ClickHouse 使用一個專門的後台線程池來評估 TTL move 表達式。該池的行為由以下參數控制,這些參數可以在 config.xml 或 /etc/clickhouse-server/config.d/ 文件夾內的單獨配置文件中進行定義。

以下列出了參數及其當前默認值:

background_move_pool_size: 8

background_move_processing_pool_thread_sleep_seconds: 10

background_move_processing_pool_thread_sleep_seconds_random_part: 1.0

background_move_processing_pool_thread_sleep_seconds_if_nothing_to_do: 0.1

background_move_processing_pool_task_sleep_seconds_when_no_work_min: 10

background_move_processing_pool_task_sleep_seconds_when_no_work_max: 600

background_move_processing_pool_task_sleep_seconds_when_no_work_multiplier: 1.1

background_move_processing_pool_task_sleep_seconds_when_no_work_random_part: 1.0

出於測試目的,我們使用以下配置文件來實現即時移動。

<yandex> <background_move_processing_pool_thread_sleep_seconds>0.5</background_move_processing_pool_thread_sleep_seconds> <background_move_processing_pool_task_sleep_seconds_when_no_work_max>0.5</background_move_processing_pool_task_sleep_seconds_when_no_work_max></yandex>

這個簡單的配置突出了兩個最重要的參數,你需要視情況加以調整。這兩個參數是「background_move_processing_pool_task_sleep_seconds_when_no_work_max」和「background_move_processing_pool_thread_sleep_seconds」。「background_move_processing_pool_task_sleep_seconds_when_no_work_max」定義了當沒有工作(移動)時,線程池可以睡眠的最長時間。默認情況下,ClickHouse 將其設置為 600 秒。這意味着,在觸發 TTL move 表達式後,實際的移動可以在 10 分鐘內開始。完成移動的時間取決於 ClickHouse 需要移動的分片數量和磁盤的 I/O 性能。「background_move_processing_pool_thread_sleep_seconds」參數定義了工作者線程在接受另一個任務之前睡眠的秒數。

基於這些參數,當後台 move 進程池喚醒時,它會掃描所有分片的 TTL 表達式,並確定是否有任何需要移動的數據。

請注意,將分片移動到一個磁盤或卷時,後台 move 進程池會檢查由存儲策略定義的限制條件。如果 ClickHouse 不能根據 TTL move 表達式移動某些分片,則會稍後嘗試移動。

何時評估 TTL move?

ClickHouse 會在以下情況下評估 TTL move 表達式:

你插入分片時

後台 move 進程池處理任務喚醒時

ClickHouse 創建新分片作為後台合併過程的結果後

副本下載新分片時

一些已知的極端情況

沒有什麼是完美的,所以這裡列出了一些已知的與 TTL move 相關的極端情況,請你注意。

沒有 SQL 語句來強制執行 TTL move 而不執行分片合併。

TTL move 和 delete 之間的行為差異。

由於 I/O 瓶頸,在同一物理磁盤內的大量分片的多線程移動會有損性能。

結論

在本文中,我們研究了新的 TTL move 功能,以及它如何擴展新存儲策略的用法。藉助 TTL move,ClickHouse 擁有一個強大的工具來管理使用多卷存儲情況下的數據存儲方式。雖然仍存在一些極端情況,但我們在努力解決。你無需顧慮這些情況,不妨嘗試一下,看看存儲策略和 TTL move 如何顯著降低存儲成本,為你節約資金。新的 TTL move 將幫助你把數據存儲到合適的地方。

後續

您已經了解了在 ClickHouse 中處理實時更新相關內容,本系列還包括其他內容:

在 ClickHouse 中處理實時更新

使用新的 TTL move,將數據存儲在合適的地方(本文)

在 ClickHouse 物化視圖中使用 Join

ClickHouse 聚合函數和聚合狀態

ClickHouse 中的嵌套數據結構

獲取更多EMR ClickHouse相關信息,可查看產品文檔:

https://help.aliyun.com/document_detail/212195.html

釘釘掃描下方二維碼加入產品交流群一起參與討論~

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 鑽石舞台 的頭像
    鑽石舞台

    鑽石舞台

    鑽石舞台 發表在 痞客邦 留言(0) 人氣()