Clickhouse副本與分片

七年·發表於2020-11-21

一 副本與分片概述

副本(replica) 是指兩個相同資料的表或表一部分,作用是為了資料備份與安全
分片(shard) 是指不同的伺服器儲存同一張表的不同部分,作用是為了水平切分表,緩解單一服務的壓力.
針對於副本的需求,有兩種不同的方式,後面會一一概述.

二 下載並安裝zookeeper

clickhouse要實現副本與分片需要依賴於zookeeper,並且zookeeper版本要3.4.5以及以上.
ZK的下載與安裝參考我另外一篇博文Zookeeper的下載與安裝

三 ClickHouse配置zookeeper

安裝啟動好zookeeper後,我們需要在clickhouse中配置zookeeper
clickhouse兩種配置zookeeper方式,一種是直接在config.xml中配置,另外一種是在外部檔案中配置好了,在config.xml中進行引用.

1 內部配置方式:

vim /etc/clickhouse-server/config.xml

新增如下配置:

  <zookeeper>
    <node index="1">  #index是連線zk的順序
        <host>node01</host> #znode地址
        <port>2181</port>   #znode埠
    </node>
    <node index="2">
        <host>node02</host>
        <port>2181</port>
    </node>
    <node index="3">
        <host>node03</host>
        <port>2181</port>
    </node>

2 外部配置方式:

建立外部配置檔案:

vim /etc/clickhouse-server/config.d/zks.xml
<?xml version="1.0"?>
<yandex>
   <zks>
     <node index="1">
            <host>node01</host>
            <port>2181</port>
     </node>
             <node index="2">
            <host>node02</host>
            <port>2181</port>
     </node>
             <node index="3">
            <host>node03</host>
            <port>2181</port>
     </node>
   </zks>
</yandex>

引入外部配置檔案中的配置

vim /etc/clickhouse-server/config.xml
#檔案的路徑
<include_from>/etc/clickhouseserver/config.d/zks.xml</include_from>
#incl中指的是配置在外部配置檔案中的標籤
<zookeeper incl="zks" optional="true" />

上方是麻煩的寫法,如果在zks中的標籤<zks>...</zks>改為<zookeeper>..</zookeeper>,那麼可以不要config.xml中的<zookeeper incl...>標籤,只需要引用外部配置檔案即可.

zk的配置不支援熱更改,必須要重啟clickhouse服務,但是在重啟之前可以先使用以下sql查詢:

 select * from system.zookeeper where path = '/';

會報表不存在.
重啟服務後,再還執行上方sql,就可以查詢到zookeeper表,說明zookeeper配置好了.

4 副本實現方式

1 複製合併樹引擎實現副本

當前直接支援副本的引擎之後Replicatedxxx複製合併樹引擎,對於複製合併樹引擎實現副本我已經寫過了,所以這裡只上鍊接
ClickHouse ReplicatedMergeTree家族引擎

2 通過分片配置實現副本

首先要配置cluster.

vim /etc/clickhouse-server/config.xml

在vim模式下輸入:/remote_servers快速查詢到配置cluster的標籤
在這裡插入圖片描述
在<remote_servers>標籤之中就是我們配置shard與replica的地方
先看下面配置,說明

 <remote_servers incl="clickhouse_remote_servers" >
   <my_shard>
               <shard>
                     <replica>
                            <host>node01</host>
                            <port>9000</port>
                     </replica>
                     <replica>
                            <host>node02</host>
                            <port>9000</port>
                     </replica>
              </shard>
              <shard>
                     <replica>
                            <host>node03</host>
                            <port>9000</port>
                     </replica>
                     <replica>
                            <host>node04</host>
                            <port>9000</port>
                     </replica>
              </shard>
      </my_shard>
 </remote_servers>

</my_shard>是cluster名稱,任意填寫,在後面通過這個名字引用如 on cluster my_shard
<shard>指分片,有幾個分片就配置幾個<shard>標籤
<replica>指副本,有幾個副本就有幾個<replica>標籤.
上方的配置是有兩個分片,每個分片兩個副本.這裡理解也可以是有兩個分片,每個分片一個副本.
打個比方說,一張test表基於以上配置,共有id 1,2,3,4 四條資料,那麼node01中有1,2兩條,node02中有1,2兩條,node03中有3,4兩條,node04中有3,4兩條,這樣子舉例比較好理解.,至於到底是兩個分片兩個副本,還是兩個分片一個副本,隨意,但是本文會說是兩個副本.
因為這裡我們是演示副本的實現,所以就不考慮分片了,分片後面說,所以現在只建立一個分片,分片中有三個副本.
每臺伺服器中都增加以下配置,host換成自己的ip

<remote_servers incl="clickhouse_remote_servers" >
   <my_shard>
               <shard>
                     <replica>
                            <host>node01</host>
                            <port>9000</port>
                          <!-- 預設為default 
                          <user></user>
                          預設為空
                          <password></password> -->
                     </replica>
                     <replica>
                            <host>node02</host>
                            <port>9000</port>
                     </replica>
                     <replica>
                            <host>node03</host>
                            <port>9000</port>
                     </replica>    
              </shard>
      </my_shard>
 </remote_servers>

上面配置好了以後,cluster可以熱載入,所以不需要重啟服務,通過系統表可以檢視cluster

select * from system.clusters where cluster='my_shard';

在這裡插入圖片描述
接下來的任務,需要藉助於分散式表來完成,但是介紹分散式表之前,先說一下分散式ddl
分散式ddl指的是在一臺伺服器上執行sql,其他伺服器同步執行,需要藉助於cluster,如下方

create table tableName on cluster my_shard (id Int8,name String)engine=xxx ;

上面建立的一張表,除了on cluster my_shard以外就是正常的建立表語句,my_shard我們上面配置過,裡面一共包含了node01,node02,node03 三個replica,那麼在執行的時候,就會到my_shard配置下的所有伺服器中執行create table語句.也就是這三臺伺服器任意一臺執行以上sql,三臺的表都會建立好,這就是分散式ddl,分散式ddl也需要zookeeper的支援,同樣也支援drop table…
瞭解了分散式ddl,我們可以在任意一臺機器上建立表:

create table default.replicaTest on cluster my_shard(id Int8,name String) engine =MergeTree order by id;

在這裡插入圖片描述

此時發現my_shard配置的node01,node02,node03上面都建立好了replicaTest表.表建立好了以後,接下來要建立分散式表,分散式表本身不儲存任何資料,可以把它當成資料操作的工具,他會分發到被分散式表代理的其他表.

Distributed(cluster_name, db_name, table_name[, sharding_key[, policy_name]])

第一個是配置的cluster名稱,第二個第三個分別代表代理的資料庫,資料表,第四個引數是資料插入分發策略,指定一個欄位名(必須是Int型別)或者rand(),第五個引數是策略名稱.
下面基於上面的replicaTest建立一個分散式表:

create table replicaTest_all as replicaTest 
engine = Distributed(my_shard,default,replicaTest,id);

我在node01機器上建立了一張分散式表(當然也可以通過分散式ddl,每臺機器上都建立一張分散式表)
建立好了,我們插入資料:

insert into replicaTest_all values(1,'zhang');

插入成功後,每臺機器查詢replicaTest表資料,返現三臺機器上都有1,zhang這條資料.而查詢replicaTest_all分散式表,也只查詢到了1,zhang一條資料.

插入分散式表時,基於my_shard的配置,會把插入的資料全部分發到代理的每一臺伺服器的replicaTest表,這也就是為什麼我們插入replicaTest_all結果三臺機器都有這條資料的原因.而查詢時,會從副本中選擇一個查詢(查詢有策略可以選擇,感興趣可以查詢load_balancing).針對於insert與select會作用於本地表,其他的操作基本都只會作用於分散式表.

通過分散式表的方式完成副本的實現,總結一下,1 配置檔案配置cluster 2 cluster中配置的伺服器上建立表 3 通過分散式表代理本地表,實現資料插入後分發到本地表.
這種資料副本的實現可以是任何引擎,但是對於某臺Clickhouse的壓力比較大,而第一種的副本實現必須是Repicated*合併樹引擎,壓力稍小於分散式表的方式.

通過分散式表的方式實現資料副本時,寫入我們配置的分散式表時,分散式表會往shard下的所有replica都寫入一份資料.如果我們這裡的replicaTest表改成ReplicatedMergeTree引擎,那麼我們就只需要分散式表選擇一個replica寫入即可,由ReplicatedMergeTree完成資料同步,而不需要每個replica都要寫入,這項配置需要新增 <internal_replication>true<internal_replication>

 <my_shard>
               <shard>
                     <internal_replication>true</internal_replication>
                     ......
              </shard>
 </my_shard>

五 分片

上面我們只使用了一個分片,接下來說一下分片

<remote_servers incl="clickhouse_remote_servers" >
   <my_shard_2>
               <shard>
                     <replica>
                            <host>node01</host>
                            <port>9000</port>
                     </replica>
               </shard>
               <shard>       
                     <replica>
                            <host>node02</host>
                            <port>9000</port>
                     </replica>
               </shard>
               <shard>       
                     <replica>
                            <host>node03</host>
                            <port>9000</port>
                     </replica>    
              </shard>
      </my_shard_2>
 </remote_servers>

我們每臺伺服器新增以上配置,此時沒有使用副本,只是三個分片.
查詢分片:
在這裡插入圖片描述
配置好後,通過分散式ddl先建立表

create table replicaTest2 on cluster my_shard_2 (id Int,name String)
engine=MergeTree order by id;

建立分散式表:

