ClickHouse應用(一)


最近參與的項目一直使用到ClickHouse,所以趁最近比較閑來對ClickHouse來做個總結 。 首先,來談談,我使用了這么多天ClickHouse的感受吧:

  • ClickHouse查詢超快,ClickHouse是列式存儲數據庫,同時ClickHouse支持bitmap這樣的壓縮算法,大大提升了查詢速度。 ClickHouse支持幾十億數據進行秒級運算

  • ClickHouse支持向量處理,Clickhouse支持array數據格式

  • ClickHouse支持多類復雜的數據格式,同一般的數據庫相比較,ClickHouse除了支持普通的數據類型之外,還支持 嵌套-Nested ,Json ,Array , Bitmap等格式

  • 支持多類型表

    • Kafka引擎表 - Clickhouse可以直接接入Kafka的數據到ClickHouse中
    • Mysql引擎表 - 和Mysql數據庫的表進行同步
    • MergeTree家族
    • MergeTree : 普通引擎表
    • replacingMergeTree : 主要用於去重數據
    • AggregatingMergeTree : 用於增量數據的更新
    • SummingMergeTree : 主要用於數據的預聚合

雖然,ClickHouse優點很多 ,但是 還是感覺有很多不方便的地方 ,應該是ClickHouse的作用主要是進行實時點擊場景吧,其和傳統的HDFS並不相同吧,ClickHouse還是有以下的缺點:

  • 不支持高並發,雖然ClickHouse查詢速度超快,但是並不支持高並發,官方建議qps為100,可以通過修改配置文件增加連接數,但是在服務器足夠好的情況下;

  • ClickHouse 沒法支持事務 ,不支持真正意義的刪除和更新

  • ClickHouse 無法做到向 HDFS一樣,通過shell腳本來定時觸發sql任務 , ClickHouse面向的是實時場景,在計算時 可以 搭配 物化視圖預聚合進行操作,雖然 物化視圖很強大,但是也不是萬能的 😂

  • Clickhouse中刪除數據是異步的,正常來說ClickHouse並不贊同刪除超過50多G的表或者視圖,其實正常來說,一般也不會刪除這么大的表和物化視圖,但是可能難免有這樣的需求,此使再刪大表,就會發現,雖然drop table table_name當時執行成功了,但是在短時間內還是會查到這張表,同時這個表里還是有數據的,這與ClickHouse底層的Attention有關系。

大致談了一下自己使用ClickHouse的感受,接下來從建表入手具體談一下ClickHouse的應用

建表

普通表

  • 單機
create table table_name (
col_1 String ,
col_2 Nullable(String) ,
col_3 UInt64,
col_4 Nullable(UInt64) ,
col_5 DateTime ,
col_6 Date
) ENGINE = MergeTree() | AggregatingMergeTree() | SummingMergeTree()  -- 三個里面選擇一個即可
PARTITION BY (toDate(col_5))
ORDER BY (col_1, col_2)
;
  • 集群 (單副本)
-- 本地表
create table table_name on CLUSTER '{cluster}' (
col_1 String ,
col_2 Nullable(String) ,
col_3 UInt64,
col_4 Nullable(UInt64) ,
col_5 DateTime ,
col_6 Date
) ENGINE = MergeTree() | AggregatingMergeTree() | SummingMergeTree()
PARTITION BY (toDate(col_5))
ORDER BY (col_1, col_2)
;

-- 分布式表
CREATE TABLE table_name_distribute ON CLUSTER default
AS table_name
ENGINE = Distributed(default , default_database, table_name , rand())
;

  • 集群(雙副本)
