開箱即用的 GoWind Admin|風(fēng)行,企業(yè)級前后端一體中后臺框架:ClickHouse集成指南
ClickHouse 是一款由俄羅斯搜索引擎公司 Yandex 開發(fā)的開源列式存儲數(shù)據(jù)庫,專為海量數(shù)據(jù)實(shí)時(shí)分析設(shè)計(jì)。它以極致的查詢性能和高吞吐寫入能力著稱,尤其擅長處理PB 級別的結(jié)構(gòu)化數(shù)據(jù),并能在毫秒到秒級內(nèi)完成復(fù)雜的聚合分析(如多維度統(tǒng)計(jì)、漏斗計(jì)算、用戶行為分析等),是大數(shù)據(jù)分析、數(shù)據(jù)倉庫、實(shí)時(shí)報(bào)表等場景的核心工具。
ClickHouse 的核心概念
| 概念 | 說明 |
|---|---|
| 表(Table) | 類似關(guān)系型數(shù)據(jù)庫的表,存儲結(jié)構(gòu)化數(shù)據(jù),但底層按列存儲。 |
| 引擎(Engine) | 決定表的存儲方式、查詢特性和分布式行為,是 ClickHouse 的核心設(shè)計(jì)。例如: - MergeTree 系列:最常用,支持索引、分區(qū)、副本,適合海量數(shù)據(jù)存儲;- Log 系列:輕量無索引,適合臨時(shí)小表;- Distributed:分布式表,用于管理集群分片。 |
| 分區(qū)(Partition) | 按規(guī)則(如時(shí)間、地區(qū))將表數(shù)據(jù)拆分,查詢時(shí)可快速過濾分區(qū),減少掃描范圍(如按 “日期” 分區(qū),查詢 “2023 年 10 月數(shù)據(jù)” 僅需掃描對應(yīng)分區(qū))。 |
| 主鍵(Primary Key) | 用于排序和快速查找,不同于關(guān)系型數(shù)據(jù)庫的唯一約束,ClickHouse 主鍵允許重復(fù),主要作用是優(yōu)化查詢性能。 |
| 跳數(shù)索引(Skip Index) | 輔助索引,用于快速判斷某一范圍內(nèi)是否存在符合條件的數(shù)據(jù)(如 “數(shù)值是否在 100-200 之間”),進(jìn)一步減少掃描量。 |
| 分片(Shard) | 集群中數(shù)據(jù)的物理拆分單位,每個(gè)分片存儲表的一部分?jǐn)?shù)據(jù),分布在不同節(jié)點(diǎn),實(shí)現(xiàn)并行處理。 |
| 副本(Replica) | 同一分片的冗余備份,用于故障恢復(fù)和負(fù)載均衡(查詢可分散到不同副本),保證數(shù)據(jù)不丟失。 |
ClickHouse 與其他數(shù)據(jù)庫的差異
| 維度 | ClickHouse | 傳統(tǒng)關(guān)系型數(shù)據(jù)庫(如 MySQL) | Hadoop 生態(tài)(如 Hive) |
|---|---|---|---|
| 核心場景 | 實(shí)時(shí)海量數(shù)據(jù)分析(PB 級,毫秒 / 秒級響應(yīng)) | 事務(wù)性業(yè)務(wù)(增刪改查,強(qiáng)一致性) | 離線批處理分析(TB/PB 級,分鐘 / 小時(shí)級) |
| 存儲方式 | 列式存儲,高壓縮 | 行式存儲,壓縮率低 | 列式存儲(ORC/Parquet),壓縮率高 |
| 寫入特性 | 高吞吐,近實(shí)時(shí),不支持事務(wù) | 支持事務(wù),寫入性能適中 | 批處理寫入,延遲高 |
| 查詢性能 | 極致的聚合查詢速度 | 適合單行 / 小批量查詢,復(fù)雜分析慢 | 支持復(fù)雜分析,但速度慢(依賴 MapReduce/Spark) |
| 靈活性 | 不支持行級更新 / 刪除,事務(wù)弱 | 支持行級增刪改查,事務(wù)強(qiáng) | 不支持實(shí)時(shí)更新,靈活性低 |
Docker部署
docker pull bitnami/clickhouse:latest
docker run -itd \
--name clickhouse-server \
--network=app-tier \
-p 8123:8123 \
-p 9000:9000 \
-p 9004:9004 \
-e ALLOW_EMPTY_PASSWORD=no \
-e CLICKHOUSE_ADMIN_USER=default \
-e CLICKHOUSE_ADMIN_PASSWORD=123456 \
bitnami/clickhouse:latest
在 Go Wind Admin 中使用 ClickHouse
我把ClickHouse的SDK封裝了起來,并且提供了配置文件的支持,使用起來非常簡單。
ClickHouse支持go的sql標(biāo)準(zhǔn)庫更新查詢,但是,會有一些限制,比如不支持事務(wù)等。所以,想要完整的功能,還是需要使用ClickHouse的官方SDK。因此,我們僅提供了原生的ClickHouse SDK 封裝。
首先,我們需要安裝庫:
go get github.com/tx7do/kratos-bootstrap/database/clickhouse
接著在數(shù)據(jù)庫的配置文件data.yaml中添加ClickHouse的配置:
data:
clickhouse:
addresses:
- "localhost:9000"
username: "default"
password: "123456"
database: "finances"
添加好了配置之后,我們就可以在data包里面創(chuàng)建Clickhouse的客戶端了:
package data
import (
"github.com/tx7do/kratos-bootstrap/database/clickhouse"
)
func NewClickHouseClient(logger log.Logger, cfg *conf.Bootstrap) *clickhouse.Client {
cli, err := clickhouse.NewClient(logger, cfg)
if err != nil {
return nil
}
return cli
}
在data/init.go注入到wire:
//go:build wireinject
// +build wireinject
package data
import "github.com/google/wire"
var ProviderSet = wire.NewSet(
NewClickHouseClient,
)
在這里,我們以股票的K線(蠟燭圖)為實(shí)例,來講解如何使用ClickHouse。
首先,定義模型:
package data
import "time"
type Candle struct {
Timestamp *time.Time `json:"timestamp" ch:"timestamp"`
Symbol *string `json:"symbol" ch:"symbol"`
Open *float64 `json:"open" ch:"open"`
High *float64 `json:"high" ch:"high"`
Low *float64 `json:"low" ch:"low"`
Close *float64 `json:"close" ch:"close"`
Volume *float64 `json:"volume" ch:"volume"`
}
最后,實(shí)現(xiàn)CandleRepo:
package data
import (
"github.com/go-kratos/kratos/v2/log"
"github.com/tx7do/kratos-bootstrap/database/clickhouse"
)
const candleTableName = "candles"
type CandleRepo struct {
client *clickhouse.Client
log *log.Helper
}
func NewCandleRepo(logger log.Logger, client *clickhouse.Client) *CandleRepo {
repo := &CandleRepo{
log: log.NewHelper(log.With(logger, "module", "candle/ck/repo")),
client: client,
}
return repo
}
func (r *CandleRepo) Create(ctx context.Context, req *Candle) error {
if req == nil {
return candleV1.ErrorBadRequest("request data is required")
}
err := r.client.Insert(ctx, candleTableName, "", req)
if err != nil {
r.log.Errorf("create candle failed: %s", err.Error())
return candleV1.ErrorInternalServerError("create candle failed")
}
return nil
}