基于zookeeper的高可用clickhouse集群搭建

环境准备

准备了4台机器,Centos7.6,可以通过如下命令查看系统版本


image.png

配置hosts

vim /etc/hosts

编辑hosts文件,新增机器hostname与ip的匹配关系,每台机器上都要填写集群的4台机器的配置关系。

单机安装clickhouse

通过https://repo.yandex.ru/clickhouse/rpm/stable/x86_64下载如下文件清单

image.png

使用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命令启动clickhouse服务
通过clickhouse-client命令连接到服务,使用service clickhouse-server stop命令停止服务。

安装成功后,部分重要文件的默认路径如下:
服务器配置文件:/etc/clickhouse-server/config.xml
用户配置文件:/etc/clickhouse-server/users.xml
数据存放路径(可修改):/var/lib/clickhouse/
日志存放路径(可修改):/var/log/clickhouse-server/

需要修改如下的配置信息:

  1. 开放远程登录
    默认情况下clickhouse服务仅支持本地登录(localhost:9000),开放远程登录需要修改/etc/clickhouse-server/config.xml文件。在命令行客户端使用命令vim /etc/clickhouse-server/config.xml打开文件,找到listen_host节点,取消注释,保存后退出。
  1. 修改使用内存的上限
    ClickHouse默认配置允许使用的内容只有不到10G,超过该数值大小的表格将无法进行关联、聚合等操作,可根据服务器实际内存大小修改该配置,打开/etc/clickhouse-server/users.xml文件,找到max_menory_usage节点,修改后保存退出。

  2. 修改或者添加账户
    Clickhouse服务默认账户为default,无密码登录。如需要修改默认账户或者添加新账户,请打开/etc/clickhouse-server/users.xml文件,找到users节点,将其下属的default节点注释掉,新增test节点如下图,保存后退出。

安装zookeeper

clickhouse在支持MergeTree引擎的表级别自动备份,这个功能是通过zookeeper来实现的。副本是表级别的,不是整个服务器级的。所以,服务器里可以同时有复制表和非复制表。副本不依赖分片。每个分片有它自己的独立副本。
此外,该功能要求zookeeper 3.4.5或更高版本。具体安装过程略。

clickhouse集群安装及配置

按照上面按照单机clickhouse的步骤,在其余3台机器上也分别安装clickhouse,然后进行集群配置。
集群配置可以在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 in incl does not exist, it is recorded in the log. To prevent ClickHouse from logging missing substitutions, specify the optional="true"attribute (for example, settings for macros).

简单来讲就是对于config.xml中带有 'incl'标识的属性,可以在另外的配置文件中修改该属性的值,而另外的配置文件的默认路径就是/etc/metrika.xml。
集群采用2个分片2个副本的配置方式,


image.png

两个shard(shardA,shardB),每个shard 有2个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相关配置-->
<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代表当我们插入数据是,clickhouse只需要负责插入两个shard中的一个副本就可以,另外一个副本的数据由zookeeper负责同步,这样就可以保证当其中的某个副本实例宕机时的数据一致性。
另外在macros的部分是宏配置,这样在建立ReplicatedMergeTree的表时就不用在每个实例上一一执行建表语句,只需要在一个实例上执行就可以。

数据测试

按照官方文档样例,先下载示例数据
官方链接

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

该样例数据大约有几十个G,我只下载了其中一个月的数据作为测试
建表

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;

导入数据

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;

复制表引擎:ReplicatedMergeTree('shard_name','replicate_name')
如上,复制表要指定两个参数。

  1. 当前本地复制表实例所属的分片服务名称。
    分片服务名是zookeeper上的目录名称,如果你知道zookeeper实现的统一命名服务,那就好理解了,类似dubbo的服务提供者在zookeeper注册的服务名。在dubbo中同一个服务名有多个实例提供相同的服务。服务调用者只需要通过服务名就能够获取到该服务的所有实例的信息。复制表这和dubbo差不多。分片服务名就是在zookeeper上注册的服务提供者名称。多个复制表的该名称如果一样,那么这些复制表都属于同一个分片服务,只不过表示不同副本而已。同一个分片服务的不同副本之间数据会相互同步,保持一致。
  2. 当前这张表所属的副本名称。如果第一个参数相同,当前第二个参数需要不同。

复制表机制就仅仅只是提供一种副本机制。属于同一个分片服务的不同复制表之间会相互同步数据。但在查询某个副本时,这个副本宕机了还无法把这个查询自动切换到其他副本查询。需要重新去另外一个未宕机的副本实例上查询那个副本对应的本地复制表。虽然不同副本的数据是一样的,但对用户来说,某个副本宕机了还需要手动切换查询的副本实例。
如果要做到在某个副本宕机时自动切换到其他可用副本,那么就需要结合Distributed分布式表进行使用了

建立Distributed分布式表(可以理解为视图)

 CREATE TABLE ontime_all AS ontime_replica
ENGINE = Distributed(cluster_2shards_2replicas, dwhtl, ontime_replica, rand())

插入数据

insert into ontime_all select * from ontime

在每个节点上登录clickhouse查询验证数据。
然后关闭其中一个节点后,ontime仍可查询并且数据记录完整。
向OnTime表再插入1000条数据,然后再启动刚才关闭的节点,验证zookeeper的自动同步功能

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345