ClickHouse是俄罗斯第—大搜索引擎Yandex开发的列式储存数据库,这个列式储存数据库的性能大幅超越了很多商业MPP数据库软件,所以花了一段时间实际应用了一下,其出众的速度征服了我,还是很有应用前景的。

接下来我将逐步更新这个系列的文章,内容包括ClickHouse单机安装部署、监听端口、客户端访问接口、可视化客户端、导入导出数据、MergeTree系列表引擎、其他表引擎、ClickHouse分布式集群、Oracle同步数据到Clickhouse以及一些爬坑经验,含金量很足,敬请关注。

《手摸手带你学ClickHouse》之安装部署

1、Clickhouse简介

1.1 简介

官网:https://clickhouse.tech/,ClickHouse是面向 OLAP 的分布式列式DBMS。

优点:
1、灵活的MPP架构,支持线性扩展,简单方便,高可靠性
2、多服务器分布式处理数据,完备的DBMS系统
3、底层数据列式存储,优化索引数据
4、快:比Vertica快5倍,比Hive快279倍,比MySQL快800倍,其可处理的数据级别已达到10亿级别
5、功能多:支持数据统计分析各种场景,支持类SQL查询,异地复制部署。

ClickHouse 正是以不依赖Hadoop 生态、支持海量数据存储,分布式运算,快速闪电的性能,几乎实时的数据分析,友好的SQL语法,出色的函数支持等特点在大数据分析领域越走越远。

缺点:
1、 不擅长多表join操作,建议使用一个大宽表
2、 不支持事务
3、尽量做1000行以上的批量写入,避免逐行或小批量insert、update和delete。

1.2 应用场景

1、绝大多数请求都是用于读访问的,要求实时返回结果
2、数据需要以大批次(大于1000行)进行更新,而不是单行更新;或者根本没有更新操作。
3、数据只是添加到数据库,没有必要修改
4、读取数据时,会从数据库中提取出大量的行,但只用到一小部分列
5、表很“宽”,即表中包含大量的列
6、查询频率相对较低(通常每台服务器每秒查询数百次或更少)
7、对于简单查询,允许大约50毫秒的延迟
8、列的值是比较小的数值和短字符串(例如,每个URL只有60个字节)
9、在处理单个查询时需要高吞吐量(每台服务器每秒高达数十亿行)
10、不需要事务
11、数据一致性要求较低
12、每次查询中只会查询一个大表。除了一个大表,其余都是小表
13、查询结果显著小于数据源。即数据有过滤或聚合。返回结果不超过单个服务器内存木小。

1.3 架构

ClickHouse常用大数据处理架构

手摸手带你学ClickHouse_数据

2、 ClickHouse单机安装部署

2.1 环境准备

用于安装的RPM包可以从下面两个仓库中任选一个下载:

https://repo.yandex.ru/clickhouse/rpm/stable/x86_64/

https://packagecloud.io/altinity/clickhouse

手摸手带你学ClickHouse_客户端_02

2.1.1 安装文件清单

需要下载以下4个安装包文件,版本根据自己需要,但是每个RPM文件的版本号要一致。

clickhouse-client-{version}.noarch. rpm

clickhouse-common-static-{version}.x86_64.rpm

clickhouse-common-static-dbg-{version}.x86_64.rpm

clickhouse-server-{version }.noarch. rpm

手摸手带你学ClickHouse_数据_03

注:这里有几个版本需要注意,20.6.3开始支持explain,所以建议大于此版本号。同时在20.8开始支持实时同步mysql,根据需要自行选择,我这里使用21.3.9.83-2版本。

2.1.2 检查环境依赖

验证当前服务器CPU是否支持SSE 4.2指令集,因为向量化执行需要用到。

grep -q sse4_2 /proc/cpuinfo && echo “SSE 4.2 supported” || echo “SSE 4.2 not supported”

手摸手带你学ClickHouse_xml_04

2.1.3 设置FQDN

在/etc/hosts中添加你服务器的FQDN,例如

hostnamectl --static set-hostname test1.com

手摸手带你学ClickHouse_数据_05

配置hosts文件,加入内容,添加后如下:

cat /etc/hosts

手摸手带你学ClickHouse_xml_06

注:配置集群时把每个节点都加入,以保证局域网内能互相访问。

2.1.4 安装命令

命令1(系统依赖包安装的比较完整时用此命令):

rpm -ivh *.rpm

命令2(使用此命令缺的依赖包会自动安装):

yum install *.rpm

手摸手带你学ClickHouse_xml_07

安装完成后如下所示

手摸手带你学ClickHouse_数据_08

2.2 目录结构

2.2.1 核心目录

(1)/etc/clickhouse-server

手摸手带你学ClickHouse_数据_09

服务端的配置文件目录,包括全局配置config.xml和用户配置users.xml等。

(2)/var/lib/clickhouse

默认的数据存储目录

(3)/var/log/clickhouse-server

默认的日志保存目录

2.2.2 配置文件
(1)/etc/security/limits.d/clickhouse.conf

文件句柄数量的配置,默认如下:

手摸手带你学ClickHouse_客户端_10

该配置也可以通过config.xml的max_open_files修改。

(2)/etc/cron.d/clickhouse-server

cron定时任务配置,用于恢复因异常原因中断的ClickHouse服务进程。

2.2.3 可执行文件

/usr/bin目录下

(1)clickhouse:主程序的可执行文件。
(2)clickhouse-client:一个指向ClickHouse可执行文件的软链接,供客户端连接使用。
(3)clickhouse-server:一个指向ClickHouse可执行文件的软链接,供服务端启动使用。
(4)clickhouse-compressor:内置提供的压缩工具,可用于数据的正压反解。

2.3 启动服务

首先修改数据保存目录,以免空间不够。

cd /etc/clickhouse-server
vi config.xml

修改内容如下:

<path>/chbase/data/</path>
<tmp_path>/chbase/data/tmp/</tmp_path>
<user_files_path>/chbase/data/user_files/</user_files_path>

手摸手带你学ClickHouse_客户端_11

修改目录后,将目录Owner设置为clickhouse用户。

chown clickhouse:clickhouse /chbase/data/ -R

启动服务:

service clickhouse-server start

手摸手带你学ClickHouse_xml_12

# 查看服务状态
service clickhouse-server status
# 服务停止
service clickhouse-server stop
# 服务重启
service clickhouse-server restart

注:如果提示如下Init script is already running,则使用如下命令启动:

systemctl start clickhouse-server.service

如果你是新版本,clickhouse还为我们提供了简单的命令

clickhouse start
clickhouse stop
clickhouse status

启动后查看监听的默认端口号9000

netstat -nltp|grep 9000

手摸手带你学ClickHouse_数据_13

如上所示,则clickhouse已正常启动。

2.4 卸载

查看

rpm -qa | grep clickhouse

手摸手带你学ClickHouse_xml_14

卸载命令:

rpm -e -rpm包名

2.5 设置账号密码

PASSWORD=$(base64 < /dev/urandom | head -c8); echo "$PASSWORD"; echo -n "$PASSWORD" | sha256sum | tr -d '-'

手摸手带你学ClickHouse_数据_15

这样可以得到两行数据,第一行是密码明文,第二行是密码密文

vim /etc/clickhouse-server/users.xml

找到 users --> default --> 标签下的password修改成password_sha256_hex,并把密文填进去

<password_sha256_hex>密码密文</password_sha256_hex>

添加密码后,命令行启动的方式为

clickhouse-client -h ip地址 -d default -m -u default --password 密码明文

上一篇文章《手摸手带你学ClickHouse》之安装部署,详细介绍了ClickHouse单机的安装部署,本文主要内容为ClickHouse监听端口、访问接口、设置允许其他ip访问以及可视化的客户端。

《手摸手带你学ClickHouse》之访问接口

1、Clickhouse的监听端口

clickhouse 的端口如下所示

手摸手带你学ClickHouse_xml_16

手摸手带你学ClickHouse_客户端_17

这里先重点介绍 9000 以及 8123 端口。

1.1 9000端口

9000就是服务的默认监听端口号,在使用 clickhouse-client 连接时就是使用这个。

手摸手带你学ClickHouse_xml_18

1.2 8123端口

8123则是JDBC网络远程连接时使用,后续使用java时就用此端口。

这些端口对应在配置文件 /etc/clickhouse-server/config.xml 中,如下

手摸手带你学ClickHouse_数据_19

1.3 开放防火墙

防火墙开放8123和9000端口

firewall-cmd --add-port=8123/tcp --permanent --zone=public
firewall-cmd --reload
firewall-cmd --add-port=9000/tcp --permanent --zone=public
firewall-cmd --reload

2、客户端的访问接口

启动成功后,就可以启用客户端(clickhouse-client)进行连接,有两种命令执行方式,交互式执行和非交互式执行。

2.1 交互式执行

交互式执行就是直接运行 clickhouse-client 进行登录,和MySQL客户端类似,可以使用show databases、show tables等命令,如下

clickhouse-server client

或者

clickhouse-client -m

-m 可以执行一个多行语句

手摸手带你学ClickHouse_客户端_20

交互式执行的SQL会被记录到 ~/.clickhouse-client-history 文件中,如下

cat ~/.clickhouse-client-history

手摸手带你学ClickHouse_数据_21

2.2 非交互式执行

非交互式执行主要用于批处理场景,例如数据导入导出等。在执行脚本命令时需要加上 --query 参数指定要执行的SQL语句。例如

clickhouse-client --query="select * from tb_test" > /chbase/db_test.tsv

3、允许其他ip访问

默认被允许访问的只有127.0.0.1,这里要先修改为允许其他 ip 也能访问

vim /etc/clickhouse-server/config.xml

修改如下内容(取消注释即可),如下

<listen_host>::</listen_host>

手摸手带你学ClickHouse_xml_22

然后重启服务即可。

4、可视化客户端

由于JDBC网络远程连接需要使用8123端口,先确认8123可以被监听

netstat -tunlp|grep 8123

手摸手带你学ClickHouse_xml_23