create table table_name on CLUSTER '{cluster}' (
col_1 String ,
col_2 Nullable(String) ,
col_3 UInt64,
col_4 Nullable(UInt64) ,
col_5 DateTime ,
col_6 Date
) ENGINE = replacingMergeTree(/clickhouse/tables/cdp.xmp_track_new/{shard}', '{replica}') | replacingAggregatingMergeTree() | replacingSummingMergeTree()
PARTITION BY (toDate(col_5))
ORDER BY (col_1, col_2)
;


-- 分布式表 (邏輯表)
CREATE TABLE table_name_distribute ON CLUSTER default
AS table_name
ENGINE = Distributed(default , default_database, table_name , rand())
;

在建表的時候,主要關注以下幾個概念:

  • 分布式表

對於,單機版的ClickHouse很好理解,就是在ClickHouse建了一張普通表 , 沒有分布式和本地表概念之分

對於, 集群版來說,則需要通過 on CLUSTER '{cluster}'來在每個節點上建表,向本地表導入數據的話,ClickHouse會將數據分散在不同的節點存儲,因此,如果直接select本地表只能查看到一半的數據; 此使則需要分布式表,分布式表是一個邏輯上的表, 可以理解為數據庫中的視圖, 一般查詢都查詢分布式表. 分布式表引擎會將我們的查詢請求路由本地表進行查詢, 然后進行匯總最終返回給用戶; 因此我們在ClickHouse中除了最終查詢的表是分布式表之外,中間計算過程則用本地表進行;

  • 分區/ 分片

partition by 后面接的是該表的分區,例 :partition by (col_1, col_2), 和hive中的partition有相同的含義, 其主要按照具體的字段來對數據進行分區/分片存放,但是有點不太一樣的是,CK里面的partition是建表是的一個字段 ,建表的時候指定以該字段進行分區分片 ,但是需要注意一點的是 ,在往該表中寫入數據的時候,分區越多其寫入速度會變慢;

  • 排序/ 主鍵

order by 類似於主鍵的作用,不過其和Mysql不一樣的是其沒有主鍵約束,索引的含義更多一點 , 同時需要注意的時候,order by (字段) 后接的字段必須是非空類型,對於Null值類型的字段並不支持用於索引中,同樣的Nullable類型的字段也不行。

物化視圖

​ 數據庫中的視圖(view)是從一張或多張數據庫表查詢導出的虛擬表,反映基礎表中數據的變化,且本身不存儲數據。而物化視圖(materialized view)則和普通視圖有很大的區別。

物化視圖是查詢結果集的一份持久化存儲,其和普通視圖不一樣的是,普通視圖並不存儲數據,而物化視圖則可以存儲數據,其趨於一張表。物化視圖可以是原始表的一個簡單copy, 也可以是原始表的簡單聚合結果集,也可以是通過一些處理的結果集, 其會隨着原始更新而發生變化,這種更新僅限於insert操作,對於updatedelete 並不會及時發生變化,而需要手動去進行更新,但是物化視圖還是應用於多種場景:

  • 接入kafka外表的數據,可以通過物化視圖將kafka的數據進行相應的處理,最終導入到一張表中
  • 適用於預聚合場景, 但是這個地方的預聚合-僅限於簡單的一些count , sum , uniq, max , min 這些操作,對於更復雜的場景可不太適合,利用物化視圖進行預聚合的時候,可以聯合countState, sumState,uniqState ,maxState ,minState 以及countMerge , sumMerge , uniqMerge , maxMerge,minMerge 進行操作。 利用這種預聚合的能力,來實現實時點擊流的計算。

使用物化視圖,主要是利用空間來換時間的概念,來快速的進行查詢,但是物化視圖還是有如下幾個限制:

  • join 場景,當要對多個表進行關聯的時候,物化視圖並不會里面更新,而是當左表有更新的時候才會發生相應的更新,當如果左邊不變右邊新插入數據,物化視圖則不會立馬更新。
  • 物化視圖不能立馬感應源表的更新和刪除操作,需要手動進行更新
  • 必須指定物化視圖的engine 用於數據存儲
  • TO [db].[table]語法的時候,不得使用POPULATE。但是需要注意的是 使用POPULATE可能會引起大批量數據的讀寫,導致數據庫連接不上等問題😂,不要問為什么,說多了都是累
  • 查詢語句(select)可以包含下面的子句:DISTINCT, GROUP BY, ORDER BY, LIMIT…
  • 物化視圖的alter操作有些限制,操作起來不大方便。
  • 若物化視圖的定義使用了TO [db.]name 子語句,則可以將目標表的視圖 卸載 DETACH 在裝載 ATTACH

創建物化視圖

  1. 使用TO [db].[table]命令

使用TO [db].[table]這種語法建表時,只需要指定 物化視圖的表名db.view_table_name,寫入數據目標表的表名db.goal_table_name 以及讀取表的邏輯select 語句

CREATE MATERIALIZED VIEW db.view_table_name
TO db.goal_table_name
AS
SELECT
*
FROM
db.orign_table_name
  1. 使用POPULATE

使用POPULATE語法建表時,需要指定物化視圖對應的引擎, 以及 分區 和 索引 ,此使物化視圖和一張普通表沒什么太大的區別

CREATE MATERIALIZED VIEW db.view_table_name
ENGINE = AggregatingMergeTree()
PARTITION BY (stat_day )
order by (stat_day)
POPULATE
AS
SELECT
*
from
db.origin_table_name

利用物化視圖進行預聚合運算

方式一,直接把物化視圖作為最終要查詢的表,即用POPULATE進行建表, 利用物化視圖來求pv & uv , countState & uniqState則會把uid進行匯總,在最終進行查詢的時候,再利用countMerge & uniqMerge 函數將用戶idmerge在一起,此種用法還可以應用於多項目的pv & uv的計算。

CREATE MATERIALIZED VIEW db.view_table_name
ENGINE = AggregatingMergeTree()
PARTITION BY ( stat_day)
ORDER BY (stat_day)
POPULATE
AS
select
toDate(now()) as run_day ,
project as project_id,
toDate(server_time) as stat_day ,
countState(uid) as pv ,
uniqState(uid) as uv
from
db.origin_table_name
group by
project,toDate(server_time)
;

假設要求,多個項目共同的pv & uv ,則可以使用上面的物化視圖進行結果查詢,使用以下查詢語句即可,對了切忌不要直接select * from db.view_table_name查詢物化視圖,因為countState這些函數都存的是亂碼。

select
countMerge(pv) as pv ,
uniqMerge(uv) as uv
from
db.view_table_name
where
project_id in ('11','22','33')

方式二,即用TO [db].[table_name] 將結果存入到對應的目標表中,和上面一致,再不贅述

外表

​ clickhouse可以通過一些引擎來直接連接其他數據源的數據,就我最常用的兩種來舉例說明,有興趣的朋友看官網

kafka外表

​ 直接配置相關信息來連接實時接收kafka的數據, 具體的參數看官網吧,此處接收kafka的數據時候,如果字段固定的話,可以直接使用JSONEachRow,不過采用以下兩種建表方式,如果字段不固定,則直接使用JSONAsString。

JSONAsString建表

CREATE TABLE db.kafka_base
(
`eventStr` String
)
ENGINE = Kafka
SETTINGS kafka_broker_list = 'host:9092',
kafka_topic_list = 'topic',
kafka_group_name = 'group',
kafka_format = 'JSONAsString',
kafka_row_delimiter = '\n',
kafka_schema = '',
kafka_num_consumers =1,
kafka_skip_broken_messages=10
;

JSONEachRow建表 需要將所有的字段都列出來

CREATE TABLE db.kafka_base
(
'a' String ,
'b' String ,
'c' String
)
ENGINE = Kafka
SETTINGS
kafka_broker_list = 'host:9092',
kafka_topic_list = 'topic1',
kafka_group_name = 'group1',
kafka_format = 'JSONEachRow',
kafka_row_delimiter = '\n',
kafka_schema = '',
kafka_num_consumers = 1

Mysql

CREATE TABLE db.mysql_base
(
't1' String ,
't2' String
) ENGINE = MySQL('host:port', 'database', 'table', 'user', 'password'[, replace_query, 'on_duplicate_clause']);

參數設置

  • host:port :MySQL server的地址。
  • database :MySQL數據庫名稱。
  • table : MySQL表名。
  • user : MySQL用戶名。
  • password : MySQL用戶密碼。
  • replace_query :將INSERT INTO查詢轉換為REPLACE INTO查詢的標識。如果replace_query=1, 查詢將被替換。
  • on_duplicate_clause : 將ON DUPLICATE KEY 'on_duplicate_clause’表達式添加到INSERT查詢中。

總的來說,一旦踏入Clickhouse地界,無數個坑在等你,當然也有驚喜,Over !!!


免責聲明!

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



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