create table replicaTest2_all as replicaTest2 
engine =Distributed(my_shard_2,default,replicaTest2,rand());

插入資料

insert into replicaTest2_all values(1,'zhang'),(2,'li'),(3,'zhao'),
(4,'qian'),(5,'sun'),(6,'wang'),(7,'tian'),(8,'he'),(9,'zheng'),(10,'dong');

檢視資料:
node01:

node01.hadoop.com :) select * from replicaTest2;
┌─id─┬─name─┐
│  4 │ qian │
│ 10 │ dong │
└────┴──────┘

node02:

node02.hadoop.com :) select * from replicaTest2;
┌─id─┬─name──┐
│  1 │ zhang │
│  2 │ li    │
│  3 │ zhao  │
│  5 │ sun   │
│  9 │ zheng │
└────┴───────┘

node03:

node03.hadoop.com :) select * from replicaTest2;
┌─id─┬─name─┐
│  6 │ wang │
│  7 │ tian │
│  8 │ he   │
└────┴──────┘

由此可見資料被replicaTest2_all分散式表隨機分發到了三個分片中.
分發有策略,需要做配置<weight>

           <shard>  
                 <weight>2</weight>     
                 <replica>
                        <host>node01</host>
                        <port>9000</port>
                 </replica>    
          </shard>

weight預設為1,既node01 node02 node03 都為1,那麼總的權重為3,每個分片通過分散式表第四個引數sharding_key分配到資料的概率是一樣.
如果我們權重分別設定為1,2,3 那麼總權重是6,那麼總區間就是[0,6),排在shard配置第一位的node01,權重佔比為1/6,所以屬於區間[0,1),排在shard配置第二位的node02,佔比2/6,所以區間為[1,3),至於最後的node03就是[3,6).所以如果rand()產生的數字除以6取餘落在哪個區間,資料就會分發到哪個shard,通過權重配置,可以實現資料按照想要的比重分配.

六 副本與分片

上面分別講述了副本與分片的實現,其實最合適的方法就是利用分散式表實現分片資料的寫入,而每一分片內通過Replicated*引擎實現副本資料的同步,下面實現這個:
三臺伺服器配置my_shard_3


    <my_shard_3>
               <shard>
                      <internal_replication>true</internal_replication>
                     <weight>1</weight>
                     <replica>
                            <host>node01</host>
                            <port>9000</port>
                     </replica>
               </shard>
               <shard>
                      <internal_replication>true</internal_replication>
                      <weight>1</weight>
                     <replica>
                            <host>node02</host>
                            <port>9000</port>
                     </replica>
                     <replica>
                            <host>node03</host>
                            <port>9000</port>
                     </replica>
              </shard>
     </my_shard_3>

此時有兩個shard 第二shard有兩個副本.(因為本人只有三臺伺服器,故這樣分配)
利用分散式ddl建立ReplicatedMergeTree表

CREATE TABLE default.replicaTest3 on cluster my_shard_3
( `id` Int32,`name` String)
ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard_name}/replicaTest3', '{replica_name}')
ORDER BY id

上方的建立表以及巨集變數在上方貼的ClickHouse ReplicatedMergeTree家族引擎連結中已經講過了,這裡不再贅述,下面是我的配置的巨集變數:
node01:

node01.hadoop.com :) select * from system.macros;

┌─macro────────┬─substitution─┐
│ replica_name │ 01           │
│ shard_name   │ 01           │
└──────────────┴──────────────┘

node02:

node02.hadoop.com :) select * from system.macros;

┌─macro────────┬─substitution─┐
│ replica_name │ 01           │
│ shard_name   │ 02           │
└──────────────┴──────────────┘

node03

node03.hadoop.com :) select * from system.macros;

┌─macro────────┬─substitution─┐
│ replica_name │ 02           │
│ shard_name   │ 02           │
└──────────────┴──────────────┘

建立好本地表後,建立分散式表:


CREATE TABLE default.replicaTest3_all
( `id` Int32, `name` String)
ENGINE = Distributed('my_shard_3', 'default', 'replicaTest3', id) 

插入資料:

insert into replicaTest3_all values(1,'zhangfei'),(2,'guanyu'),(3,'liubie'),(4,'zhaoyun'),
(5,'machao'),(6,'caocao'),(7,'lvbu'),(8,'zhuge'),(9,'dianwei');

檢視資料:
node01:

┌─id─┬─name────┐
│  2 │ guanyu  │
│  4 │ zhaoyun │
│  6 │ caocao  │
│  8 │ zhuge   │
└────┴─────────┘

node02和node03

┌─id─┬─name─────┐
│  1 │ zhangfei │
│  3 │ liubie   │
│  5 │ machao   │
│  7 │ lvbu     │
│  9 │ dianwei  │
└────┴──────────┘

至此,最適合生產的分片與副本機制就配置好了.

相關文章