可视化工具这里我选择DBeaver(https://dbeaver.io/download/),如下

手摸手带你学ClickHouse_数据_24

手摸手带你学ClickHouse_数据_25

手摸手带你学ClickHouse_xml_26

《手摸手带你学ClickHouse》之导入导出数据

1、导入数据

测试数据如下

手摸手带你学ClickHouse_客户端_27

解压

xz -d hits_v1.tsv.xz
xz -d visits_v1.tsv.xz

解压后

手摸手带你学ClickHouse_客户端_28

创建数据库

手摸手带你学ClickHouse_数据_29

创建表

CREATE TABLE tutorial.hits_v1
(
    `WatchID` UInt64,
    `JavaEnable` UInt8,
    `Title` String,
    `GoodEvent` Int16,
    `EventTime` DateTime,
    `EventDate` Date,
    `CounterID` UInt32,
    `ClientIP` UInt32,
    `ClientIP6` FixedString(16),
    `RegionID` UInt32,
    `UserID` UInt64,
    `CounterClass` Int8,
    `OS` UInt8,
    `UserAgent` UInt8,
    `URL` String,
    `Referer` String,
    `URLDomain` String,
    `RefererDomain` String,
    `Refresh` UInt8,
    `IsRobot` UInt8,
    `RefererCategories` Array(UInt16),
    `URLCategories` Array(UInt16),
    `URLRegions` Array(UInt32),
    `RefererRegions` Array(UInt32),
    `ResolutionWidth` UInt16,
    `ResolutionHeight` UInt16,
    `ResolutionDepth` UInt8,
    `FlashMajor` UInt8,
    `FlashMinor` UInt8,
    `FlashMinor2` String,
    `NetMajor` UInt8,
    `NetMinor` UInt8,
    `UserAgentMajor` UInt16,
    `UserAgentMinor` FixedString(2),
    `CookieEnable` UInt8,
    `JavascriptEnable` UInt8,
    `IsMobile` UInt8,
    `MobilePhone` UInt8,
    `MobilePhoneModel` String,
    `Params` String,
    `IPNetworkID` UInt32,
    `TraficSourceID` Int8,
    `SearchEngineID` UInt16,
    `SearchPhrase` String,
    `AdvEngineID` UInt8,
    `IsArtifical` UInt8,
    `WindowClientWidth` UInt16,
    `WindowClientHeight` UInt16,
    `ClientTimeZone` Int16,
    `ClientEventTime` DateTime,
    `SilverlightVersion1` UInt8,
    `SilverlightVersion2` UInt8,
    `SilverlightVersion3` UInt32,
    `SilverlightVersion4` UInt16,
    `PageCharset` String,
    `CodeVersion` UInt32,
    `IsLink` UInt8,
    `IsDownload` UInt8,
    `IsNotBounce` UInt8,
    `FUniqID` UInt64,
    `HID` UInt32,
    `IsOldCounter` UInt8,
    `IsEvent` UInt8,
    `IsParameter` UInt8,
    `DontCountHits` UInt8,
    `WithHash` UInt8,
    `HitColor` FixedString(1),
    `UTCEventTime` DateTime,
    `Age` UInt8,
    `Sex` UInt8,
    `Income` UInt8,
    `Interests` UInt16,
    `Robotness` UInt8,
    `GeneralInterests` Array(UInt16),
    `RemoteIP` UInt32,
    `RemoteIP6` FixedString(16),
    `WindowName` Int32,
    `OpenerName` Int32,
    `HistoryLength` Int16,
    `BrowserLanguage` FixedString(2),
    `BrowserCountry` FixedString(2),
    `SocialNetwork` String,
    `SocialAction` String,
    `HTTPError` UInt16,
    `SendTiming` Int32,
    `DNSTiming` Int32,
    `ConnectTiming` Int32,
    `ResponseStartTiming` Int32,
    `ResponseEndTiming` Int32,
    `FetchTiming` Int32,
    `RedirectTiming` Int32,
    `DOMInteractiveTiming` Int32,
    `DOMContentLoadedTiming` Int32,
    `DOMCompleteTiming` Int32,
    `LoadEventStartTiming` Int32,
    `LoadEventEndTiming` Int32,
    `NSToDOMContentLoadedTiming` Int32,
    `FirstPaintTiming` Int32,
    `RedirectCount` Int8,
    `SocialSourceNetworkID` UInt8,
    `SocialSourcePage` String,
    `ParamPrice` Int64,
    `ParamOrderID` String,
    `ParamCurrency` FixedString(3),
    `ParamCurrencyID` UInt16,
    `GoalsReached` Array(UInt32),
    `OpenstatServiceName` String,
    `OpenstatCampaignID` String,
    `OpenstatAdID` String,
    `OpenstatSourceID` String,
    `UTMSource` String,
    `UTMMedium` String,
    `UTMCampaign` String,
    `UTMContent` String,
    `UTMTerm` String,
    `FromTag` String,
    `HasGCLID` UInt8,
    `RefererHash` UInt64,
    `URLHash` UInt64,
    `CLID` UInt32,
    `YCLID` UInt64,
    `ShareService` String,
    `ShareURL` String,
    `ShareTitle` String,
    `ParsedParams` Nested(
        Key1 String,
        Key2 String,
        Key3 String,
        Key4 String,
        Key5 String,
        ValueDouble Float64),
    `IslandID` FixedString(16),
    `RequestNum` UInt32,
    `RequestTry` UInt8
)
ENGINE = MergeTree()
PARTITION BY toYYYYMM(EventDate)
ORDER BY (CounterID, EventDate, intHash32(UserID))
SAMPLE BY intHash32(UserID)
SETTINGS index_granularity = 8192;
CREATE TABLE tutorial.visits_v1
(
    `CounterID` UInt32,
    `StartDate` Date,
    `Sign` Int8,
    `IsNew` UInt8,
    `VisitID` UInt64,
    `UserID` UInt64,
    `StartTime` DateTime,
    `Duration` UInt32,
    `UTCStartTime` DateTime,
    `PageViews` Int32,
    `Hits` Int32,
    `IsBounce` UInt8,
    `Referer` String,
    `StartURL` String,
    `RefererDomain` String,
    `StartURLDomain` String,
    `EndURL` String,
    `LinkURL` String,
    `IsDownload` UInt8,
    `TraficSourceID` Int8,
    `SearchEngineID` UInt16,
    `SearchPhrase` String,
    `AdvEngineID` UInt8,
    `PlaceID` Int32,
    `RefererCategories` Array(UInt16),
    `URLCategories` Array(UInt16),
    `URLRegions` Array(UInt32),
    `RefererRegions` Array(UInt32),
    `IsYandex` UInt8,
    `GoalReachesDepth` Int32,
    `GoalReachesURL` Int32,
    `GoalReachesAny` Int32,
    `SocialSourceNetworkID` UInt8,
    `SocialSourcePage` String,
    `MobilePhoneModel` String,
    `ClientEventTime` DateTime,
    `RegionID` UInt32,
    `ClientIP` UInt32,
    `ClientIP6` FixedString(16),
    `RemoteIP` UInt32,
    `RemoteIP6` FixedString(16),
    `IPNetworkID` UInt32,
    `SilverlightVersion3` UInt32,
    `CodeVersion` UInt32,
    `ResolutionWidth` UInt16,
    `ResolutionHeight` UInt16,
    `UserAgentMajor` UInt16,
    `UserAgentMinor` UInt16,
    `WindowClientWidth` UInt16,
    `WindowClientHeight` UInt16,
    `SilverlightVersion2` UInt8,
    `SilverlightVersion4` UInt16,
    `FlashVersion3` UInt16,
    `FlashVersion4` UInt16,
    `ClientTimeZone` Int16,
    `OS` UInt8,
    `UserAgent` UInt8,
    `ResolutionDepth` UInt8,
    `FlashMajor` UInt8,
    `FlashMinor` UInt8,
    `NetMajor` UInt8,
    `NetMinor` UInt8,
    `MobilePhone` UInt8,
    `SilverlightVersion1` UInt8,
    `Age` UInt8,
    `Sex` UInt8,
    `Income` UInt8,
    `JavaEnable` UInt8,
    `CookieEnable` UInt8,
    `JavascriptEnable` UInt8,
    `IsMobile` UInt8,
    `BrowserLanguage` UInt16,
    `BrowserCountry` UInt16,
    `Interests` UInt16,
    `Robotness` UInt8,
    `GeneralInterests` Array(UInt16),
    `Params` Array(String),
    `Goals` Nested(
        ID UInt32,
        Serial UInt32,
        EventTime DateTime,
        Price Int64,
        OrderID String,
        CurrencyID UInt32),
    `WatchIDs` Array(UInt64),
    `ParamSumPrice` Int64,
    `ParamCurrency` FixedString(3),
    `ParamCurrencyID` UInt16,
    `ClickLogID` UInt64,
    `ClickEventID` Int32,
    `ClickGoodEvent` Int32,
    `ClickEventTime` DateTime,
    `ClickPriorityID` Int32,
    `ClickPhraseID` Int32,
    `ClickPageID` Int32,
    `ClickPlaceID` Int32,
    `ClickTypeID` Int32,
    `ClickResourceID` Int32,
    `ClickCost` UInt32,
    `ClickClientIP` UInt32,
    `ClickDomainID` UInt32,
    `ClickURL` String,
    `ClickAttempt` UInt8,
    `ClickOrderID` UInt32,
    `ClickBannerID` UInt32,
    `ClickMarketCategoryID` UInt32,
    `ClickMarketPP` UInt32,
    `ClickMarketCategoryName` String,
    `ClickMarketPPName` String,
    `ClickAWAPSCampaignName` String,
    `ClickPageName` String,
    `ClickTargetType` UInt16,
    `ClickTargetPhraseID` UInt64,
    `ClickContextType` UInt8,
    `ClickSelectType` Int8,
    `ClickOptions` String,
    `ClickGroupBannerID` Int32,
    `OpenstatServiceName` String,
    `OpenstatCampaignID` String,
    `OpenstatAdID` String,
    `OpenstatSourceID` String,
    `UTMSource` String,
    `UTMMedium` String,
    `UTMCampaign` String,
    `UTMContent` String,
    `UTMTerm` String,
    `FromTag` String,
    `HasGCLID` UInt8,
    `FirstVisit` DateTime,
    `PredLastVisit` Date,
    `LastVisit` Date,
    `TotalVisits` UInt32,
    `TraficSource` Nested(
        ID Int8,
        SearchEngineID UInt16,
        AdvEngineID UInt8,
        PlaceID UInt16,
        SocialSourceNetworkID UInt8,
        Domain String,
        SearchPhrase String,
        SocialSourcePage String),
    `Attendance` FixedString(16),
    `CLID` UInt32,
    `YCLID` UInt64,
    `NormalizedRefererHash` UInt64,
    `SearchPhraseHash` UInt64,
    `RefererDomainHash` UInt64,
    `NormalizedStartURLHash` UInt64,
    `StartURLDomainHash` UInt64,
    `NormalizedEndURLHash` UInt64,
    `TopLevelDomain` UInt64,
    `URLScheme` UInt64,
    `OpenstatServiceNameHash` UInt64,
    `OpenstatCampaignIDHash` UInt64,
    `OpenstatAdIDHash` UInt64,
    `OpenstatSourceIDHash` UInt64,
    `UTMSourceHash` UInt64,
    `UTMMediumHash` UInt64,
    `UTMCampaignHash` UInt64,
    `UTMContentHash` UInt64,
    `UTMTermHash` UInt64,
    `FromHash` UInt64,
    `WebVisorEnabled` UInt8,
    `WebVisorActivity` UInt32,
    `ParsedParams` Nested(
        Key1 String,
        Key2 String,
        Key3 String,
        Key4 String,
        Key5 String,
        ValueDouble Float64),
    `Market` Nested(
        Type UInt8,
        GoalID UInt32,
        OrderID String,
        OrderPrice Int64,
        PP UInt32,
        DirectPlaceID UInt32,
        DirectOrderID UInt32,
        DirectBannerID UInt32,
        GoodID String,
        GoodName String,
        GoodQuantity Int32,
        GoodPrice Int64),
    `IslandID` FixedString(16)
)
ENGINE = CollapsingMergeTree(Sign)
PARTITION BY toYYYYMM(StartDate)
ORDER BY (CounterID, StartDate, intHash32(UserID), VisitID)
SAMPLE BY intHash32(UserID)
SETTINGS index_granularity = 8192;

手摸手带你学ClickHouse_客户端_30

导入数据

clickhouse-client --query "INSERT INTO tutorial.hits_v1 FORMAT TSV" --max_insert_block_size=100000 < hits_v1.tsv

7.3G数据,用时190秒

clickhouse-client --query "INSERT INTO tutorial.visits_v1 FORMAT TSV" --max_insert_block_size=100000 < visits_v1.tsv

2.5G数据,用时96秒

FORMAT TSV :导入的数据格式为TSV。
max_insert_block_size : 一次导入10万条记录。

2、查询数据效率

手摸手带你学ClickHouse_客户端_31

手摸手带你学ClickHouse_客户端_32

单表限制条件:

手摸手带你学ClickHouse_数据_33

求和:

手摸手带你学ClickHouse_xml_34

占用空间情况

手摸手带你学ClickHouse_xml_35

表关联查询:

手摸手带你学ClickHouse_数据_36

手摸手带你学ClickHouse_xml_37

需要注意的是,关联查询时小表在后(大表 join 小表)

3、导出数据

clickhouse-client --query="select * from dbtest1.tb_merge_tree FORMAT CSV">/data/ClickHouse/tb_merge_tree.csv

手摸手带你学ClickHouse_数据_38

《手摸手带你学ClickHouse》之MergeTree系列表引擎

前面的文章详细介绍了ClickHouse单机的安装部署、ClickHouse监听端口、访问接口、设置允许其他ip访问、可视化的客户端以及数据的导入导出。本文主要内容为ClickHouse数据存储功能的核心,也就是MergeTree系列的表引擎。

1、MergeTree系列表引擎

MergeTree系列的表引擎是ClickHouse数据存储功能的核心。它们提供了用于弹性和高性能数据检索的大多数功能:列存储,自定义分区,稀疏的主索引,辅助数据跳过索引等。基本MergeTree表引擎可以被认为是单节点ClickHouse实例的默认表引擎,因为它在各种用例中通用且实用。

除了基础表引擎MergeTree之外,常用的表引擎还有ReplacingMergeTree、SummingMergeTree、AggregatingMergeTree、CollapsingMergeTree和VersionedCollapsingMergeTree。

每一种合并树的变种,在继承了基础MergeTree的能力之后,又增加了独有的特性。其名称中的“合并”二字奠定了所有类型MergeTree的基因,它们的所有特殊逻辑,都是在触发合并的过程中被激活的。

主要特点:

  • 存储按主键排序的数据。
  • 如果指定了分区键则可以使用分区。

表引擎(即表的类型)决定了:

  • 数据的存储方式和位置,写到哪里以及从哪里读取数据.
  • 支持哪些查询以及如何支持。
  • 并发数据访问。
  • 索引的使用(如果存在)。
  • 是否可以执行多线程请求。
  • 数据复制参数。

后续内容将针对常用表引擎的使用进行详细讲解,下面我们先看基础表引擎MergeTree。

2、MergeTree表引擎

MergeTree表引擎是ClickHouse的基础表引擎,下面进行详细介绍。

2.1 创建表

create table tb_merge_tree(
uid UInt8,
name String,
birthday Date,
city String ,
gender String
)engine = MergeTree()
primary key uid
order by (uid ,birthday);

注:如果指定了主键,排序字段第一个字段必须是主键。如果没指定,则排序字段就是主键字段。

插入数据:

insert into tb_merge_tree values(1,'zhangsan',toDate(now()),'BJ','M'),
(2,'lisi',toDate(now()),'SH','M'),
(3,'wanger ','1996-01-05','BJ','M');

手摸手带你学ClickHouse_客户端_39

2.2 底层数据结构

接下来我们看下底层是怎么组织数据的,进入到数据存储目录

手摸手带你学ClickHouse_客户端_40

因为这个表没有建立分区,所以数据都在all这个里面

手摸手带你学ClickHouse_xml_41

.bin的就是字段值,.mrk2是标记,标记数据的位置,checksums.txt是做数据校验的,count.txt记录当前数据的条数。

2.3 主键没有唯一性要求

在ClickHouse中主键可重复,没有唯一性要求,只是用来建立索引,如下,我们可以再插入一次相同数据

手摸手带你学ClickHouse_客户端_42

2.4 合并树

这里需要注意的是,如果再次插入数据,这里还会再产生一个all文件,因为是不同批次插入的数据,如下

手摸手带你学ClickHouse_xml_43

可以看到数据也是分两块来存储

手摸手带你学ClickHouse_客户端_44

如果需要合并树,执行如下命令

optimize table tb_merge_tree;

或者

optimize table tb_merge_tree final;

加final的作用是如果有多个会同时一次性合并。

手摸手带你学ClickHouse_客户端_45

手摸手带你学ClickHouse_客户端_46

手摸手带你学ClickHouse_客户端_47

可以看到数据已合并到all_1_3_2中,过一段时间另外两个将会自动删除,如下

手摸手带你学ClickHouse_客户端_48

2.5 索引粒度

上面我们创建表的时候没有指定索引粒度,因为默认就是8192,查看一下建表语句

手摸手带你学ClickHouse_客户端_49

SETTINGS index_granularity = 8192

设置为8192就代表每隔8192行数据建一个索引,这样就大大节省了索引标记占用的空间,所以primary.idx内的索引数据常驻内存,取用速度就会很快。

2.6 分区

要使用分区需要在建表的时候指定partition by 字段名,如下

create table tb_merge_tree2(
uid UInt8,
name String,
birthday Date,
city String
)engine = MergeTree()
partition by birthday
primary key uid
order by (uid ,birthday);

这里指定了生日为分区,也就是日期相同的会在同一个分区,插入数据

insert into tb_merge_tree2 values(1,'zhangsan',toDate(now()),'BJ'),
(2,'lisi',toDate(now()),'KM'),
(3,'wanger ','1996-01-01','BJ'),
(3,'mazi ','1996-01-01','KM'),
(3,'mazi ','2000-11-11','KM');

手摸手带你学ClickHouse_客户端_50

手摸手带你学ClickHouse_xml_51

可以看到,这里按日期建立了三个分区存储,在system的parts表中也可以查询到分区信息,如下

select table,name,partition from system.parts where table='tb_merge_tree2';

手摸手带你学ClickHouse_xml_52

《手摸手带你学ClickHouse》之ReplacingMergeTree表引擎

前面的文章详细介绍了ClickHouse单机的安装部署、ClickHouse监听端口、访问接口、设置允许其他ip访问、可视化的客户端、数据的导入导出以及MergeTree表引擎。本文主要内容为ClickHouse的另外一个表引擎 ReplacingMergeTree

1、基本使用

ReplacingMergeTree这个引擎区别于MergeTree的是增加了 分区内删除重复数据(按ORDER BY排序键为基准,而不是主键) 的功能,注意是分区内。但是去重只在数据合并时,而合并是后台自动在未知时间内进行,因此不保证没有重复数据的出现。

建表如下:

create table tb_replacing_mergetree(
uid UInt8,
name String,
birthday Date,
city String
)engine = ReplacingMergeTree()
partition by city
primary key uid
order by (uid ,name);

这里使用city字段来进行分区,主键为uid,排序字段为uid和name

插入数据

insert into tb_replacing_mergetree values(1,'zhangsan',toDate(now()),'BJ'),
(1,'lisi',toDate(now()),'KM'),
(1,'wanger ','1996-01-01','BJ'),
(1,'mazi ','1996-01-01','KM'),
(1,'mazi ','2000-11-11','KM');

手摸手带你学ClickHouse_数据_53

接下来进行合并操作

optimize table tb_replacing_mergetree final;

合并后数据如下:

手摸手带你学ClickHouse_客户端_54

可以看到KM这个分区里uid和name重复的数据已经被合并。

2、删除数据版本控制

由于被删掉的数据默认按插入顺序来,保留的是最新插入的一条,这样就会有一个问题,可能老数据在新数据后插入,合并时就会删除那条新数据。

要解决这个问题,只需要在声明引擎时指定某个字段,就会默认保留该字段最大的那条数据。
例如:

create table tb_replacing_mergetree2(
uid UInt8,
name String,
age UInt8,
city String,
version UInt8
)engine = ReplacingMergeTree(version)
partition by city
primary key uid
order by (uid ,age);
insert into tb_replacing_mergetree2 values(1,'newname',25,'BJ',3),
(1,'wanger',26,'BJ',2),
(1,'mazi',26,'KM',2),
(1,'mazi',26,'KM',1),
(1,'oldname',25,'BJ',1);

手摸手带你学ClickHouse_数据_55

合并后

optimize table tb_replacing_mergetree2 final;

可以看到,分区内指定的version字段最大的值被保留。

手摸手带你学ClickHouse_xml_56


作者:樊同学