楔子
下面來說一下 ClickHouse 管理和運維相關的知識,該部分可以讓 ClickHouse 變得更加安全與健壯。在前面演示的案例中,為了方便,我們一直使用默認的 default 用戶,並且沒有配置密碼,這顯然不符合生產環境的要求。所以接下來,我們就來介紹 ClickHouse 的權限、熔斷機制、數據備份和服務監控等知識。
用戶配置
users.xml 配置文件默認位於 /etc/clickhouse-server 路徑下,ClickHouse 用它來定義用戶相關的配置項,包括系統參數的設定、用戶的定義、權限以及熔斷機制等。
用戶的角色配置(profile)
在 users.xml 中有一個 profiles 標簽,在該標簽中我們可以定義用戶角色,先看看默認配置:

這是 ClickHouse 的默認配置,顯然默認有兩個角色,分別是 default 和 readonly。需要注意的是,這里的 default 和 readonly 指的是角色,每一個用戶都具有一個角色。我們可以在 CLI 中直接切換到想要的角色:
SET profile = '角色名'
我們可以測試一下,首先我們默認使用的是 default 用戶,該用戶對應的角色默認也是 default。然后還有一個 readonly 角色,從名字上也能看出該角色只能讀數據,無法寫數據,因為內部的 readonly 屬性為 1,默認為 0。

當 default 用戶具有 default 角色時,寫數據一切正常,但是將 default 用戶的角色切換為 readonly 時則被告知:Cannot execute query in readonly mode。當然,如果我們在 default 角色對應配置中也加上 <readonly>1</readonly>,那么具有該角色的用戶同樣也會無法寫數據。
在所有的角色配置(profile)中,名稱為 default 的 profile 將作為默認的配置被加載,所以它必須存在。如果缺失名為 default 的 profile,那么 ClickHouse Server 會啟動失敗:
<Error> Application: DB::Exception: Settings profile default not found
profile 還支持繼承,實現繼承的方式是在定義中引用其他的 profile 名稱,例如:
<my_role>
<profile>default</profile>
<!-- <profile>default2</profile> 可以繼承多個-->
<distributed_product_mode>deny</distributed_product_mode>
</my_role>
相當於新建了一個名為 my_role 的角色,然后在對應的 profile 中繼承了 default 的所有配置項,並且使用新的參數值覆蓋了 default 中原有的 distributed_product_mode 配置項。
配置約束
constraints 標簽可以設置一組約束條件,以保障 profile 內的參數值不會被隨意修改,約束條件有如下三種規則:
min:最小值約束,在設置相應參數的時候,取值不能小於該閾值max:最小值約束,在設置相應參數的時候,取值不能大於該閾值readonly:只讀約束,該參數值不能被修改
下面舉例說明:

從上面的配置定義中可以看出,在 default 默認的 profile 內,給兩組參數設置了約束。首先為 max_memory_usage 設置了 min 和 max 閾值;其次為 distributed_product_mode 設置了只讀約束。然后重啟 ClickHouse,並嘗試修改 max_memory_usage 參數,將它改為 50:

可以看到最小值約束阻止了這次修改,接着繼續修改 distributed_product_mode 的值:

同樣配置約束成功阻止了預期外的修改。還有一點需要明確,在 default 中默認定義的 constraints 約束,將作為默認的全局約束,自動被其它 profile 繼承。
用戶
通過 profiles 標簽可以為用戶定義角色,那么可不可以定義用戶呢?顯然是可以的,通過 users 標簽即可。如果打開配置文件,會發現 users 標簽下已經有一個默認的 default 用戶,我們之前使用的一直都是這個用戶,而我們也可以定義一個新用戶,但必須包含如下屬性:
password
password 用於設置登錄密碼,支持明文、SHA256 加密和 double_sha1 加密三種形式,可以任選其中一種進行設置。現在分別介紹它們的使用方法。
1)明文密碼:在使用明文密碼的時候,直接通過 password 標簽定義,例如下面的代碼。
<password>123</password>
如果 password 為空,則表示免密碼登錄。
<password></password>
2)SHA256 加密:在使用 SHA256 加密算法的時候,需要通過 password_sha256_hex 標簽定義密碼,例如下面的代碼。
<password_sha256_hexs>a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3</password_sha256_hexs>
至於計算的方式,可以通過如下命令,比如對 123 進行加密:
[root@satori ~]# echo -n 123 |openssl dgst -sha256
(stdin)= a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3
或者使用 Python:
>>> import hashlib
>>> hashlib.sha256(b"123").hexdigest()
'a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3'
>>>
3)double_sha1 加密:在使用 double_sha1 加密算法的時候,則需要通過 password_double_sha1_hex 標簽定義密碼,例如下面的代碼。
<password_double_sha1_hex>23ae809ddacaf96af0fd78ed04b6a265e05aa257</password_double_sha1_hex>
至於計算的方式,可以通過如下命令,比如對 123 進行加密:
[root@satori ~]# echo -n 123 | openssl dgst -sha1 -binary | openssl dgst -sha1
(stdin)= 23ae809ddacaf96af0fd78ed04b6a265e05aa257
或者使用 Python:
>>> import hashlib
>>> _ = hashlib.sha1(b"123").digest()
>>> hashlib.sha1(_).hexdigest()
'23ae809ddacaf96af0fd78ed04b6a265e05aa257'
>>>
networks
networks 表示被允許登錄的網絡地址,用於限制用戶登錄的客戶端地址,關於這方面的介紹將會在后續展開。
profile
用戶所使用的角色,直接引用相應的名稱即可,例如:
<profile>profile_1</profile>
該配置的語義表示:該用戶使用了名為 profile_1 的角色。
quota
quota 用於設置該用戶能夠使用的資源限額,可以理解成一種熔斷機制。關於這方面的介紹同樣將會在后續展開。
下面我們就來定義一個完整的實例來定義三個用戶,密碼分別使用明文密碼、sha256 加密、double sha1 加密:
<users>
<default> <!-- 默認用戶 -->
...
</default>
<!-- 使用明文密碼 -->
<user_plaintext>
<password>123</password>
<networks>
<ip>::/0</ip>
</networks>
<profile>default</profile>
<quota>default</quota>
</user_plaintext>
<!-- 使用 sha256 加密 -->
<user_sha256>
<password_sha256_hex>a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3</password_sha256_hex>
<networks>
<ip>::/0</ip>
</networks>
<profile>default</profile>
<quota>default</quota>
</user_sha256>
<!-- 使用 double sha1 加密 -->
<user_double_sha1>
<password_double_sha1_hex>23ae809ddacaf96af0fd78ed04b6a265e05aa257</password_double_sha1_hex>
<networks>
<ip>::/0</ip>
</networks>
<profile>default</profile>
<quota>default</quota>
</user_double_sha1>
</users>
由於配置了密碼,那么以后使用指定用戶登錄時,就必須指定密碼了,舉個栗子:

--host 指定 IP,--port 指定端口,--user 指定用戶,我們看到指定用戶為 user_plaintext 進行登錄的時候報錯的,原因就是該用戶設置了密碼,但我們沒有指定。而通過 --password 指定密碼之后,便可登錄成功了。
至於其它用戶類似:
# 配置文件中寫的是加密后的結果,但是登錄時,密碼還是使用加密前的結果,這里是 123
clickhouse-client --user user_sha256 --password 123
clickhouse-client --user user_double_sha1 --password 123
權限管理
權限管理是一個始終都繞不開的話題,ClickHouse 分別從訪問、查詢和數據等角度出發,層層遞進,為我們提供了一個較為立體的權限體系。
訪問權限
訪問層控制是整個權限體系的第一層防護,它又可進一步細分成兩類權限。
網絡訪問權限
網絡訪問權限使用 networks 標簽設置,用於限制客戶端登錄的地址,限制方式可以通過 IP 地址、host 主機名稱實現,兩者選其一即可。
比如通過 IP 地址設置:
<!-- 只能通過 IP 為 127.0.0.1 的主機訪問 -->
<ip>127.0.0.1</ip>
通過 host 主機名稱設置:
<!-- 只能通過主機名為 satori 的主機訪問,另外這里配置主機名的時候還可以使用正則 -->
<host>satori</host>
數據庫與字典訪問權限
在客戶端連入服務之后,可以進一步限制某個用戶數據庫和字典的訪問權限,它們分別通過 allow_databases 和 allow_dictionaries 標簽進行設置。如果不進行定義,則表示不進行限制。
<user_plaintext>
<password>123</password>
...
<allow_databases>
<database>default</database>
<database>kagura_nana</database>
</allow_databases>
<allow_dictionaries>
<dictionary>test_dict</dictionary>
</allow_dictionaries>
</user_plaintext>
通過上述操作,該用戶在登錄之后,將只能看到為其開放了訪問權限的數據庫和字典。
查詢權限
查詢權限是整個權限體系的第二層防護,設置在 profile 中,它決定了擁有此角色的用戶能夠執行的查詢語句,查詢權限可以分為以下四類:
讀權限:包括 SELECT、EXISTS、SHOW 和 DESCRIBE 查詢寫權限:包括 INSERT 和 OPTIMIZE 查詢設置權限:包括 SET 查詢DDL 權限:包括 CREATE、DROP、ALTER、RENAME、ATTACH、DETACH 和 TRUNCATE 查詢其它權限:包括 KILL 和 USE 查詢,任何用戶都可以執行這些查詢
上述權限,可以通過以下兩項配置標簽控制:
- 1)readonly:讀權限、寫權限和設置權限均由此標簽控制,它有三種取值:
當取值為 0 時,表示不進行任何限制(默認值)當取值為 1 時,表示只擁有讀權限,即只能執行 SELECT 、EXISTS、SHOW 和 DESCRIBE當取值為 2 時,表示擁有讀權限和設置權限,相當於在讀權限的基礎上增加了 SET 查詢
- 2)allow_ddl:DDL 權限由此標簽控制,它有兩種取值:
當取值為 0 時,不允許 DDL 查詢當取值為 1 時,允許 DDL 查詢(默認值)
舉個栗子,我們增加一個角色。
<profiles>
<default>...</default>
<readonly>...</readonly>
<profile_test>
<readonly>1</readonly>
<allow_ddl>0</allow_ddl>
</profile_test>
</profiles>
修改配置文件之后,重啟 ClickHouse,然后連接:

我們看到切換角色之后,不再具有 DDL 執行權限。當然數據也是只讀模式,此時寫入數據是失敗的。
至此,權限設置已然生效。
數據行級權限
數據權限是整個權限體系中的第三層防護,它決定了一個用戶能夠看到什么數據,說人話就是數據粒度更細了,可以只將一張表的部分數據暴露給用戶。
而該權限使用 database 標簽定義(位於 users 標簽內部),database 通過定義用戶級別的查詢過濾器來實現數據的行級粒度權限,它的定義規則如下所示:
<databases>
<database_name> <!-- 數據庫名稱 -->
<table_name> <!-- 表名稱 -->
<fileter>id > 10</fileter> <!-- 數據過濾條件,也可以寫復雜的條件 -->
</table_name>
</database_name>
</databases>
我們以之前的 distributed_test_1 為例,測試一下,所以配置文件修改如下:
<user_test_2> <!-- 新建一個角色 -->
<password>123</password>
<networks>
<ip>::/0</ip>
</networks>
<profile>default</profile>
<quota>default</quota>
<databases>
<default>
<distributed_test_1>
<filter>id > 10</filter>
</distributed_test_1>
</default>
</databases>
</user_test_2>
修改之后重啟 ClickHouse:

整個過程還是很好理解的,那么 ClickHouse 底層是怎么做的呢?答案很簡單,從配置文件中 filter 標簽的內容也能看出來,就是追加了一個 WHERE 條件。
對於數據權限的使用有一點需要明確,在使用了這項功能之后,PREWHERE 優化將不再生效。所以是直接利用 ClickHouse 的內置過濾器,還是通過拼接 WHERE 查詢條件的方式實現行級過濾,需要根據使用場景進行權衡。
熔斷機制
熔斷是限制資源被過度使用的一種自我保護機制,當使用的資源數量達到閾值時,那么正在進行的操作會被自動中斷。而按照使用資源統計方式的不同,熔斷機制可以分為兩類。
根據時間周期的累積用量熔斷
在這種方式下,系統資源的用量是按照時間周期累積統計的,當累積量達到闞值,則直到下個計算周期開始之前,該用戶將無法繼續進行操作。這種方式通過 users.xml 內的 quotas 標簽來定義資源配額,以默認的配置為例:

看圖中最后一行,是 </yandex>,顯然到此配置文件就結束了,所以 users.xml 中最外層是 yandex 標簽,然后 yandex 標簽里面有三個子標簽,分別是 profiles、users、quotas,分別用於定義角色、定義用戶、熔斷限流,還是比較簡單的,整個結構比較清晰。然后看看 quotas 標簽里面的內容都代表什么含義吧。
default:自定義名稱,全局唯一duration:累積的時間周期,單位秒queries:在周期內允許執行的查詢次數,0 表示不限制errors:在周期內允許發生異常的次數,0 表示不限制result_row:在周期內允許查詢返回的結果行數,0 表示不限制read_row:在周期內,在分布式查詢中,允許遠端節點讀取的數據行數,0 表示不限制execution_time:周期內允許執行的查詢時間,單位秒,0 表示不限制
我們來配置一下:
<!-- 在 quotas 標簽中增加如下配置 -->
<limit_1>
<interval>
<duration>3600</duration>
<queries>2</queries>
<errors>0</errors>
<result_rows>0</result_rows>
<read_rows>0</read_rows>
<execution_time>0</execution_time>
</interval>
</limit_1>
在名為 limit_1 的配置中,1 個小時的周期內只允許最多 2 次查詢,下面讓其作用在 user_test_2 用戶上。
<user_test_2>
...
<quota>limit_1</quota> <!-- 其它部分不變,將 quota 的值從 default 改成 limit_1 -->
...
</user_test_2>
然后重啟 ClickHouse,並以 user_test_2 用戶啟動。

執行兩次查詢之后,如果再執行的話就會報錯,證明熔斷機制確實已經生效。
根據單次查詢的用量熔斷
在這種方式下,系統資源的用量是按照單次查詢統計的,而具體的熔斷規則,則是由許多不同配置項組成的,這些配置項需要定義在用戶 profile 中。如果某次查詢使用的資源用量達到了閾值,則會被中斷。以配置項 max memory_usage 為例,它限定了單次查詢可以使用的內存用量,在默認的情況下其規定不得超過 10 GB,如果一次查詢的內存用量超過 10 GB,則會得到異常。需要注意的是,在單次查詢的用量統計中,ClickHouse 是以分區為最小單元進行統計的(不是數據行的粒度),這意味着單次查詢的實際內存用量是有可能超過閾值的。
熔斷相關的配置比較多,這里介紹幾個常用的。
1)max_memory_usage:在單個 ClickHouse 服務進程中,運行一次查詢限制使用的最大內存量,默認值為 10GB。
<max_memory_usage>10000000000</max_memory_usage>
2)max_memory_usage_for_user:在單個 ClickHouse 服務進程中,以用戶為單位進行統計,單個用戶在運行查詢時限制使用的最大內存量,默認值為 0,即不做限制。
3)max_memory_usage_for_all_queries:在單個 ClickHouse 服務進程中,所有運行的查詢累加在一起所限制使用的最大內存量,默認值為 0,即不做限制。
4)max_partitions_per_insert_block:在單次 INSERT 寫入的時候,限制創建的最大分區個數,默認值為 100 個。如果超過這個閾值,將會出現異常。
Too many partitions for single INSERT block ······
5)max_rows_to_group_by:在執行 GROUP BY 聚合查詢的時候,限制去重后的聚合 KEY 的最大個數,默認值為 0,不做限制。當超過閾值時,其處理方式由 group_by_overflow_mode 決定。
6)group_by_overflow_mode:當 max_rows_to_group_by 熔斷規則觸發時,group_by_overflow_mode 將會提供三種處理方式。
throw:拋出異常,此乃默認值break:立即停止查詢,並返回當前數據any:僅根據當前已存在的聚合 KEY 繼續完成聚合查詢
7)max_bytes_before_external_group_by:在執行 GROUP BY 查詢的時候,限制使用的最大內存量,默認值為 0,不做限制。當超過閾值時,聚合查詢將會進一步借用本地磁盤。
數據備份
在之前的系列中,我們已經知道了數據副本的使用方法,那么問題來了:既然已經有了數據副本,那么還需要數據備份嗎?顯然數據備份是需要的,因為數據副本並不能處理誤刪數據這類行為。ClickHouse自身提供了多種備份數據的方法,根據數據規模的不同,可以選擇不同的形式。
導出文件備份
如果數據的體量較小,可以通過 dump 形式將數據導出為本地文件,語句如下:
clickhouse-client --query="SELECT * FROM table_name" > table_name.tsv
如果想將備份數據導入的話,可以這么做:
cat table_name.csv | clickhouse-client --query="INSERT INTO table_name FORMAT TSV"
上述這種 dump 形式的優勢在於,可以利用 SELECT 查詢並篩選數據,然后按需備份。如果是備份整個表的數據,也可以直接復制它的整個目錄文件。
通過快照表備份
快照表實質上就是普通的數據表,它通常按照業務規定的備份頻率創建,例如按天或者按周創建。所以首先需要建立一張與原表結構相同的數據表,然后再使用 INSERT INTO SELECT句式,點對點地將數據從原表寫人備份表。假設數據表 table_name 需要按日進行備份,現在為它創建當天的備份表:
CREATE TABLE table_name_bak AS table_name
有了備份表之后就可以點對點地備份數據了。
INSERT INTO table_name_bak SELECT * FROM table_name
如果考慮到容災問題,也可以將備份表放在不同的 ClickHouse 節點上,此時需要將 SQL 語句改成遠程查詢的形式:
INSERT INTO table_name_bak SELECT * FROM remote('xx.xx.xx.xx:9000', 'default', 'table_name', 'default')
按分區備份
基於數據分區的備份,ClickHouse 目前提供了 FREEZE 和 FETCH 兩種方式,現在分別介紹它們的使用方法。
使用 FREEZE 備份
FREEZE 的完整語法如下所示:
ALTER TABLE table_name FREEZE PARTITION partition_expr
分區在被備份之后,會被統一保存到 ClickHouse 根路徑 /shadow/N 子目錄下。其中 N 是一個自增長的整數,它的含義是備份的次數(FREEZE 執行過多少次),具體次數由 shadow 子目錄下的 increment.txt 文件負責記錄。而分區備份實質上是對原始目錄文件進行硬鏈接操作,所以並不會導致額外的存儲空間。整個備份的目錄會一直向上追溯至 data 根路徑的整個鏈路:

上面對 partition_test_v1 表的 202008 分區進行了備份,然后我們進入 shadow 子目錄,便可看到之前備份的分區目錄。

如果想還原分區,則需要借助於 ATTACH 裝載分區的方式實現,我們需要先將 shadow 子目錄下的分區文件復制到相應數據表的 detached 目錄下,然后再使用 ATTACH 語句裝載。
使用 FETCH 備份
FETCH 只支持 ReplicatedMergeTree 系列的表引擎,其完整語法如下所示:
ALTER TABLE table_name FETCH PARTITION partition_id FROM zk_path
其工作原理與 ReplicatedMergeTree 同步數據的原理類似,FETCH 通過指定的 zk_path 找到 ReplicatedMergeTree 的所有副本實例,然后從中選擇一個最合適的副本,並下載相應的分區數據。例如執行如下語句:
ALTER TABLE test_fetch FETCH PARTITION 202009 FROM '/clickhouse/tables/02/test_fetch'
表示將 test_fetch 的 201909 分區下載到本地,並保存到對應數據表的 detached 目錄下,目錄如下所示:
data/default/test_fetch/detached/202009_0_0_0
與 FREEZE 一樣,對於備份分區的還原操作,也需要借助 ATTACH 裝載分區來實現。 另外 FREEZE 和 FETCH 雖然都能實現對分區文件的備份,但是它們並不會備份數據表的元數據。所以說如果想做到萬無一失的備份,還需要對數據表的元數據進行備份,它們是 /data/metadata 目錄下的 [table].sql 文件,目前這些元數據需要用戶通過復制的形式單獨備份。
服務監控
基於原生功能對 ClickHouse 進行監控,可以從兩方面入手:系統表和查詢日志,接下來分別介紹它們的使用方法。
系統表
在眾多的 SYSTEM 系統表中,主要由以下三張表支撐了對 ClickHouse 運行指標的查詢,它們分別是 metrics、events 和 asynchronous_metrics。
1)metrics
metrics 表用於統計 ClickHouse 服務在運行時,當前正在執行的高層次的概要信息,包括正在執行的查詢總次數、正在發生合並的操作總次數等。

events
events 表用於統計 ClickHouse 服務在運行過程中,已經執行過的高層次的累積概要信息,包括總的查詢次數、總的 SELECT 查詢次數等。

asynchronous_metrics
asynchronous_metrics 表用於統計 ClickHouse 服務在運行過程中,當前后台正在異步運行的高層次的概要信息,包括當前分配的內存、執行隊列中的任務數量等。

查詢日志
查詢日志目前主要有 6 種類型,它們分別從不同角度記錄了 ClickHouse 的操作行為。所有查詢日志在默認配置下都是關閉狀態,需要在 config.xml 配置中進行更改,接下來分別介紹它們的開啟方法。在配置被開啟之后,ClickHouse 會為每種類型的查詢日志自動生成相應的系統表以供查詢。
1. query_log
query_log 是最常用的查詢日志,它記錄了 ClickHouse 服務中所有已經執行的查詢記錄,它的全局定義方式如下所示:
<query_log>
<database>system</database>
<table>query_log</table>
<partition_by>toYYYYMM(event_date)</partition_by>
<!-- 刷新周期 -->
<flush_interval_milliseconds>7500</flush_interval_milliseconds>
</query_log>
如果只希望具有某些角色用戶才能開啟 query_log,那么可以在 users.xml 的用戶 profile 中增加一個標簽:<log_queries>1</log_querie>。
query_log 開啟后,即可通過相應的系統表對記錄進行查詢。
SELECT * FROM system.query_log
返回的日志信息十分完善,涵蓋了查詢語句、執行時間、返回的數據量和執行用戶等。
2. query_thread_log
query_thread_log 記錄了所有線程的執行查詢的信息,它的全局定義方式如下所示:
<query_thread_log>
<database>system</database>
<table>query_thread_log</table>
<partition_by>toYYYYMM(event_date)</partition_by>
<!-- 刷新周期 -->
<flush_interval_milliseconds>7500</flush_interval_milliseconds>
</query_thread_log>
如果只希望具有某些角色用戶才能開啟 query_log,那么可以在 users.xml 的用戶 profile 中增加一個標簽:<log_query_thread>1</log_query_thread>。
log_query_thread 開啟后,即可通過相應的系統表對記錄進行查詢。
SELECT * FROM system.log_query_thread
返回的日志信息十分完善,涵蓋了線程名稱、查詢語句、執行時間、和內存使用量等。
3. part_log
part_log 記錄了 MergeTree 系列表引擎的分區操作日志,其全局定義方式如下:
<part_log>
<database>system</database>
<table>part_log</table>
<flush_interval_milliseconds>7500</flush_interval_milliseconds>
</part_log>
part_log 開啟后,即可通過相應的系統表對記錄進行查詢。
SELECT * FROM system.part_log
返回的日志信息十分完善,涵蓋了操縱類型、表名稱、分區信息和執行時間等。
4. text_log
text_log 日志記錄了 ClickHouse 運行過程中產生的一系列打印日志,包括 INFO、DEBUG 和 TRACE,它的全局定義方式如下:
<text_log>
<database>system</database>
<table>text_log</table>
<flush_interval_milliseconds>7500</flush_interval_milliseconds>
</text_log>
text_log 開啟后,即可通過相應的系統表對記錄進行查詢。
SELECT * FROM system.text_log
返回的日志信息十分完善,涵蓋了線程名稱、日志對象、日志信息和執行時間等等。
5. metric_log
metric_log 日志用於將 system.metrics 和 system.events 中的數據匯聚到一起,它的全局定義方式如下所示:
<metric_log>
<database>system</database>
<table>metric_log</table>
<flush_interval_milliseconds>7500</flush_interval_milliseconds>
<!-- 收集 metrics 和 event 的時間 -->
<collect_interval_milliseconds>1000</collect_interval_milliseconds>
</metric_log>
metric_log 開啟后,即可通過相應的系統表對記錄進行查詢。
SELECT * FROM system.metric_log
以上就是幾種查詢日志,當然,除了這些自身的查詢日志之外,ClickHouse 還能夠與眾多的第三方系統集成,比如 Prometheus。當然監控系統又是一個比較大的話題了,這里就不再展開了,有興趣可以去調研一下。
