ClickHouse實戰


1.概述

最近有被留言關於ClickHouse的使用問題,今天筆者將為大家分享一下ClickHouse的安裝細節和使用方法。

2.內容

首先安裝環境如下所示:

  • Linux:CentOS7
  • ClickHouse:21.8.10.19

2.1 ClickHouse是什么?

ClickHouse是一個真正的列式數據庫管理系統,在ClickHouse中,數據始終是按照列來進行存儲的,包括矢量(向量或者列塊)執行的過程。只要有可能,操作都是基於矢量進行分派的,而不是單個的值,這被稱為矢量化查詢執行,它有利於降低實際的數據處理開銷。

這個想法並不新鮮,矢量編程被大量用於科學數據處理中。即使在關系型數據庫中,這個想法也不是什么新的東西。通常有兩種不同的加速查詢處理的方法:矢量化查詢執行和運行時代碼生成。在后者中,動態的為每一類查詢生成代碼,消除了間接分派和動態分派。這兩種方法中,並沒有哪一種嚴格地比另一種好。運行時代碼生成可以更好的將多個操作融合在一起,從而充分利用CPU執行單元和流水線。矢量化查詢執行不是特別使用,因為它涉及必須寫到緩存並讀回的臨時向量。如果L2緩存容納不下臨時數據,那么這將成為一個問題,但是矢量化查詢執行更加容易利用CPU。

2.2 列

要表示內存中的列(實際上是列塊),需使用 IColumn 接口。該接口提供了用於實現各種關系操作符的輔助方法。幾乎所有的操作都是不可變的:這些操作不會更改原始列,但是會創建一個新的修改后的列。比如,IColumn::filter 方法接受過濾字節掩碼,用於 WHERE 和 HAVING 關系操作符中。另外的例子:IColumn::permute 方法支持 ORDER BY 實現,IColumn::cut 方法支持 LIMIT 實現等等。
不同的 IColumn 實現(ColumnUInt8、ColumnString 等)負責不同的列內存布局。內存布局通常是一個連續的數組。對於數據類型為整型的列,只是一個連續的數組,比如 std::vector。對於 String 列和 Array 列,則由兩個向量組成:其中一個向量連續存儲所有的 String 或數組元素,另一個存儲每一個 String 或 Array 的起始元素在第一個向量中的偏移。而 ColumnConst 則僅在內存中存儲一個值,但是看起來像一個列。

2.3 字段

盡管如此,有時候也可能需要處理單個值。表示單個值,可以使用 Field。Field 是 UInt64、Int64、Float64、String 和 Array 組成的聯合。IColumn 擁有 operator[] 方法來獲取第 n 個值成為一個 Field,同時也擁有 insert 方法將一個 Field 追加到一個列的末尾。這些方法並不高效,因為它們需要處理表示單一值的臨時 Field 對象,但是有更高效的方法比如 insertFrom 和 insertRangeFrom 等。
Field 中並沒有足夠的關於一個表(table)的特定數據類型的信息。比如,UInt8、UInt16、UInt32 和 UInt64 在 Field 中均表示為 UInt64。

2.4 數據類型

IDataType 負責序列化和反序列化:讀寫二進制或文本形式的列或單個值構成的塊。IDataType 直接與表的數據類型相對應。比如,有 DataTypeUInt32、DataTypeDateTime、DataTypeString 等數據類型。
IDataType 與 IColumn 之間的關聯並不大。不同的數據類型在內存中能夠用相同的 IColumn 實現來表示。比如,DataTypeUInt32 和 DataTypeDateTime 都是用 ColumnUInt32 或 ColumnConstUInt32 來表示的。另外,相同的數據類型也可以用不同的 IColumn 實現來表示。比如,DataTypeUInt8 既可以使用 ColumnUInt8 來表示,也可以使用過 ColumnConstUInt8 來表示。
IDataType 僅存儲元數據。比如,DataTypeUInt8 不存儲任何東西(除了 vptr);DataTypeFixedString 僅存儲 N(固定長度字符串的串長度)。
IDataType 具有針對各種數據格式的輔助函數。比如如下一些輔助函數:序列化一個值並加上可能的引號;序列化一個值用於 JSON 格式;序列化一個值作為 XML 格式的一部分。輔助函數與數據格式並沒有直接的對應。比如,兩種不同的數據格式 Pretty 和 TabSeparated 均可以使用 IDataType 接口提供的 serializeTextEscaped 這一輔助函數。

2.5 塊

Block 是表示內存中表的子集(chunk)的容器,是由三元組:(IColumn, IDataType, 列名) 構成的集合。在查詢執行期間,數據是按 Block 進行處理的。如果我們有一個 Block,那么就有了數據(在 IColumn 對象中),有了數據的類型信息告訴我們如何處理該列,同時也有了列名(來自表的原始列名,或人為指定的用於臨時計算結果的名字)。
當我們遍歷一個塊中的列進行某些函數計算時,會把結果列加入到塊中,但不會更改函數參數中的列,因為操作是不可變的。之后,不需要的列可以從塊中刪除,但不是修改。這對於消除公共子表達式非常方便。
Block 用於處理數據塊。注意,對於相同類型的計算,列名和類型對不同的塊保持相同,僅列數據不同。最好把塊數據(block data)和塊頭(block header)分離開來,因為小塊大小會因復制共享指針和列名而帶來很高的臨時字符串開銷。

3.安裝ClickHouse

在Github上搜索ClickHouse,並下載二進制安裝包,下載內容如下:

 

 

 下載地址如下:

https://github.com/ClickHouse/ClickHouse/releases

安裝步驟如下:

