
什么是分發(fā)引擎?
分發(fā)引擎在業(yè)務用來建立all表時使用。
all表的概念可以理解為一個視圖。
在all表上讀數(shù)據(jù)時CH數(shù)據(jù)流程如下:
1.分發(fā)SQL到對應多個shard上執(zhí)行SQL
2.執(zhí)行SQL后的數(shù)據(jù)的中間結(jié)果發(fā)送到主server上
3.數(shù)據(jù)再次匯總過濾。
如下圖所示:


在各shard執(zhí)行SQL是自動且并行化的,無需參數(shù)配置或手動干預。讀數(shù)時隨機選擇某個shard的replica進行讀書。如果表有索引優(yōu)先使用索引。
分布式引擎接受參數(shù)有:服務器配置文件中的集群名稱,遠程數(shù)據(jù)庫的名稱,遠程表的名稱以及(可選)分片鍵。例:
Distributed(logs, default, hits[, sharding_key])
以上面的建表引擎作為例子。
參數(shù)說明:
logs :?服務器配置文件中的群集名稱。rpm包裝好后的配置文件在/etc/clickhouse-server/config.xml
default: 庫名,也可以使用常量表達式來代替數(shù)據(jù)庫名稱,如currentDatabase()
hits:表名
sharding_key:路由算法
上面引擎的隱喻是:定位logs集群,從位于群集中每個服務器上的“default.hits”表中讀取數(shù)據(jù)。數(shù)據(jù)將在遠程服務器部分處理。例如,對于使用GROUP BY的查詢,將在遠程服務器上聚合數(shù)據(jù),聚合函數(shù)的中間狀態(tài)將發(fā)送到請求者服務器。數(shù)據(jù)將會進一步聚合。
配置信息詳解

這個配置文件一般叫做metrika.xml,當然你也可以給這個文件自定義一個名字,但是別忘了在主config.xml中通過include_from標簽包含進去。否則,服務會報找不到cluster的錯誤。metrika.xml 文件中一般存集群配置、ZK配置、分片配置等。
這個配置文件告訴我們說:
我定義了一個名字為"logs"的集群,由兩個shard(分片)組成,每個shard包含兩個副本。其中shard的概念,是指包含不同數(shù)據(jù)部分的服務器(所有Shard上的數(shù)據(jù)加起來是整個數(shù)據(jù)集)。副本是復制服務器(可以訪問任何副本上的數(shù)據(jù),副本是主主復制)。
對每個服務器來說,有兩個強制參數(shù)(host,port),兩個可選參數(shù)(user,password)。
host:?遠程服務器的主機地址??梢灾付橛蛎Pv4或IPv6地址。如果指定域,服務器將在啟動時執(zhí)行DNS查找。如果DNS請求失敗,服務器將無法啟動。如果修改了DNS記錄,請重新啟動服務器以使新記錄生效。
port:服務器之間通信的TCP端口(配置文件中的tcp_port,默認是9000)
user:連接到遠程服務器的用戶名稱。默認情況下,用戶是“default”。此用戶必須具有連接到遠程服務器的訪問權(quán)限。在users.xml配置文件訪問權(quán)限的管理。
password:以明文登錄到遠程服務器的密碼。默認為空字符串。
當一個讀取的sql落到一個shard上時,分片將選擇一個可用副本。當然你也可以通過配置負載均衡算法(副本訪問的優(yōu)先級,參閱‘load_balancing’設置)。如果與服務器的連接沒有建立成功,則在短暫超時后繼續(xù)嘗試連接。如果連接失敗,下一個副本將被選中,以此類推。如果所有副本的連接嘗試失敗,會以相同的方式重復幾次嘗試。與此同時,遠程服務器仍然會接受連接,但大概率不能正常提供服務。
在寫配置文件的時候,你可以只寫一個分片,這時候就沒有分發(fā)的概念了,因為所有的數(shù)據(jù)都將落在這一個shard上。你也可以搞N個分片,每個分片搞N個副本。每個分片的副本數(shù)量可以不同。
你也可以在配置文件中配任意數(shù)量的cluster。
查看集群
查看我當前有幾個集群,運行命令:

分發(fā)引擎讓你使用起來就好像在用本地服務器(其實它已經(jīng)在分布式的工作了)。注意,群集是不可擴展的:必須將其配置寫入每個服務器的配置文件。
不支持查看其他分布式表的Distributed表(除非分布式表只有一個分片)。作為替代方法,使分布式表查看“最終”表。
分發(fā)引擎需要編寫集群配置文件。修改后的配置的可熱更新,不需要重新啟動服務器。如果需要每次都向未知的分片和副本發(fā)送查詢,無需創(chuàng)建分布式表,推薦使用“遠程”表格功能。請參閱“table functions”。
有兩種將數(shù)據(jù)寫入集群的方法:
1. 你想往哪些服務器寫哪些數(shù)據(jù),直接通過分片去寫入。
2.通過distributed表灌數(shù),引擎將通過sharding key (最后一個參數(shù),必須指定)算法分散數(shù)據(jù)落到不同的服務器上。如果只有一個分片的情況下,無需指定sharding key。
每個分片都可以在配置文件中定義一個權(quán)重。默認情況下,權(quán)重等于1。數(shù)據(jù)以與分片權(quán)重成正比的量分布在分片上。例如,如果有兩個分片,第一個的權(quán)重是9,第二個的權(quán)重是10,則第一個將存放9/19數(shù)據(jù)集,第二個將存放10/19。
每個分片都可以在配置文件中定義“internal_replication”參數(shù)。如果此參數(shù)設置為“true”,則寫入操作會選擇第一個健康的replica并向其寫入數(shù)據(jù)。然后各個replica之間通過zookeeper自動同步數(shù)據(jù),類似于replicated表的數(shù)據(jù)同步模式。如果它設置為'false'(默認),數(shù)據(jù)將被寫入所有副本。實質(zhì)上,這意味著Distributed表本身復制數(shù)據(jù)。這比使用復制表要糟糕,因為副本的一致性未被檢查,并且隨著時間的推移,它們的數(shù)據(jù)會存在部分不一致的情況。
分片表達式(sharding key)
分析分片的表達式(sharding key)用來決定將數(shù)據(jù)寫入到哪個分片。
最后一項sharding_key是可選的,可以是一個表達式,例如rand(),可以是一列 例如user_id,(integer類型),通過對余數(shù)值進行取余分片,(rand()函數(shù)返回值/shards總權(quán)重)【這有個好處就是特定的Userid會落到特定的分片上,從而簡化了用戶的in和join操作】。如果擔心分片數(shù)據(jù)不均勻,也可以加上hash函數(shù),如intHash64(user_id)
假設現(xiàn)在來了一條數(shù)據(jù),這條數(shù)據(jù)要寫到哪個分片呢?我們來看看這里的算法實現(xiàn)。
首先假設我們有3個分片,權(quán)重分別是9,10,11。
3個shard分割出如下3個權(quán)重空間[0,9);[9,19);[19,30]。其中,第一個shard擁有第一個權(quán)重空間,第二個shard擁有第二個權(quán)重空間,第3個shard擁有第三個權(quán)重空間。
權(quán)重空間分割的計算方式:[prev_weight,pre_weights+weight) ... 其中,prev_weights是最左分片權(quán)重的總和,weight表示當前shard權(quán)重,示例如上。
現(xiàn)在來了一條待寫入的row,我們看看寫入時發(fā)生了什么?
1.sharding key表達式先被解析,假定sharding key表達式是rand()函數(shù),函數(shù)返回值是43。
2.函數(shù)返回值/shards權(quán)重總和=43/(9+10+11) = 13
3.查找13屬于范圍[9,19),這個權(quán)重空間屬于第二個shard,于是,數(shù)據(jù)將落到第二個shard上。
對于sharding key的選擇來說,劃分的一個簡單的余數(shù)(rand算法)是分片的有限解決方案,并不總是合適的。它適用于大中型數(shù)據(jù)(數(shù)十臺服務器),但不適用于大量數(shù)據(jù)(數(shù)百臺服務器或更多)。在后一種情況下,使用主題區(qū)域所需的分片方案,不建議使用distribute表中的條目。
當使用復制表格時,可以復制數(shù)據(jù) - 查看“resharding”部分。但在許多情況下,最好不要這樣做。SELECT查詢被發(fā)送到所有的分片,并且無論數(shù)據(jù)如何分布在分片上(它們可以完全隨機地分配),都可以工作。當你添加一個新的分片時,可以給這個shard配一個較重的權(quán)重 - 數(shù)據(jù)會有一部分落到這個shard上,這導致整體cluster的數(shù)據(jù)分布稍微不均勻,但不會影響正常查詢。
分片方案
在以下場景,應該關(guān)注設計分片方案:
?- 使用查詢需要通過特定的鍵來連接數(shù)據(jù)(IN或JOIN)。如果數(shù)據(jù)被這個鍵分割,你可以使用本地IN或JOIN而不是GLOBAL IN或GLOBAL JOIN,這樣更有效率。?
- 使用大量的服務器(數(shù)百個或更多)以及大量的小型查詢(來自各個不同個體的客戶端 、 網(wǎng)站,廣告商或合作伙伴)。為了使小型查詢不影響整個集群,在單個分片上定位單個客戶端的數(shù)據(jù)是很有意義的?;蛘?,正如我們在Yandex.Metrica中所做的那樣,您可以設置雙層分片:將整個集群劃分為“層”,其中一個層可以由多個分片組成。單個客戶端的數(shù)據(jù)位于單個圖層上,但是可以根據(jù)需要將分片添加到圖層中,在他們內(nèi)部分發(fā)。為每個層創(chuàng)建分布式表,并為全局查詢創(chuàng)建一個共享的分布式表。(聽上去很高大上,這對提供大數(shù)據(jù)的基礎(chǔ)服務提供了一點兒新思路)
數(shù)據(jù)是異步寫入的。對于分配給表的INSERT ,數(shù)據(jù)塊只寫入本地文件系統(tǒng)。數(shù)據(jù)盡快發(fā)送到后臺的遠程服務器。你應該通過檢查表目錄/var/lib/clickhouse/data/database/table /中的文件列表(等待發(fā)送的數(shù)據(jù))來檢查數(shù)據(jù)是否成功發(fā)送。
如果服務器在INSERT到Distributed表之后宕機或重啟(例如,在設備出現(xiàn)故障后),插入的數(shù)據(jù)可能會丟失。如果在表目錄中檢測到損壞的數(shù)據(jù)部分,則將其轉(zhuǎn)移到“已損壞”的子目錄中,不再使用。