环境准备
准备了4台机器,Centos7.6,可以通过如下命令查看系统版本
配置hosts
vim /etc/hosts
编辑hosts文件,新增机器hostname与ip的匹配关系,每台机器上都要填写集群的4台机器的配置关系。
单机安装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命令启动clickhouse服务
通过clickhouse-client命令连接到服务,使用service clickhouse-server stop命令停止服务。
安装成功后,部分重要文件的默认路径如下:
服务器配置文件:/etc/clickhouse-server/config.xml
用户配置文件:/etc/clickhouse-server/users.xml
数据存放路径(可修改):/var/lib/clickhouse/
日志存放路径(可修改):/var/log/clickhouse-server/
需要修改如下的配置信息:
- 开放远程登录
默认情况下clickhouse服务仅支持本地登录(localhost:9000),开放远程登录需要修改/etc/clickhouse-server/config.xml文件。在命令行客户端使用命令vim /etc/clickhouse-server/config.xml打开文件,找到listen_host节点,取消注释,保存后退出。
修改使用内存的上限
ClickHouse默认配置允许使用的内容只有不到10G,超过该数值大小的表格将无法进行关联、聚合等操作,可根据服务器实际内存大小修改该配置,打开/etc/clickhouse-server/users.xml文件,找到max_menory_usage节点,修改后保存退出。修改或者添加账户
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 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).
简单来讲就是对于config.xml中带有 'incl'标识的属性,可以在另外的配置文件中修改该属性的值,而另外的配置文件的默认路径就是/etc/metrika.xml。
集群采用2个分片2个副本的配置方式,
两个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')
如上,复制表要指定两个参数。
- 当前本地复制表实例所属的分片服务名称。
分片服务名是zookeeper上的目录名称,如果你知道zookeeper实现的统一命名服务,那就好理解了,类似dubbo的服务提供者在zookeeper注册的服务名。在dubbo中同一个服务名有多个实例提供相同的服务。服务调用者只需要通过服务名就能够获取到该服务的所有实例的信息。复制表这和dubbo差不多。分片服务名就是在zookeeper上注册的服务提供者名称。多个复制表的该名称如果一样,那么这些复制表都属于同一个分片服务,只不过表示不同副本而已。同一个分片服务的不同副本之间数据会相互同步,保持一致。 - 当前这张表所属的副本名称。如果第一个参数相同,当前第二个参数需要不同。
复制表机制就仅仅只是提供一种副本机制。属于同一个分片服务的不同复制表之间会相互同步数据。但在查询某个副本时,这个副本宕机了还无法把这个查询自动切换到其他副本查询。需要重新去另外一个未宕机的副本实例上查询那个副本对应的本地复制表。虽然不同副本的数据是一样的,但对用户来说,某个副本宕机了还需要手动切换查询的副本实例。
如果要做到在某个副本宕机时自动切换到其他可用副本,那么就需要结合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的自动同步功能