## 第1步
# 解壓common包
tar -zxvf clickhouse-common-static-21.8.10.19.tgz
tar -zxvf clickhouse-common-static-dbg-21.8.10.19.tgz

# 執行腳本安裝
sudo doinst.sh

## 第2步
# 解壓server包
tar -zxvf clickhouse-server-21.8.10.19.tgz

# 修改config.xml存儲路徑和監聽端口
<!-- 實際數據存儲路徑,建議用一個磁盤空間較大的目錄 -->
<path>/appcom/clickhouse/data/</path>
<tmp_path>/appcom/clickhouse/data/tmp/</tmp_path>

<!-- 開啟遠程訪問 -->
<listen_host>::</listen_host>

# 執行腳本安裝
sudo doinst.sh

## 第3步
# 解壓client包
tar -zxvf clickhouse-client-21.8.10.19.tgz

# 執行腳本安裝
sudo doinst.sh

## 第4步
# 啟動ClickHouse
/etc/init.d/clickhouse-server start

## 第5步
# 啟動Client
clickhouse-client -m -h 127.0.0.1 --user default --port 9000 --password ckxxx

4.實戰

4.1 准備實戰數據

在Linux控制台中執行如下命令,獲取表數據。命令如下所示:

# 獲取表數據
curl https://datasets.clickhouse.com/hits/tsv/hits_v1.tsv.xz | unxz --threads=`nproc` > hits_v1.tsv

curl https://datasets.clickhouse.com/visits/tsv/visits_v1.tsv.xz | unxz --threads=`nproc` > visits_v1.tsv

4.2 創建數據庫和表

如果是使用ClickHouse Client執行建表語句,啟動Client時,需要指定 -m 的參數,具體命令如下:

-- 創建數據庫
CREATE DATABASE IF NOT EXISTS tutorial;

-- 創建表(hits_v1)所有用戶在服務所涵蓋的所有網站上完成的每個操作
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);

-- 創建表(visits_v1)預先構建的會話,而不是單個操作
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);

4.3 加載數據

數據導入到ClickHouse是通過INSERT INTO方式完成的,查詢類似許多SQL數據庫。然而,數據通常是在一個提供支持序列化格式而不是VALUES子句(也支持)。執行命令如下所示:

# 導入hits_v1表數據
clickhouse-client --password ckxxx --query "INSERT INTO tutorial.hits_v1 FORMAT TSV" --max_insert_block_size=100000 < hits_v1.tsv

# 導入visits_v1表數據
clickhouse-client --password ckxxx --query "INSERT INTO tutorial.visits_v1 FORMAT TSV" --max_insert_block_size=100000 < visits_v1.tsv

4.4 查詢

這里查詢,我們使用Hue來作為可視化界面,這里推薦大家使用JDBC的方式來連接ClickHouse,由於Hue的Python庫sqlalchemy-clickhouse對ClickHouse的兼容很差,如果使用sqlalchemy的方式來連接ClickHouse,會出現很多不兼容的情況,同時,在使用JDBC的方式時,也推薦大家使用clickhouse4j-1.4.4.jar的驅動包,而不是clickhouse-jdbc驅動包,即使用cc.blynk.clickhouse.ClickHouseDriver來替代ru.yandex.clickhouse.ClickHouseDriver。Hue系統文件hue.ini的配置內容如下所示:

 [[[clickhouse]]]
       name=ClickHouse
       interface=jdbc
    #   ## Specific options for connecting to the ClickHouse server.
    #   ## The JDBC driver clickhouse-jdbc.jar and its related jars need to be in the CLASSPATH environment variable.
       options='{"url": "jdbc:clickhouse://localhost:8123", "driver": "cc.blynk.clickhouse.ClickHouseDriver", "user": "default", "password": "ckxxx"}'

同時,在~/.bash_profile中配置clickhouse4j-1.4.4.jar的環境變量,內容如下:

export CLASSPATH=/appcom/clickhouse/app/clickhouse4j-1.4.4.jar

執行source ~/.bash_profile使環境變量立即生效。

4.4.1 AVG查詢

SELECT
    StartURL AS URL,
    AVG(Duration) AS AvgDuration
FROM tutorial.visits_v1
WHERE StartDate BETWEEN '2014-03-23' AND '2014-03-30'
GROUP BY URL
ORDER BY AvgDuration DESC
LIMIT 10

預覽截圖如下:

 

 4.4.2 SUM查詢

SELECT
    sum(Sign) AS visits,
    sumIf(Sign, has(Goals.ID, 1105530)) AS goal_visits,
    (100. * goal_visits) / visits AS goal_percent
FROM tutorial.visits_v1
WHERE (CounterID = 912887) AND (toYYYYMM(StartDate) = 201403) AND (domain(StartURL) = 'yandex.ru')

預覽截圖如下:

 

5.總結

ClickHouse屬於靈活的MPP架構,支持在線擴展,簡單方便,可組件高可用分布式集群。多服務器分布式處理數據,擁有完整的DBMS系統。底層數據列式存儲,支持壓縮,優化數據存儲,優化索引數據,優化底層存儲。同時,支持數據統計分析各種場景,支持類SQL查詢,異地復制部署等。

6.結束語

這篇博客就和大家分享到這里,如果大家在研究學習的過程當中有什么問題,可以加群進行討論或發送郵件給我,我會盡我所能為您解答,與君共勉!

另外,博主出書了《Kafka並不難學》和《Hadoop大數據挖掘從入門到進階實戰》,喜歡的朋友或同學, 可以在公告欄那里點擊購買鏈接購買博主的書進行學習,在此感謝大家的支持。關注下面公眾號,根據提示,可免費獲取書籍的教學視頻。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM