You need to enable JavaScript to run this app.
导航
ClickHouse Go Driver
最近更新时间:2025.11.13 16:32:03首次发布时间:2023.12.18 12:14:26
复制全文
我的收藏
有用
有用
无用
无用

您可以通过开源的 ClickHouse Go 驱动程序连接到 ByteHouse 云数仓版,连接后进行数据写入查询等操作。本文为您介绍 ClickHouse Go 驱动连接 ByteHouse 云数仓版的主要操作流程和操作要点。

背景信息

由于 ByteHouse 的 Go 驱动当前在维护升级中,您可以使用开源 ClickHouse 的 Go 驱动连接 ByteHouse,开源 ClickHouse 支持的 API 接口和协议如下。

细分项

能力说明

API 接口

支持:

  • (推荐)ClickHouse Client 接口:提供了最佳的性能,并支持一些 ClickHouse 的特殊能力,例如列写入。
  • database/sql 接口:如果您需要连接多个数据库、或者希望使用基于标准 database/sql 接口的工具和库,您可以使用本接口。

协议

支持:

  • (推荐)TCP/Native 协议:建议使用 TCP/Native 协议以获得最佳性能。
  • HTTP 协议:仅 database/sql 接口支持,如果您需要兼容防火墙和代理,您可以使用 HTTP 协议。

环境要求

推荐使用以下版本:

驱动

已验证版本

ClickHouse Go 基础驱动

2.30.0

Go

Golang 1.21

使用限制
  • 当前暂不支持 ByteHouse JSONB 和 Bitmap64 的数据类型。
  • 如果您在使用过程中遇到其他未知限制,请联系 ByteHouse 团队处理。

安装驱动

下载驱动

单击该链接下载 ClickHouse Go 基础驱动。

安装驱动

您可以通过 go get 安装。

go get github.com/ClickHouse/clickhouse-go/v2@v2.30.0

获取 ByteHouse 连接信息

ByteHouse 支持通过 IAM 用户或数据库用户连接 ClickHouse Go Driver。IAM 用户与数据库用户二者差异说明如下,您可按需选择。

  • IAM 用户为火山引擎访问控制(IAM)中创建的用户,其权限由 IAM 权限策略及您授予的 ByteHouse 资源和数据权限决定。IAM 用户可访问 ByteHouse 控制台,也支持通过 CLI、连接驱动、生态工具、API 等方式访问 ByteHouse。
  • 数据库用户为 ByteHouse 中创建的数据库级别用户,可为其授予环境、资源和数据权限。数据库用户不可访问 ByteHouse 控制台,但支持通过 CLI、连接驱动、生态工具、API 等方式访问 ByteHouse。

更多 IAM 用户和数据库用户的介绍请参见以下文档:

使用 IAM 用户连接

请参考步骤三:获取 ByteHouse 连接串信息,了解如何通过 IAM 用户方式连接到 ByteHouse。
通用参数说明如下:

参数

配置要点

Host

Host 在不同协议中的字段不同,但格式相同,为 tenant-{TENANT_ID}-{REGION}-public.bytehouse.volces.com,配置为 ByteHouse 的公网域名,其中 tenant-{TENANT_ID}-{REGION} 分别为火山引擎主账号的账号 ID 和 ByteHouse 的地域信息,您可以在 ByteHouse 控制台的租户管理 > 基本信息 > 网络信息中查看并复制网络信息。详情请参见步骤二:配置网络信息

Port

  • TCP 协议固定为 19000。
  • HTTP 协议固定为 8123。

Database

配置为连接 ByteHouse 的数据库名称。

username & password

  • username:设置为 bytehouse。
  • password:为 ByteHouse 的 <API_Key>,您可以在 ByteHouse 控制台的租户管理 > 连接信息中获取 API Key。详情请参见获取 API Key

virtual_warehouse

配置为计算组名。您可登录 ByteHouse 控制台,单击顶部计算组,查看并复制计算组 ID。示例:vw-{environment_id}{account_id}-{virtual_warehouse_name}
如果不配置,则默认使用您在 ByteHouse 设置的默认计算组。

使用数据库用户连接

请参考步骤三:获取 ByteHouse 连接串信息,了解如何通过数据库用户的方式连接到 ByteHouse。
通用参数说明如下:

参数

配置要点

Host

Host 在不同协议中的字段不同,但格式相同,为 tenant-{TENANT_ID}-{REGION}-public.bytehouse.volces.com,配置为 ByteHouse 的公网域名,其中 tenant-{TENANT_ID}-{REGION} 分别为火山引擎主账号的账号 ID 和 ByteHouse 的地域信息,您可以在 ByteHouse 控制台的租户管理 > 基本信息 > 网络信息中查看并复制网络信息。详情请参见步骤二:配置网络信息

Port

  • TCP 协议固定为 19000。
  • HTTP 协议固定为 8123。

Database

配置为连接 ByteHouse 的数据库名称。

username & password

  • username:参数填写说明如下,详情请参见获取数据库用户及密码

    注意

    根据您使用的连接方式不同,username 的拼接方式不同:

    • 使用 OpenDB 连接时,username 配置为{accountID_or_accountName}::{username}[::{envID}]
    • 使用 DSN 方式连接时,username 配置为 {accountID_or_accountName}%3A%3A{username}[%3A%3A{envID}]
    • {accountID_or_accountName} :指火山引擎用户账号 ID 或名称,可登录 ByteHouse 控制台,单击右上角个人中心查看并复制账号 ID 或名称。
    • {username} :指登录 ByteHouse 数据库账号的用户名。可在ByteHouse 控制台 > 权限管理 > 用户 > 查看数据库用户名
    • {envID}:可选配置,数据库所在的环境名称。如果使用 default 环境,可不配置;如需使用其他环境,需指定环境名称,配置时无需添加[]。您可登录 ByteHouse 控制台,在租户管理 > 基本信息 > 当前环境中获取。
      使用示例如下:
      • 配置环境 ID:21xxxxxxxx::demouser::demoenv21xxxxxxxx%3A%3Ademouser%3A%3Ademoenv
      • 不配置环境 ID:21xxxxxxxx::demouser21xxxxxxxx%3A%3Ademouser
  • password:数据库账号的密码由管理员创建数据库账号时自定义配置,您可联系管理员获取密码。如果密码丢失或遗忘,可通联系管理员重置密码,详情请参考重置密码

virtual_warehouse

配置为计算组名。您可登录 ByteHouse 控制台,单击顶部计算组,查看并复制计算组 ID。示例:vw-{environment_id}{account_id}-{virtual_warehouse_name}
如果不配置,则默认使用您在 ByteHouse 设置的默认计算组。

基本用法

连接 ByteHouse

可参考下面代码连接至 ByteHouse,使用时注意替换连接语句中的 {Host}{Password}{User}{Database}{VIRTUAL_WAREHOUSE_ID} 等连接信息字段,获取方式请参见获取 ByteHouse 连接信息

  • 超时时间配置:
    • 使用 TCP 协议时:dial_timeout 默认为 30s、read_timeout 默认为 300s。
    • 使用 HTTP 协议时:dial_timeout 默认为 30s、read_timeout 默认为 300s。
  • 默认支持 keepAlive,可以复用连接和避免短链接。

使用 TCP/Native 协议

  • 使用 ClickHouse Driver 的 OpenDB 创建连接

    import (
        "context"
        //"crypto/tls"
        "database/sql"
        "fmt"
        "github.com/ClickHouse/clickhouse-go/v2"
        "github.com/google/uuid"
        "time"
    )
    
    func main() {
        host := "{Host}"
        port := 19000
        password := "{Password}"
        user := "{User}"
        database := "{Database}"
        virtual_warehouse_id := "{VIRTUAL_WAREHOUSE_ID}"
    
        db := clickhouse.OpenDB(&clickhouse.Options{
         Addr: []string{fmt.Sprintf("%s:%d", host, port)},
         Auth: clickhouse.Auth{
            Database: database,
            Username: user,
            Password: password,
         },
         Protocol: clickhouse.Native,
         TLS: &tls.Config{
            InsecureSkipVerify: true,
         },
         Settings: clickhouse.Settings{
            "virtual_warehouse": virtual_warehouse_id,
         },
        })
    
  • 通过 Go Database 的 DSN(Data Source Name,数据源名称)创建连接

    import (
        "context"
        //"crypto/tls"
        "database/sql"
        "fmt"
        "github.com/ClickHouse/clickhouse-go/v2"
        "github.com/google/uuid"
        "time"
    )
    
    func main() {
        host := "{Host}"
        port := 19000
        password := "{Password}"
        user := "{User}"
        database := "{Database}"
        virtual_warehouse_id := "{VIRTUAL_WAREHOUSE_ID}"
    
        db, err := sql.Open(
           "clickhouse",
           fmt.Sprintf("clickhouse://%v:%v@%v:%v/%v?secure=true&virtual_warehouse=%v", user, password, host, port, database, virtual_warehouse_id),
        )
       if err != nil {
          log.Fatal(err)
       }
    

使用 HTTP 协议

使用 HTTP 协议时,全局连接时,必须添加 describe_query_with_data_type_flags=0 参数。

  • 使用 ClickHouse Driver 的 OpenDB 创建连接

    import (
        "context"
        //"crypto/tls"
        "database/sql"
        "fmt"
        "github.com/ClickHouse/clickhouse-go/v2"
        "github.com/google/uuid"
        "time"
    )
    
    func main() {
        host := "{Host}"
        port := 8123
        password := "{Password}"
        user := "{User}"
        database := "{Database}"
        virtual_warehouse_id := "{VIRTUAL_WAREHOUSE_ID}"
    
        db := clickhouse.OpenDB(&clickhouse.Options{
         Addr: []string{fmt.Sprintf("%s:%d", host, port)},
         Auth: clickhouse.Auth{
            Database: database,
            Username: user,
            Password: password,
         },
         Protocol: clickhouse.HTTP,
         TLS: &tls.Config{
            InsecureSkipVerify: true,
         },
         Settings: clickhouse.Settings{
            "virtual_warehouse":                   virtual_warehouse_id,
            "describe_query_with_data_type_flags": "0",
         },
        })
    
  • 通过 Go Database 的 DSN(Data Source Name,数据源名称)创建连接

    import (
        "context"
        //"crypto/tls"
        "database/sql"
        "fmt"
        "github.com/ClickHouse/clickhouse-go/v2"
        "github.com/google/uuid"
        "time"
    )
    
    func main() {
        host := "{Host}"
        port := 8123
        password := "{Password}"
        user := "{User}"
        database := "{Database}"
        virtual_warehouse_id := "{VIRTUAL_WAREHOUSE_ID}"
    
        db, err := sql.Open(
           "clickhouse",
           fmt.Sprintf("https://%v:%v@%v:%v/%v?secure=true&describe_query_with_data_type_flags=0&virtual_warehouse=%v", user, password, host, port, database, virtual_warehouse_id),
        )
        if err != nil {
           log.Fatal(err)
        }
    

自定义 query ID

您可使用 Context 设置 query ID。

func UseContextSendQueryId(db *sql.DB) {
    queryId, _ := uuid.NewUUID()
    ctx := clickhouse.Context(context.Background(), clickhouse.WithQueryID(fmt.Sprintf("customer_%v", queryId.String())))
    _, err := db.ExecContext(ctx, "DROP DATABASE IF EXISTS bhgotest")
    if err != nil {
       log.Fatal(err)
    }
}

自定义 query settings

func UseContextSendQuerySetting(db *sql.DB) {
    // we can use context to pass settings to a specific API call
    ctx := clickhouse.Context(context.Background(), clickhouse.WithSettings(clickhouse.Settings{
       "max_execution_time": 450,
    }))

    var settingValue uint16
    if err := db.QueryRowContext(ctx, "SELECT getSetting('max_execution_time')").Scan(&settingValue); err != nil {
       log.Fatal(err)
    }
    fmt.Printf("settingValue: %v\n", settingValue)
    if settingValue != 450 {
       panic(fmt.Sprintf("expected setting value to be 450, got %d", settingValue))
    }
}

连接与查询完整示例

您可以使用以下代码连接 ByteHouse,并管理数据。

import (
    "context"
    //"crypto/tls"
    "database/sql"
    "fmt"
    "github.com/ClickHouse/clickhouse-go/v2"
    "github.com/google/uuid"
    "time"
)

func main() {
    host := "{Host}"
    port := 8123 // 根据协议选择8123(HTTP)还是19000(TCP)
    password := "{Password}"
    user := "{User}"
    database := "{Database}"
    virtual_warehouse_id := "{VIRTUAL_WAREHOUSE_ID}"

    //db := clickhouse.OpenDB(&clickhouse.Options{
    // Addr: []string{fmt.Sprintf("%s:%d", host, port)},
    // Auth: clickhouse.Auth{
    //    Database: database,
    //    Username: user,
    //    Password: password,
    // },
    // Protocol: clickhouse.Native,
    // TLS: &tls.Config{
    //    InsecureSkipVerify: true,
    // },
    // Settings: clickhouse.Settings{
    //    "virtual_warehouse": virtual_warehouse_id,
    // },
    //})
    db, err := sql.Open(
       "clickhouse",
       fmt.Sprintf("clickhouse://%v:%v@%v:%v/%v?secure=true&virtual_warehouse=%v", user, password, host, port, database, virtual_warehouse_id),
    )
    if err != nil {
       log.Fatal(err)
    }

    defer db.Close()

    if err := db.Ping(); err != nil {
       log.Fatal(err)
    }

    UseContextSendQueryId(db)
    UseContextSendQuerySetting(db)

    _, err = db.Exec("DROP DATABASE IF EXISTS bhgotest")
    if err != nil {
       log.Fatal(err)
    }

    _, err = db.Exec("CREATE DATABASE IF NOT EXISTS bhgotest")
    if err != nil {
       log.Fatal(err)
    }

    _, err = db.Exec(`
CREATE TABLE IF NOT EXISTS bhgotest.example (
        Col1 UInt8
    , Col2 String
    , Col3 FixedString(3)
    , Col4 UUID
    , Col5 Map(String, UInt8)
    , Col6 Array(String)
    , Col7 Tuple(String, UInt8, Array(Map(String, String))) KV
    , Col8 DateTime
) Engine = CnchMergeTree() ORDER BY tuple()
`)
    if err != nil {
       log.Fatal(err)
    }

    batchInsert, err := db.Begin()
    if err != nil {
       log.Fatal(err)
    }
    batchInsertPrepare, err := batchInsert.Prepare("INSERT INTO bhgotest.example")
    if err != nil {
       log.Fatal(err)
    }
    for i := 0; i < 1000; i++ {
       _, err := batchInsertPrepare.Exec(
          uint8(42),
          "ClickHouse", "Inc",
          uuid.New(),
          map[string]uint8{"key": 1},             // Map(String, UInt8)
          []string{"Q", "W", "E", "R", "T", "Y"}, // Array(String)
          []any{ // Tuple(String, UInt8, Array(Map(String, String)))
             "String Value", uint8(5), []map[string]string{
                {"key": "value"},
                {"key": "value"},
                {"key": "value"},
             },
          },
          time.Now(),
       )
       if err != nil {
          log.Fatal(err)
       }
    }
    batchInsert.Commit()

    row := db.QueryRow("SELECT * FROM bhgotest.example  limit 1")
    var (
       col1             uint8
       col2, col3, col4 string
       col5             map[string]uint8
       col6             []string
       col7             interface{}
       col8             time.Time
    )
    if err := row.Scan(&col1, &col2, &col3, &col4, &col5, &col6, &col7, &col8); err != nil {
       log.Fatal(err)
    }
    print(col1, col2, col3, col4, col5, col6, col7)

    db.Exec("DROP TABLE IF EXISTS bhgotest.example")

}

func UseContextSendQueryId(db *sql.DB) {
    queryId, _ := uuid.NewUUID()
    ctx := clickhouse.Context(context.Background(), clickhouse.WithQueryID(fmt.Sprintf("customer_%v", queryId.String())))
    _, err := db.ExecContext(ctx, "DROP DATABASE IF EXISTS bhgotest")
    if err != nil {
       log.Fatal(err)
    }
}

func UseContextSendQuerySetting(db *sql.DB) {
    // we can use context to pass settings to a specific API call
    ctx := clickhouse.Context(context.Background(), clickhouse.WithSettings(clickhouse.Settings{
       "max_execution_time": 450,
    }))

    var settingValue uint16
    if err := db.QueryRowContext(ctx, "SELECT getSetting('max_execution_time')").Scan(&settingValue); err != nil {
       panic(err)
    }
    fmt.Printf("settingValue: %v\n", settingValue)
    if settingValue != 450 {
       panic(fmt.Sprintf("expected setting value to be 450, got %d", settingValue))
    }
}