You need to enable JavaScript to run this app.
ByteHouse云数仓版

ByteHouse云数仓版

复制全文
Golang
ClickHouse Go Driver
复制全文
ClickHouse Go Driver

您可以通过开源的 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))
    }
}
最近更新时间:2025.11.13 16:32:03
这个页面对您有帮助吗?
有用
有用
无用
无用