環(huán)境準(zhǔn)備
準(zhǔn)備了4臺(tái)機(jī)器,Centos7.6年栓,可以通過如下命令查看系統(tǒng)版本
配置hosts
vim /etc/hosts
編輯hosts文件峻呕,新增機(jī)器hostname與ip的匹配關(guān)系范嘱,每臺(tái)機(jī)器上都要填寫集群的4臺(tái)機(jī)器的配置關(guān)系。
單機(jī)安裝clickhouse
通過https://repo.yandex.ru/clickhouse/rpm/stable/x86_64下載如下文件清單
使用sudo rpm -ivh xxxx.rpm命令安裝所有文件
sudo rpm -ivh clickhouse-common-static-19.17.6.36-2.x86_64.rpm
sudo rpm -ivh clickhouse-common-static-dbg-19.17.6.36-2.x86_64.rpm
sudo rpm -ivh clickhouse-server-19.17.6.36-2.noarch.rpm
sudo rpm -ivh clickhouse-client-19.17.6.36-2.noarch.rpm
安裝完成后普碎,通過service clickhouse-server start命令啟動(dòng)clickhouse服務(wù)
通過clickhouse-client命令連接到服務(wù)吼肥,使用service clickhouse-server stop命令停止服務(wù)。
安裝成功后,部分重要文件的默認(rèn)路徑如下:
服務(wù)器配置文件:/etc/clickhouse-server/config.xml
用戶配置文件:/etc/clickhouse-server/users.xml
數(shù)據(jù)存放路徑(可修改):/var/lib/clickhouse/
日志存放路徑(可修改):/var/log/clickhouse-server/
需要修改如下的配置信息:
- 開放遠(yuǎn)程登錄
默認(rèn)情況下clickhouse服務(wù)僅支持本地登錄(localhost:9000)潜沦,開放遠(yuǎn)程登錄需要修改/etc/clickhouse-server/config.xml文件萄涯。在命令行客戶端使用命令vim /etc/clickhouse-server/config.xml打開文件,找到listen_host節(jié)點(diǎn)唆鸡,取消注釋涝影,保存后退出。
修改使用內(nèi)存的上限
ClickHouse默認(rèn)配置允許使用的內(nèi)容只有不到10G争占,超過該數(shù)值大小的表格將無法進(jìn)行關(guān)聯(lián)燃逻、聚合等操作,可根據(jù)服務(wù)器實(shí)際內(nèi)存大小修改該配置臂痕,打開/etc/clickhouse-server/users.xml文件伯襟,找到max_menory_usage節(jié)點(diǎn),修改后保存退出握童。修改或者添加賬戶
Clickhouse服務(wù)默認(rèn)賬戶為default姆怪,無密碼登錄。如需要修改默認(rèn)賬戶或者添加新賬戶澡绩,請(qǐng)打開/etc/clickhouse-server/users.xml文件稽揭,找到users節(jié)點(diǎn),將其下屬的default節(jié)點(diǎn)注釋掉肥卡,新增test節(jié)點(diǎn)如下圖溪掀,保存后退出。
安裝zookeeper
clickhouse在支持MergeTree引擎的表級(jí)別自動(dòng)備份步鉴,這個(gè)功能是通過zookeeper來實(shí)現(xiàn)的揪胃。副本是表級(jí)別的,不是整個(gè)服務(wù)器級(jí)的氛琢。所以喊递,服務(wù)器里可以同時(shí)有復(fù)制表和非復(fù)制表。副本不依賴分片阳似。每個(gè)分片有它自己的獨(dú)立副本骚勘。
此外,該功能要求zookeeper 3.4.5或更高版本障般。具體安裝過程略调鲸。
clickhouse集群安裝及配置
按照上面按照單機(jī)clickhouse的步驟,在其余3臺(tái)機(jī)器上也分別安裝clickhouse挽荡,然后進(jìn)行集群配置藐石。
集群配置可以在config.xml編輯,但推薦通過 編輯/etc/metrika.xml配置集群定拟,在clickhouse官方文檔中有如下描述
The config can also define “substitutions”. If an element has the
incl
attribute, the corresponding substitution from the file will be used as the value. By default, the path to the file with substitutions is/etc/metrika.xml
. This can be changed in the include_from element in the server config. The substitution values are specified in/yandex/substitution_name
elements in this file. If a substitution specified inincl
does not exist, it is recorded in the log. To prevent ClickHouse from logging missing substitutions, specify theoptional="true"
attribute (for example, settings for macros).
簡(jiǎn)單來講就是對(duì)于config.xml中帶有 'incl'標(biāo)識(shí)的屬性于微,可以在另外的配置文件中修改該屬性的值逗嫡,而另外的配置文件的默認(rèn)路徑就是/etc/metrika.xml。
集群采用2個(gè)分片2個(gè)副本的配置方式株依,
兩個(gè)shard(shardA,shardB),每個(gè)shard 有2個(gè)replica,比如shardA有(A,A1)和shardB有(B,B1)驱证。
具體的配置信息如下:
<yandex>
<clickhouse_remote_servers>
<cluster_2shards_2replicas>
<shard>
<internal_replication>true</internal_replication>
<replica>
<host>host1</host>
<port>9000</port>
</replica>
<replica>
<host>host2</host>
<port>9000</port>
</replica>
</shard>
<shard>
<internal_replication>true</internal_replication>
<replica>
<host>host3</host>
<port>9000</port>
</replica>
<replica>
<host>host4</host>
<port>9000</port>
</replica>
</shard>
</cluster_2shards_2replicas>
</clickhouse_remote_servers>
<!--zookeeper相關(guān)配置-->
<zookeeper-servers>
<node index="1">
<host>host1</host>
<port>2181</port>
</node>
<node index="2">
<host>host2</host>
<port>2181</port>
</node>
<node index="3">
<host>host3</host>
<port>2181</port>
</node>
<node index="4">
<host>host4</host>
<port>2181</port>
</node>
</zookeeper-servers>
<macros>
<layer>01</layer>
<shard>01</shard>
<replica>cluster_116_01_02</replica>
</macros>
<networks>
<ip>::/0</ip>
</networks>
<clickhouse_compression>
<case>
<min_part_size>10000000000</min_part_size>
<min_part_size_ratio>0.01</min_part_size_ratio>
<method>lz4</method>
</case>
</clickhouse_compression>
</yandex>
其中需要注意的是<internal_replication>true</internal_replication>,true代表當(dāng)我們插入數(shù)據(jù)是恋腕,clickhouse只需要負(fù)責(zé)插入兩個(gè)shard中的一個(gè)副本就可以抹锄,另外一個(gè)副本的數(shù)據(jù)由zookeeper負(fù)責(zé)同步,這樣就可以保證當(dāng)其中的某個(gè)副本實(shí)例宕機(jī)時(shí)的數(shù)據(jù)一致性荠藤。
另外在macros的部分是宏配置伙单,這樣在建立ReplicatedMergeTree的表時(shí)就不用在每個(gè)實(shí)例上一一執(zhí)行建表語句,只需要在一個(gè)實(shí)例上執(zhí)行就可以哈肖。
數(shù)據(jù)測(cè)試
按照官方文檔樣例吻育,先下載示例數(shù)據(jù)
官方鏈接
for s in `seq 1987 2018`
do
for m in `seq 1 12`
do
wget https://transtats.bts.gov/PREZIP/On_Time_Reporting_Carrier_On_Time_Performance_1987_present_${s}_${m}.zip
done
done
該樣例數(shù)據(jù)大約有幾十個(gè)G,我只下載了其中一個(gè)月的數(shù)據(jù)作為測(cè)試
建表
CREATE TABLE `ontime` (
`Year` UInt16,
`Quarter` UInt8,
`Month` UInt8,
`DayofMonth` UInt8,
`DayOfWeek` UInt8,
`FlightDate` Date,
`UniqueCarrier` FixedString(7),
`AirlineID` Int32,
`Carrier` FixedString(2),
`TailNum` String,
`FlightNum` String,
`OriginAirportID` Int32,
`OriginAirportSeqID` Int32,
`OriginCityMarketID` Int32,
`Origin` FixedString(5),
`OriginCityName` String,
`OriginState` FixedString(2),
`OriginStateFips` String,
`OriginStateName` String,
`OriginWac` Int32,
`DestAirportID` Int32,
`DestAirportSeqID` Int32,
`DestCityMarketID` Int32,
`Dest` FixedString(5),
`DestCityName` String,
`DestState` FixedString(2),
`DestStateFips` String,
`DestStateName` String,
`DestWac` Int32,
`CRSDepTime` Int32,
`DepTime` Int32,
`DepDelay` Int32,
`DepDelayMinutes` Int32,
`DepDel15` Int32,
`DepartureDelayGroups` String,
`DepTimeBlk` String,
`TaxiOut` Int32,
`WheelsOff` Int32,
`WheelsOn` Int32,
`TaxiIn` Int32,
`CRSArrTime` Int32,
`ArrTime` Int32,
`ArrDelay` Int32,
`ArrDelayMinutes` Int32,
`ArrDel15` Int32,
`ArrivalDelayGroups` Int32,
`ArrTimeBlk` String,
`Cancelled` UInt8,
`CancellationCode` FixedString(1),
`Diverted` UInt8,
`CRSElapsedTime` Int32,
`ActualElapsedTime` Int32,
`AirTime` Int32,
`Flights` Int32,
`Distance` Int32,
`DistanceGroup` UInt8,
`CarrierDelay` Int32,
`WeatherDelay` Int32,
`NASDelay` Int32,
`SecurityDelay` Int32,
`LateAircraftDelay` Int32,
`FirstDepTime` String,
`TotalAddGTime` String,
`LongestAddGTime` String,
`DivAirportLandings` String,
`DivReachedDest` String,
`DivActualElapsedTime` String,
`DivArrDelay` String,
`DivDistance` String,
`Div1Airport` String,
`Div1AirportID` Int32,
`Div1AirportSeqID` Int32,
`Div1WheelsOn` String,
`Div1TotalGTime` String,
`Div1LongestGTime` String,
`Div1WheelsOff` String,
`Div1TailNum` String,
`Div2Airport` String,
`Div2AirportID` Int32,
`Div2AirportSeqID` Int32,
`Div2WheelsOn` String,
`Div2TotalGTime` String,
`Div2LongestGTime` String,
`Div2WheelsOff` String,
`Div2TailNum` String,
`Div3Airport` String,
`Div3AirportID` Int32,
`Div3AirportSeqID` Int32,
`Div3WheelsOn` String,
`Div3TotalGTime` String,
`Div3LongestGTime` String,
`Div3WheelsOff` String,
`Div3TailNum` String,
`Div4Airport` String,
`Div4AirportID` Int32,
`Div4AirportSeqID` Int32,
`Div4WheelsOn` String,
`Div4TotalGTime` String,
`Div4LongestGTime` String,
`Div4WheelsOff` String,
`Div4TailNum` String,
`Div5Airport` String,
`Div5AirportID` Int32,
`Div5AirportSeqID` Int32,
`Div5WheelsOn` String,
`Div5TotalGTime` String,
`Div5LongestGTime` String,
`Div5WheelsOff` String,
`Div5TailNum` String
) ENGINE = MergeTree
PARTITION BY Year
ORDER BY (Carrier, FlightDate)
SETTINGS index_granularity = 8192;
導(dǎo)入數(shù)據(jù)
for i in *.zip; do echo $i; unzip -cq $i '*.csv' | sed 's/\.00//g' | clickhouse-client --host=example-perftest01j --query="INSERT INTO ontime FORMAT CSVWithNames"; done
建立ReplicatedMergeTree表
CREATE TABLE IF NOT EXISTS dwhtl.ontime_replica ON CLUSTER cluster_2shards_2replicas (
`Year` UInt16,
`Quarter` UInt8,
`Month` UInt8,
`DayofMonth` UInt8,
`DayOfWeek` UInt8,
`FlightDate` Date,
`UniqueCarrier` FixedString(7),
`AirlineID` Int32,
`Carrier` FixedString(2),
`TailNum` String,
`FlightNum` String,
`OriginAirportID` Int32,
`OriginAirportSeqID` Int32,
`OriginCityMarketID` Int32,
`Origin` FixedString(5),
`OriginCityName` String,
`OriginState` FixedString(2),
`OriginStateFips` String,
`OriginStateName` String,
`OriginWac` Int32,
`DestAirportID` Int32,
`DestAirportSeqID` Int32,
`DestCityMarketID` Int32,
`Dest` FixedString(5),
`DestCityName` String,
`DestState` FixedString(2),
`DestStateFips` String,
`DestStateName` String,
`DestWac` Int32,
`CRSDepTime` Int32,
`DepTime` Int32,
`DepDelay` Int32,
`DepDelayMinutes` Int32,
`DepDel15` Int32,
`DepartureDelayGroups` String,
`DepTimeBlk` String,
`TaxiOut` Int32,
`WheelsOff` Int32,
`WheelsOn` Int32,
`TaxiIn` Int32,
`CRSArrTime` Int32,
`ArrTime` Int32,
`ArrDelay` Int32,
`ArrDelayMinutes` Int32,
`ArrDel15` Int32,
`ArrivalDelayGroups` Int32,
`ArrTimeBlk` String,
`Cancelled` UInt8,
`CancellationCode` FixedString(1),
`Diverted` UInt8,
`CRSElapsedTime` Int32,
`ActualElapsedTime` Int32,
`AirTime` Int32,
`Flights` Int32,
`Distance` Int32,
`DistanceGroup` UInt8,
`CarrierDelay` Int32,
`WeatherDelay` Int32,
`NASDelay` Int32,
`SecurityDelay` Int32,
`LateAircraftDelay` Int32,
`FirstDepTime` String,
`TotalAddGTime` String,
`LongestAddGTime` String,
`DivAirportLandings` String,
`DivReachedDest` String,
`DivActualElapsedTime` String,
`DivArrDelay` String,
`DivDistance` String,
`Div1Airport` String,
`Div1AirportID` Int32,
`Div1AirportSeqID` Int32,
`Div1WheelsOn` String,
`Div1TotalGTime` String,
`Div1LongestGTime` String,
`Div1WheelsOff` String,
`Div1TailNum` String,
`Div2Airport` String,
`Div2AirportID` Int32,
`Div2AirportSeqID` Int32,
`Div2WheelsOn` String,
`Div2TotalGTime` String,
`Div2LongestGTime` String,
`Div2WheelsOff` String,
`Div2TailNum` String,
`Div3Airport` String,
`Div3AirportID` Int32,
`Div3AirportSeqID` Int32,
`Div3WheelsOn` String,
`Div3TotalGTime` String,
`Div3LongestGTime` String,
`Div3WheelsOff` String,
`Div3TailNum` String,
`Div4Airport` String,
`Div4AirportID` Int32,
`Div4AirportSeqID` Int32,
`Div4WheelsOn` String,
`Div4TotalGTime` String,
`Div4LongestGTime` String,
`Div4WheelsOff` String,
`Div4TailNum` String,
`Div5Airport` String,
`Div5AirportID` Int32,
`Div5AirportSeqID` Int32,
`Div5WheelsOn` String,
`Div5TotalGTime` String,
`Div5LongestGTime` String,
`Div5WheelsOff` String,
`Div5TailNum` String
) ENGINE = ReplicatedMergeTree('/clickhouse/tables/{layer}-{shard}/ontime_replica', '{replica}')
PARTITION BY FlightDate
ORDER BY (Year,FlightDate)
SETTINGS index_granularity = 8192;
復(fù)制表引擎:ReplicatedMergeTree('shard_name','replicate_name')
如上淤井,復(fù)制表要指定兩個(gè)參數(shù)布疼。
- 當(dāng)前本地復(fù)制表實(shí)例所屬的分片服務(wù)名稱。
分片服務(wù)名是zookeeper上的目錄名稱币狠,如果你知道zookeeper實(shí)現(xiàn)的統(tǒng)一命名服務(wù)游两,那就好理解了,類似dubbo的服務(wù)提供者在zookeeper注冊(cè)的服務(wù)名总寻。在dubbo中同一個(gè)服務(wù)名有多個(gè)實(shí)例提供相同的服務(wù)器罐。服務(wù)調(diào)用者只需要通過服務(wù)名就能夠獲取到該服務(wù)的所有實(shí)例的信息梢为。復(fù)制表這和dubbo差不多渐行。分片服務(wù)名就是在zookeeper上注冊(cè)的服務(wù)提供者名稱。多個(gè)復(fù)制表的該名稱如果一樣铸董,那么這些復(fù)制表都屬于同一個(gè)分片服務(wù)祟印,只不過表示不同副本而已。同一個(gè)分片服務(wù)的不同副本之間數(shù)據(jù)會(huì)相互同步粟害,保持一致蕴忆。 - 當(dāng)前這張表所屬的副本名稱。如果第一個(gè)參數(shù)相同悲幅,當(dāng)前第二個(gè)參數(shù)需要不同套鹅。
復(fù)制表機(jī)制就僅僅只是提供一種副本機(jī)制。屬于同一個(gè)分片服務(wù)的不同復(fù)制表之間會(huì)相互同步數(shù)據(jù)汰具。但在查詢某個(gè)副本時(shí)卓鹿,這個(gè)副本宕機(jī)了還無法把這個(gè)查詢自動(dòng)切換到其他副本查詢。需要重新去另外一個(gè)未宕機(jī)的副本實(shí)例上查詢那個(gè)副本對(duì)應(yīng)的本地復(fù)制表留荔。雖然不同副本的數(shù)據(jù)是一樣的吟孙,但對(duì)用戶來說,某個(gè)副本宕機(jī)了還需要手動(dòng)切換查詢的副本實(shí)例。
如果要做到在某個(gè)副本宕機(jī)時(shí)自動(dòng)切換到其他可用副本杰妓,那么就需要結(jié)合Distributed分布式表進(jìn)行使用了
建立Distributed分布式表(可以理解為視圖)
CREATE TABLE ontime_all AS ontime_replica
ENGINE = Distributed(cluster_2shards_2replicas, dwhtl, ontime_replica, rand())
插入數(shù)據(jù)
insert into ontime_all select * from ontime
在每個(gè)節(jié)點(diǎn)上登錄clickhouse查詢驗(yàn)證數(shù)據(jù)藻治。
然后關(guān)閉其中一個(gè)節(jié)點(diǎn)后,ontime仍可查詢并且數(shù)據(jù)記錄完整巷挥。
向OnTime表再插入1000條數(shù)據(jù)桩卵,然后再啟動(dòng)剛才關(guān)閉的節(jié)點(diǎn),驗(yàn)證zookeeper的自動(dòng)同步功能