跳至主要内容

·阅读时间:2分钟

这是一个关于如何开始使用 Python 和 ClickHouse 云服务的逐步示例。

注意

请记住,Python 版本和库依赖关系一直在不断发展。在尝试此操作时,请确保使用驱动程序和 Python 环境的最新支持版本。

在撰写本文时,我们分别使用 clickhouse-connect 驱动程序版本 0.5.23 和 python 3.11.2

步骤

  1. 检查 Python 版本
$  python -V
Python 3.11.2
  1. 我们将在名为 ch-python 的文件夹中组装项目
$ mkdir ch-python
$ cd ch-python
  1. 创建一个名为 requirements.txt 的依赖项文件,其中包含
clickhouse-connect==0.5.23
  1. 创建一个名为 main.py 的 Python 源文件
import clickhouse_connect
import sys
import json

CLICKHOUSE_CLOUD_HOSTNAME = 'HOSTNAME.clickhouse.cloud'
CLICKHOUSE_CLOUD_USER = 'default'
CLICKHOUSE_CLOUD_PASSWORD = 'YOUR_SECRET_PASSWORD'

client = clickhouse_connect.get_client(
host=CLICKHOUSE_CLOUD_HOSTNAME, port=8443, username=CLICKHOUSE_CLOUD_USER, password=CLICKHOUSE_CLOUD_PASSWORD)

print("connected to " + CLICKHOUSE_CLOUD_HOSTNAME + "\n")
client.command(
'CREATE TABLE IF NOT EXISTS new_table (key UInt32, value String, metric Float64) ENGINE MergeTree ORDER BY key')

print("table new_table created or exists already!\n")

row1 = [1000, 'String Value 1000', 5.233]
row2 = [2000, 'String Value 2000', -107.04]
data = [row1, row2]
client.insert('new_table', data, column_names=['key', 'value', 'metric'])

print("written 2 rows to table new_table\n")

QUERY = "SELECT max(key), avg(metric) FROM new_table"

result = client.query(QUERY)

sys.stdout.write("query: ["+QUERY + "] returns:\n\n")
print(result.result_rows)
  1. 创建虚拟环境
chpython$ python -m venv venv
  1. 加载虚拟环境
chpython$ source venv/bin/activate

加载后,您的终端提示符应该以 (venv) 为前缀,安装依赖项

(venv) ➜  chpython$ pip install -r requirements.txt
Collecting certifi
Using cached certifi-2023.5.7-py3-none-any.whl (156 kB)
Collecting urllib3>=1.26
Using cached urllib3-2.0.2-py3-none-any.whl (123 kB)
Collecting pytz
Using cached pytz-2023.3-py2.py3-none-any.whl (502 kB)
Collecting zstandard
Using cached zstandard-0.21.0-cp311-cp311-macosx_11_0_arm64.whl (364 kB)
Collecting lz4
Using cached lz4-4.3.2-cp311-cp311-macosx_11_0_arm64.whl (212 kB)
Installing collected packages: pytz, zstandard, urllib3, lz4, certifi, clickhouse-connect
Successfully installed certifi-2023.5.7 clickhouse-connect-0.5.23 lz4-4.3.2 pytz-2023.3 urllib3-2.0.2 zstandard-0.21.0
  1. 启动代码!
(venv) chpython$ venv/bin/python main.py

connected to HOSTNAME.clickhouse.cloud

table new_table created or exists already!

written 2 rows to table new_table

query: [SELECT max(key), avg(metric) FROM new_table] returns:

[(2000, -50.9035)]
提示

如果使用旧版本的 Python(例如 3.9.6),您可能会遇到与 urllib3 库相关的 ImportError。在这种情况下,要么将您的 Python 环境升级到较新的版本,要么在您的 requirements.txt 文件中将 urllib3 版本固定到 1.26.15

·阅读时间:5分钟

如何使用 API 管理 ClickHouse 云上的集群?

答案

我们将使用 Terraform 配置我们的基础设施和 ClickHouse 提供程序

步骤

1). 在云中创建 API 密钥。请按照此处的文档操作 - https://clickhouse.ac.cn/docs/en/cloud/manage/openapi

在本地保存凭据。

2). 使用以下方法安装 Terraform - https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli

如果您使用的是 Mac,可以使用 Homebrew 包管理器。

3). 在任何您喜欢的位置创建一个目录

mkdir test
➜ test pwd
/Users/jaijhala/Desktop/terraform/test

4). 创建 2 个文件:main.tfsecret.tfvars

复制以下内容

main.tf 文件将是

terraform {
required_providers {
clickhouse = {
source = "ClickHouse/clickhouse"
version = "0.0.2"
}
}
}

variable "organization_id" {
type = string
}

variable "token_key" {
type = string
}

variable "token_secret" {
type = string
}

provider clickhouse {
environment = "production"
organization_id = var.organization_id
token_key = var.token_key
token_secret = var.token_secret
}


variable "service_password" {
type = string
sensitive = true
}

resource "clickhouse_service" "service123" {
name = "jai-terraform"
cloud_provider = "aws"
region = "us-east-2"
tier = "development"
idle_scaling = true
password = var.service_password
ip_access = [
{
source = "0.0.0.0/0"
description = "Anywhere"
}
]
}

output "CLICKHOUSE_HOST" {
value = clickhouse_service.service123.endpoints.0.host
}

您可以在上面的资源部分中替换您自己的参数,例如服务名称、区域等。

secret.tfvars 是您放置所有先前下载的 API 密钥相关信息的位置。此文件背后的想法是,所有机密凭据都将隐藏在主配置文件之外。

它将类似于(替换这些参数)

organization_id = "e957a5f7-4qe3-4b05-ad5a-d02b2dcd0593"
token_key = "QWhhkMeytqQruTeKg"
token_secret = "4b1dNmjWdLUno9lXxmKvSUcPP62jvn7irkuZPbY"
service_password = "password123!"

5). 从此目录运行 terraform init

预期输出

Initializing the backend...

Initializing provider plugins...
- Finding clickhouse/clickhouse versions matching "0.0.2"...
- Installing clickhouse/clickhouse v0.0.2...
- Installed clickhouse/clickhouse v0.0.2 (self-signed, key ID D7089EE5C6A92ED1)

Partner and community providers are signed by their developers.
If you'd like to know more about provider signing, you can read about it here:
https://www.terraform.io/docs/cli/plugins/signing.html

Terraform has created a lock file .terraform.lock.hcl to record the provider
selections it made above. Include this file in your version control repository
so that Terraform can guarantee to make the same selections by default when
you run "terraform init" in the future.

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.

6). 运行 terraform apply -var-file=secret.tfvars 命令。

类似于

➜  test terraform apply -var-file=secret.tfvars

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with
the following symbols:
+ create

Terraform will perform the following actions:

# clickhouse_service.service123 will be created
+ resource "clickhouse_service" "service123" {
+ cloud_provider = "aws"
+ endpoints = (known after apply)
+ id = (known after apply)
+ idle_scaling = true
+ ip_access = [
+ {
+ description = "Anywhere"
+ source = "0.0.0.0/0"
},
]
+ last_updated = (known after apply)
+ name = "jai-terraform"
+ password = (sensitive value)
+ region = "us-east-2"
+ tier = "development"
}

Plan: 1 to add, 0 to change, 0 to destroy.

Changes to Outputs:
+ CLICKHOUSE_HOST = (known after apply)

Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.

Enter a value: yes

键入 yes 并按 Enter 键

旁注:请注意上面显示 password = (sensitive value)。这是因为我们在 main.tf 文件中为密码设置了 sensitive = true

7). 创建服务需要几分钟时间,但最终它应该显示为

  Enter a value: yes

clickhouse_service.service123: Creating...
clickhouse_service.service123: Still creating... [10s elapsed]
clickhouse_service.service123: Still creating... [20s elapsed]
clickhouse_service.service123: Still creating... [30s elapsed]
clickhouse_service.service123: Still creating... [40s elapsed]
clickhouse_service.service123: Still creating... [50s elapsed]
clickhouse_service.service123: Still creating... [1m0s elapsed]
clickhouse_service.service123: Still creating... [1m10s elapsed]
clickhouse_service.service123: Still creating... [1m20s elapsed]
clickhouse_service.service123: Still creating... [1m30s elapsed]
clickhouse_service.service123: Still creating... [1m40s elapsed]
clickhouse_service.service123: Creation complete after 1m41s [id=aa8d8d63-1878-4600-8470-630715af38ed]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

Outputs:

CLICKHOUSE_HOST = "h3ljlaqez6.us-east-2.aws.clickhouse.cloud"
➜ test

8). 检查云控制台,您应该能够看到已创建的服务。

9). 要再次清理/销毁服务,请运行 terraform destroy -var-file=secret.tfvars

类似于

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with
the following symbols:
- destroy

Terraform will perform the following actions:

# clickhouse_service.service123 will be destroyed
- resource "clickhouse_service" "service123" {
- cloud_provider = "aws" -> null
- ............

Plan: 0 to add, 0 to change, 1 to destroy.

Changes to Outputs:
- CLICKHOUSE_HOST = "h3ljlaqez6.us-east-2.aws.clickhouse.cloud" -> null

Do you really want to destroy all resources?
Terraform will destroy all your managed infrastructure, as shown above.
There is no undo. Only 'yes' will be accepted to confirm.

Enter a value:

键入 yes 并按 Enter 键

10).

clickhouse_service.service123: Destroying... [id=aa8d8d63-1878-4600-8470-630715af38ed]
clickhouse_service.service123: Still destroying... [id=aa8d8d63-1878-4600-8470-630715af38ed, 10s elapsed]
clickhouse_service.service123: Still destroying... [id=aa8d8d63-1878-4600-8470-630715af38ed, 20s elapsed]
clickhouse_service.service123: Destruction complete after 27s

Destroy complete! Resources: 1 destroyed.

它应该从云控制台中消失。

有关云 API 的更多详细信息,请参见此处 - https://clickhouse.ac.cn/docs/en/cloud/manage/api/api-overview

·阅读时间:2分钟

问题

如何使用 SSH 密钥身份验证连接到 CH 云服务?

答案

1) 使用 ssh-keygen 创建密钥对。示例

➜  new ssh-keygen \
-t ed25519 \
> -f /Users/testuser/.ssh/ch_key
Generating public/private ed25519 key pair.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /Users/testuser/.ssh/ch_key
Your public key has been saved in /Users/testuser/.ssh/ch_key.pub
.....

2) 使用公钥(上述示例中的 ch_key.pub)创建用户。

clickhouse-cloud :) CREATE USER abcuser IDENTIFIED WITH ssh_key BY KEY 'AAAABBBcdE1lZDI1NTE5AAAAIISdl4CrGM8mckXBUXLjL3ef9XwnycDWEvBPu3toB40m' TYPE 'ssh-ed25519';

CREATE USER abcuser IDENTIFIED WITH ssh_key BY KEY AAAABBBcdE1lZDI1NTE5AAAAIISdl4CrGM8mckXBUXLjL3ef9XwnycDWEvBPu3toB40m TYPE `ssh-ed25519`

Query id: 34c6aad6-5f88-4c80-af7a-7d37c91ba7d5

Ok.

3) 运行 SHOW users 以确认用户创建。

4) 将 default_role 授予用户(可选)。

clickhouse-cloud :) grant default_role to abcuser;

GRANT default_role TO abcuser

Query id: 4a054003-220a-4dea-8e8d-eb1f08ee7b10

Ok.

0 rows in set. Elapsed: 0.137 sec.

5) 现在使用私钥对服务进行身份验证。

➜  new ./clickhouse client --host myhost.us-central1.gcp.clickhouse.cloud --secure --user abcuser --ssh-key-file '/Users/testuser/.ssh/ch_key'
ClickHouse client version 23.12.1.863 (official build).
Enter your private key passphrase (leave empty for no passphrase):
Connecting to myhost.us-central1.gcp.clickhouse.cloud:9440 as user abcuser.
Connected to ClickHouse server version 23.9.2.

clickhouse-cloud :) select currentUser();

SELECT currentUser()

Query id: d4b6bb60-ef45-47d3-8740-db9f2941dcd2

┌─currentUser()─┐
│ abcuser │
└───────────────┘

1 row in set. Elapsed: 0.001 sec.

clickhouse-cloud :)

·阅读时间:3分钟

下表提供了 system.dashboards 中使用的指标与 system.custom_metrics 中的 Prometheus 指标的映射关系。
这对于希望监控 system.dashboards 中相同指标的客户很有用。

system.dashboards 中指标到 system.custom_metrics 中 Prometheus 指标的映射表

仪表盘标题Prometheus 指标名称 (system.custom_metrics)
概述每秒查询数ClickHouseProfileEvents_Query
概述CPU 使用率(核心)ClickHouseProfileEvents_OSCPUVirtualTimeMicroseconds
概述正在运行的查询ClickHouseMetrics_Query
概述正在运行的合并ClickHouseMetrics_Merge
概述每秒选取的字节数ClickHouseProfileEvents_SelectedBytes
概述IO 等待ClickHouseProfileEvents_OSIOWaitMicroseconds
概述CPU 等待ClickHouseProfileEvents_OSCPUWaitMicroseconds
概述操作系统 CPU 使用率(用户空间)ClickHouseAsyncMetrics_OSUserTimeNormalized
概述操作系统 CPU 使用率(内核)ClickHouseAsyncMetrics_OSSystemTimeNormalized
概述从磁盘读取ClickHouseProfileEvents_OSReadBytes
概述从文件系统读取ClickHouseProfileEvents_OSReadChars
概述内存(跟踪)ClickHouseMetrics_MemoryTracking
概述负载平均值(15分钟)ClickHouseAsyncMetrics_LoadAverage15
概述每秒选取的行数ClickHouseProfileEvents_SelectedRows
概述每秒插入的行数ClickHouseProfileEvents_InsertedRows
概述MergeTree 总分区数ClickHouseAsyncMetrics_TotalPartsOfMergeTreeTables
概述每个分区的最大分区数ClickHouseAsyncMetrics_MaxPartCountForPartition
云概述每秒查询数ClickHouseProfileEvents_Query
云概述CPU 使用率(核心)ClickHouseProfileEvents_OSCPUVirtualTimeMicroseconds
云概述正在运行的查询ClickHouseMetrics_Query
云概述正在运行的合并ClickHouseMetrics_Merge
云概述每秒选取的字节数ClickHouseProfileEvents_SelectedBytes
云概述IO 等待(本地文件系统)ClickHouseProfileEvents_OSIOWaitMicroseconds
云概述S3 读取等待ClickHouseProfileEvents_ReadBufferFromS3Microseconds
云概述每秒 S3 读取错误数ProfileEvent_ReadBufferFromS3RequestsErrors
云概述CPU 等待ClickHouseProfileEvents_OSCPUWaitMicroseconds
云概述操作系统 CPU 使用率(用户空间,标准化)ClickHouseAsyncMetrics_OSUserTimeNormalized
云概述操作系统 CPU 使用率(内核,标准化)ClickHouseAsyncMetrics_OSSystemTimeNormalized
云概述从磁盘读取(字节/秒)ClickHouseProfileEvents_OSReadBytes
云概述从文件系统读取(字节/秒)ClickHouseProfileEvents_OSReadChars
云概述内存(跟踪,字节)ClickHouseMetrics_MemoryTracking
云概述负载平均值(15分钟)ClickHouseAsyncMetrics_LoadAverage15
云概述每秒选取的行数ClickHouseProfileEvents_SelectedRows
云概述每秒插入的行数ClickHouseProfileEvents_InsertedRows
云概述MergeTree 总分区数ClickHouseAsyncMetrics_TotalPartsOfMergeTreeTables
云概述每个分区的最大分区数ClickHouseAsyncMetrics_MaxPartCountForPartition
云概述从 S3 读取(字节/秒)ClickHouseProfileEvents_ReadBufferFromS3Bytes
云概述文件系统缓存大小ClickHouseMetrics_FilesystemCacheSize
云概述每秒磁盘 S3 写入请求数ClickHouseProfileEvents_DiskS3PutObject + ClickHouseProfileEvents_DiskS3UploadPart + ClickHouseProfileEvents_DiskS3CreateMultipartUpload + ClickHouseProfileEvents_DiskS3CompleteMultipartUpload
云概述每秒磁盘 S3 读取请求数ClickHouseProfileEvents_DiskS3GetObject + ClickHouseProfileEvents_DiskS3HeadObject + ClickHouseProfileEvents_DiskS3ListObjects
云概述FS 缓存命中率ClickHouseProfileEvents_CachedReadBufferReadFromCacheBytes / (ClickHouseProfileEvents_CachedReadBufferReadFromCacheBytes + ClickHouseProfileEvents_CachedReadBufferReadFromSourceBytes)
云概述页面缓存命中率greatest(0, (sum(ClickHouseProfileEvents_OSReadChars) - sum(ClickHouseProfileEvents_OSReadBytes)) / (sum(ClickHouseProfileEvents_OSReadChars) + sum(ClickHouseProfileEvents_ReadBufferFromS3Bytes)))
云概述每秒网络接收字节数ClickHouseProfileEvents_NetworkReceiveBytes
云概述每秒网络发送字节数ClickHouseProfileEvents_NetworkSendBytes

相关链接
https://clickhouse.ac.cn/docs/en/integrations/prometheus

·阅读时间:1分钟

问题

当尝试使用连接器从 PowerBI 连接到 ClickHouse 时,您会收到以下身份验证错误

We encountered an error while trying to connect.
Details: "ODBC: ERROR [HY000] HTTP status code: 403
Received error:
Code: 516. DB::Exception: default: Authentication failed: password is incorrect, or there is no user with such name.
If you have installed ClickHouse and forgot password you can reset it in the configuration file.
The password for default user is typically located at /etc/clickhouse-server/users.d/default-password.xml and deleting this file will reset the password.
See also /etc/clickhouse-server/users.ml on the server where
ClickHouse is installed.

powerbi_error

答案

检查正在使用的密码,查看密码是否包含波浪号 ~

建议为连接使用专用用户并手动设置密码。如果使用 ClickHouse Cloud 并需要使用 default 用户的管理员级权限,则创建新用户并分配 default_role

更多信息
https://clickhouse.ac.cn/docs/en/operations/access-rights#user-account-management
https://clickhouse.ac.cn/docs/en/cloud/security/cloud-access-management#database-roles

·阅读时间:2分钟

问题

如何创建可以查询其他集群或实例的表?

答案

下面是一个简单的功能测试示例。

在此示例中,使用 ClickHouse Cloud,但该示例在使用自托管集群时也能正常工作。目标将需要更改为目标节点或负载均衡器的 url/主机/dns。

在集群 A 中

./clickhouse client --host clusterA.us-west-2.aws.clickhouse.cloud --secure --password 'Password123!'

创建数据库

create database db1;

创建表

 CREATE TABLE db1.table1_remote1
(
`id` UInt32,
`timestamp_column` DateTime,
`string_column` String
)
ENGINE = MergeTree()
ORDER BY id;

插入一些示例行

insert into db1.table1_remote1
values
(1, '2023-09-29 00:01:00', 'a'),
(2, '2023-09-29 00:02:00', 'b'),
(3, '2023-09-29 00:03:00', 'c');

在集群 B 中

./clickhouse client --host clusterB.us-east-2.aws.clickhouse.cloud --secure --password 'Password123!'

创建数据库

create database db1;

创建表

CREATE TABLE db1.table1_remote2
(
`id` UInt32,
`timestamp_column` DateTime,
`string_column` String
)
ENGINE = MergeTree()
ORDER BY id;

插入示例行

insert into db1.table1_remote1
values
(4, '2023-09-29 00:04:00', 'x'),
(5, '2023-09-29 00:05:00', 'y'),
(6, '2023-09-29 00:06:00', 'z');

在集群 C 中
*此集群将用于从其他两个集群收集数据,但是,也可以用作数据源。

./clickhouse client --host clusterC.us-west-2.aws.clickhouse.cloud --secure --password 'Password123!'

创建数据库

create database db1;

使用 remoteSecure() 创建远程表以连接到其他集群。
远程集群 A 表的定义

CREATE TABLE db1.table1_remote1_main
(
`id` UInt32,
`timestamp_column` DateTime,
`string_column` String
) AS remoteSecure('clusterA.us-west-2.aws.clickhouse.cloud:9440', 'db1.table1_remote1', 'default', 'Password123!');

远程集群 B 表的定义

CREATE TABLE db1.table1_remote2_main
(
`id` UInt32,
`timestamp_column` DateTime,
`string_column` String
) AS remoteSecure('clusterB.us-east-2.aws.clickhouse.cloud:9440', 'db1.table1_remote2', 'default', 'Password123!')

创建用于收集结果的合并表

create table db1.table1_merge_remote
(
id UInt32,
timestamp_column DateTime,
string_column String
)
engine = Merge('db1', 'table.\_main');

测试结果

clickhouse-cloud :) select * from db1.table1_merge_remote;

SELECT *
FROM db1.table1_merge_remote

Query id: 46b6e741-bbd1-47ed-b40e-69ddb6e0c364

┌─id─┬────timestamp_column─┬─string_column─┐
│ 1 │ 2023-09-29 00:01:00 │ a │
│ 2 │ 2023-09-29 00:02:00 │ b │
│ 3 │ 2023-09-29 00:03:00 │ c │
└────┴─────────────────────┴───────────────┘
┌─id─┬────timestamp_column─┬─string_column─┐
│ 4 │ 2023-09-29 00:04:00 │ x │
│ 5 │ 2023-09-29 00:05:00 │ y │
│ 6 │ 2023-09-29 00:06:00 │ z │
└────┴─────────────────────┴───────────────┘

6 rows in set. Elapsed: 0.275 sec.

更多信息
https://clickhouse.ac.cn/docs/en/sql-reference/table-functions/remote
https://clickhouse.ac.cn/docs/en/engines/table-engines/special/merge

·阅读时间:4分钟

问题

在 ClickHouse 中应该使用什么数据类型来优化查询速度和存储?

答案

在许多情况下,当使用来自其他系统的自动转换或尝试选择数据类型时,用户通常会选择“多多益善”或“选择更容易的”或“选择最通用的”方法。对于数百万甚至数十亿行的较小数据集,这可能有效。它可能不会被注意到,并且对于用户查询差异在其用例中很小的那种数据集是可以接受的。

但是,随着数据量的增长和变得更加明显,它将变得不可接受。

例如,在 WebUI 中,查询花费 50 毫秒和 500 毫秒之间的差异对于大多数用例来说可能是可以的,但一个比另一个慢 10 倍,即使对于前端用户来说,也不太明显。

示例初始表

timestamp Datetime64(9),
group_id Int64,
vendor_id String,
product_id String,
category1 Int64,
code_name String,
paid_status String,
country_code String,
description String,
price Float64,
attributes Map(String, String)

示例数据

3456, 0123456789, bd6087b7-6026-4974-9122-bc99faae5d84, "2024-03-01 01:00:01.000", 98, "bear", paid", "us", "corvette model car", 123.45, {"color" : "blue", "size" : "S"}
156, 0000012345, bd6087b7-6026-4974-9122-bc99faae5d84, "2024-03-01 01:00:02:123", 45, "tiger", "not paid", "uk", "electric car", 53432.10, {"color" : "red", "model" : "X"}
...

以下是一些可以优化此数据的建议

timestamp : DateTime64(9)
除非需要科学精度,否则不太可能需要 9 位精度(纳秒)。可能用于显示或排序,但通常不用于搜索、主键等查询。

  • 建议
    对于主键,按顺序排列:DateTime
    对于显示或排序:添加其他列 - 例如 timestamp_microseconds : DateTime64(6)

group_id : Int64
这似乎是一个整数,选择最小的整数类型,使其适合该列所需的最大数字。根据此示例数据集和列名称,它不太可能需要千万亿个值,可能 Int16 可以工作,它最多可以有 16k 个值。

  • 建议:Int16

vendor_id : String
此列看起来像是数字,但有前导零,可能需要保留格式。并且似乎只有特定数量的字符。

  • 建议:FixedString(10)

product_id : String
这个是字母数字的,所以直觉上应该是字符串,但是,它也是一个 UUID。

  • 建议:UUID

category1 : Int64
值很小,可能类别不多,并且看起来不会增长太多或有限制。少于 255

  • 建议:UInt8

code_name : String
此字段看起来可能只有有限数量的字符串将被使用。对于这种字符串值可能在数百或数千个的情况,低基数字段会有所帮助。

  • 建议:LowCardinality(String)

paid_status : String
有一个字符串值为“paid”或“not_paid”。对于可能只有两个值的情况,请使用布尔值。

  • 建议:Bool

country_code : String
有时会有满足多个优化的列。在此示例中,只有特定数量的国家/地区代码,并且它们都是两个字符的标识符。

  • 建议:LowCardinality(FixedString(2))

price : Float64
当已知固定精度时,不建议使用浮点数,尤其是在财务数据和计算中。最好根据所需的精度使用 Decimal 类型。对于此用例,商品价格可能不超过 999,999.00

  • 建议:Decimal(10,2)

attributes : map
通常可能有一个表,其中包含映射中的动态属性。搜索键或值通常较慢。有几种方法可以使映射更快。如果大多数记录中都存在键,最好将这些键作为低基数放在单独的列中,并将那些在另一列高基数中稀疏的键放在单独的列中。从那里,创建跳过索引将更有效,尽管它可能会增加查询的复杂性。

  • 建议:lc_attributes: Map(String, String), hc_attributes: Map(String, String)。

根据查询,还可以使用以下选项来创建跳过索引和/或提取属性
使用 Array Join 将其提取到使用物化视图的列中:https://clickhouse.ac.cn/docs/knowledgebase/using-array-join-to-extract-and-query-attributes
为键使用跳过索引:https://clickhouse.ac.cn/docs/knowledgebase/improve-map-performance

·阅读时间:4分钟

问题

如果我在使用映射类型的列中具有不同的属性,如何提取它们并在查询中使用它们?

答案

这是一个从可变属性字段提取键和值的示例。此方法将从源/原始表中的每一行创建看似重复的行。但是,由于提取了键和值,因此可以将它们放入主键或具有索引的辅助键中,例如布隆过滤器。

在此示例中,我们基本上有一个创建指标表的源,它具有多个属性,可以应用于具有映射的属性字段。如果某些属性始终存在于记录中,最好将它们提取到自己的列中并填充。

您应该能够直接复制粘贴以查看输出是什么,以及在此实例中物化视图做了什么。

创建一个示例数据库

create database db1;

创建将包含行和属性的初始表

create table db1.table1_metric_map
(
id UInt32,
timestamp DateTime,
metric_name String,
metric_value Int32,
attributes Map(String, String)
)
engine = MergeTree()
order by timestamp;

将示例行插入表中。样本大小故意很小,以便在创建物化视图时,您可以看到每行属性如何被乘以。

insert into db1.table1_metric_map
VALUES
(1, '2023-09-20 00:01:00', 'ABC', 10, {'env':'prod','app':'app1','server':'server1'}),
(2, '2023-09-20 00:01:00', 'ABC', 20,{'env':'prod','app':'app2','server':'server1','dc':'dc1'}),
(3, '2023-09-20 00:01:00', 'ABC', 30,{'env':'qa','app':'app1','server':'server1'}),
(4, '2023-09-20 00:01:00', 'ABC', 40,{'env':'qa','app':'app2','server':'server1','dc':'dc1'}),
(5, '2023-09-20 00:01:00', 'DEF', 50,{'env':'prod','app':'app1','server':'server2'}),
(6, '2023-09-20 00:01:00', 'DEF', 60, {'env':'prod','app':'app2','server':'server1'}),
(7, '2023-09-20 00:01:00', 'DEF', 70,{'env':'qa','app':'app1','server':'server1'}),
(8, '2023-09-20 00:01:00', 'DEF', 80,{'env':'qa','app':'app2','server':'server1'}),
(9, '2023-09-20 00:02:00', 'ABC', 90,{'env':'prod','app':'app1','server':'server1'}),
(10, '2023-09-20 00:02:00', 'ABC', 100,{'env':'prod','app':'app1','server':'server2'}),
(11, '2023-09-20 00:02:00', 'ABC', 110,{'env':'qa','app':'app1','server':'server1'}),
(12, '2023-09-20 00:02:00', 'ABC', 120,{'env':'qa','app':'app1','server':'server1'}),
(13, '2023-09-20 00:02:00', 'DEF', 130,{'env':'prod','app':'app1','server':'server1'}),
(14, '2023-09-20 00:02:00', 'DEF', 140,{'env':'prod','app':'app2','server':'server1','dc':'dc1'}),
(15, '2023-09-20 00:02:00', 'DEF', 150,{'env':'qa','app':'app1','server':'server2'}),
(16, '2023-09-20 00:02:00', 'DEF', 160,{'env':'qa','app':'app1','server':'server1','dc':'dc1'}),
(17, '2023-09-20 00:03:00', 'ABC', 170,{'env':'prod','app':'app1','server':'server1'}),
(18, '2023-09-20 00:03:00', 'ABC', 180,{'env':'prod','app':'app1','server':'server1'}),
(19, '2023-09-20 00:03:00', 'ABC', 190,{'env':'qa','app':'app1','server':'server1'}),
(20, '2023-09-20 00:03:00', 'ABC', 200,{'env':'qa','app':'app1','server':'server2'}),
(21, '2023-09-20 00:03:00', 'DEF', 210,{'env':'prod','app':'app1','server':'server1'}),
(22, '2023-09-20 00:03:00', 'DEF', 220,{'env':'prod','app':'app1','server':'server1'}),
(23, '2023-09-20 00:03:00', 'DEF', 230,{'env':'qa','app':'app1','server':'server1'}),
(24, '2023-09-20 00:03:00', 'DEF', 240,{'env':'qa','app':'app1','server':'server1'});

然后,我们可以使用数组连接创建物化视图,以便它可以将映射属性提取到键和值列中。为了演示,在下面的示例中,它使用隐式表(使用 POPULATE 命令和后备表,如 .inner.{uuid}...)。但是,推荐的最佳实践是使用显式表,您首先定义表,然后使用 TO 命令在其上创建物化视图。

CREATE MATERIALIZED VIEW db1.table1_metric_map_mv
ORDER BY id
POPULATE AS
select
*,
attributes.keys as attribute_keys,
attributes.values as attribute_values
from db1.table1_metric_map
array join attributes
where notEmpty(attributes.keys);

新表将有更多行,并且将提取键,如下所示

SELECT *
FROM db1.table1_metric_map_mv
LIMIT 5

Query id: b7384381-53af-4e3e-bc54-871f61c033a6

┌─id─┬───────────timestamp─┬─metric_name─┬─metric_value─┬─attributes───────────┬─attribute_keys─┬─attribute_values─┐
│ 1 │ 2023-09-20 00:01:00 │ ABC │ 10 │ ('env','prod') │ env │ prod │
│ 1 │ 2023-09-20 00:01:00 │ ABC │ 10 │ ('app','app1') │ app │ app1 │
│ 1 │ 2023-09-20 00:01:00 │ ABC │ 10 │ ('server','server1') │ server │ server1 │
│ 2 │ 2023-09-20 00:01:00 │ ABC │ 20 │ ('env','prod') │ env │ prod │
│ 2 │ 2023-09-20 00:01:00 │ ABC │ 20 │ ('app','app2') │ app │ app2 │
└────┴─────────────────────┴─────────────┴──────────────┴──────────────────────┴────────────────┴──────────────────┘

从这里开始,为了查询需要特定属性的行,您将执行以下操作

SELECT
t1_app.id AS id,
timestamp,
metric_name,
metric_value
FROM
(
SELECT *
FROM db1.table1_metric_map_mv
WHERE (attribute_keys = 'app') AND (attribute_values = 'app1') AND (metric_name = 'ABC')
) AS t1_app
INNER JOIN
(
SELECT *
FROM db1.table1_metric_map_mv
WHERE (attribute_keys = 'server') AND (attribute_values = 'server1')
) AS t2_server ON t1_app.id = t2_server.id

Query id: 72ce7f19-b02a-4b6e-81e7-a955f257436d

┌─id─┬───────────timestamp─┬─metric_name─┬─metric_value─┐
│ 1 │ 2023-09-20 00:01:00 │ ABC │ 10 │
│ 3 │ 2023-09-20 00:01:00 │ ABC │ 30 │
│ 9 │ 2023-09-20 00:02:00 │ ABC │ 90 │
│ 11 │ 2023-09-20 00:02:00 │ ABC │ 110 │
│ 12 │ 2023-09-20 00:02:00 │ ABC │ 120 │
│ 17 │ 2023-09-20 00:03:00 │ ABC │ 170 │
│ 18 │ 2023-09-20 00:03:00 │ ABC │ 180 │
│ 19 │ 2023-09-20 00:03:00 │ ABC │ 190 │
└────┴─────────────────────┴─────────────┴──────────────┘

·阅读时间:3分钟

问题

如何使用 Docker 镜像设置 ClickHouse 以连接到 Microsoft SQL Server?

答案

关于此示例的说明

  • 使用 ClickHouse Docker Ubuntu 镜像
  • 使用 FreeTDS 驱动程序
  • 使用 MSSQL Server 2012R2
  • 此示例中的 Windows 主机名为 MARSDB2.marsnet2.local,IP 为:192.168.1.133(使用您的主机名和/或 IP 更新)
  • MSSQL 实例名称 MARSDB2
  • MSSQL 登录和数据库用户为 sql_user

MSSQL 测试示例设置

在 MSSQL 中创建的数据库和表

Screenshot 2024-01-01 at 8 25 50 PM

MSSQL 登录用户,sql_user

Screenshot 2024-01-01 at 8 27 11 PM

sql_user 的数据库成员资格角色

Screenshot 2024-01-01 at 8 27 35 PM

具有登录权限的数据库用户

Screenshot 2024-01-01 at 8 35 34 PM

使用 ODBC 配置 ClickHouse

创建工作目录

mkdir ch-odbc-mssql
cd ch-odbc-mssql

创建 odbc.ini 文件

vim odbc.ini

添加以下条目以更新 DSN 和 IP 的名称

[marsdb2_mssql]
Driver = FreeTDS
Server = 192.168.1.133

创建 odbcinst.ini 文件

vim odbcinst.ini

添加以下条目(跟踪是可选的,但有助于调试)

[ODBC]
Trace = Yes
TraceFile = /tmp/odbc.log

[FreeTDS]
Description = FreeTDS
Driver = /usr/lib/aarch64-linux-gnu/odbc/libtdsodbc.so
Setup = /usr/lib/x86_64-linux-gnu/odbc/libtdsS.so
UsageCount = 1

配置 Dockerfile 以下载镜像并添加 TDS 和所需的 ODBC 库

创建 Dockerfile

vim Dockerfile

添加 Dockerfile 的内容

FROM clickhouse/clickhouse-server:23.10

# Install the ODBC driver

RUN apt-get update && apt-get install -y --no-install-recommends unixodbc \
&& apt-get install -y freetds-bin freetds-common freetds-dev libct4 libsybdb5 \
&& apt-get install tdsodbc

构建新的 Docker 镜像

docker build . -t marsnet/clickhouse-odbc:23.10

创建 docker-compose.yml 文件

vim docker-compose.yml

将以下内容添加到 YAML 中

version: '3.7'
services:
clickhouse:
image: marsnet/clickhouse-odbc:23.10
container_name: clickhouse-odbc
hostname: clickhouse-host
ports:
- "9000:9000"
- "8123:8123"
- "9009:9009"
volumes:
- ./odbc.ini:/etc/odbc.ini
- ./odbcinst.ini:/etc/odbcinst.ini
restart: always
ulimits:
memlock:
soft: -1
hard: -1
nofile:
soft: 262144
hard: 262144
deploy:
resources:
limits:
memory: 4g

启动容器

docker compose up --detach

启动容器后,您应该会看到类似以下内容

ch-odbc-mssql % docker compose up --detach
[+] Running 1/1
✔ Container clickhouse-odbc Started

检查以确保容器正在运行

ch-odbc-mssql % docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
87a400b803ce marsnet/clickhouse-odbc:23.10 "/entrypoint.sh" 57 minutes ago Up About a minute 0.0.0.0:8123->8123/tcp, 0.0.0.0:9000->9000/tcp, 0.0.0.0:9009->9009/tcp clickhouse-odbc

测试 ODBC 连接

使用 ClickHouse 客户端登录

./clickhouse client

使用 odbc 表函数测试对远程 MSSQL 数据库表的 SELECT 操作

clickhouse-host :) SELECT * from odbc('DSN=marsdb2_mssql;port=1433;Uid=sql_user;Pwd=ClickHouse123;Database=db1', 'table1');

SELECT *
FROM odbc('DSN=marsdb2_mssql;port=1433;Uid=sql_user;Pwd=ClickHouse123;Database=db1', 'table1')

Query id: 23494da2-6e12-4ade-95fa-372a0420cac1

┌─id─┬─column1─┐
│ 1 │ abc │
│ 2 │ def │
│ 3 │ ghi │
└────┴─────────┘

3 rows in set. Elapsed: 0.188 sec.

您还可以使用 odbc 表引擎创建远程表

CREATE TABLE table1_odbc_mssql
(
`id` Int32,
`column1` String
)
ENGINE = ODBC('DSN=marsdb2_mssql;port=1433;Uid=sql_user;Pwd=ClickHouse123;Database=db1', 'dbo', 'table1')

使用 SELECT 查询测试新的远程表

clickhouse-host :) select * from table1_odbc_mssql;

SELECT *
FROM table1_odbc_mssql

Query id: 94724368-485d-4364-ae58-a435a225c37d

┌─id─┬─column1─┐
│ 1 │ abc │
│ 2 │ def │
│ 3 │ ghi │
└────┴─────────┘

3 rows in set. Elapsed: 0.218 sec.

更多信息,请参阅

·阅读时间:2分钟

问题

如何使用源表或目标表处理 JSON 消息,并使用物化视图进行提取?
如何在没有实验性 JSON 对象的情况下处理 JSON?

答案

处理 JSON 数据的常用模式是将数据发送到目标表,并使用 JSONExtract 函数将数据提取到新表中,使用物化视图触发器。这通常在以下流程和模式中完成

source data --> MergeTree table --> Materialized View (with base table) --> application/client

目标表应具有一个 raw 字符串字段,您可以在其中存储原始 JSON。它还应具有一个或两个其他字段,可用于管理该表,以便随着数据的老化可以对其进行分区和修剪。

*某些集成可以将字段添加到原始数据中,例如,如果使用 ClickHouse Kafka 连接器接收器。

以下是一个简化的示例

  • 创建示例数据库
create database db1;
  • 创建一个目标表,您的原始 JSON 将插入其中
create table db1.table2_json_raw
(
id Int32,
timestamp DateTime,
raw String
)
engine = MergeTree()
order by timestamp;
  • 为物化视图创建基础表
create table db1.table2_json_mv_base
(
id Int32,
timestamp DateTime,
raw_string String,
custId Int8,
custName String
)
engine = MergeTree()
order by timestamp;
  • 为基础表创建物化视图
create materialized view db1.table2_json_mv to db1.table2_json_mv_base
AS SELECT
id,
timestamp,
raw as raw_string,
simpleJSONExtractRaw(raw, 'customerId') as custId,
simpleJSONExtractRaw(raw, 'customerName') as custName
FROM
db1.table2_json_raw;
  • 插入一些示例行
 insert into db1.table2_json_raw
values
(1, '2024-05-16 00:00:00', '{"customerId":1, "customerName":"ABC"}'),
(2, '2024-05-16 00:00:01', '{"customerId":2, "customerName":"XYZ"}');
  • 查看提取结果和将在查询中使用的物化视图
clickhouse-cloud :) select * from db1.table2_json_mv;

SELECT *
FROM db1.table2_json_mv

Query id: 12655fd3-567a-4dfb-9ef7-abc4b11ad044

┌─id─┬───────────timestamp─┬─raw_string─────────────────────────────┬─custId─┬─custName─┐
│ 1 │ 2024-05-16 00:00:00 │ {"customerId":1, "customerName":"ABC"} │ 1 │ "ABC" │
│ 2 │ 2024-05-16 00:00:01 │ {"customerId":2, "customerName":"XYZ"} │ 2 │ "XYZ" │
└────┴─────────────────────┴────────────────────────────────────────┴────────┴──────────┘

其他参考链接
物化视图:https://clickhouse.ac.cn/docs/en/guides/developer/cascading-materialized-views
使用 JSON:https://clickhouse.ac.cn/docs/en/integrations/data-formats/json#other-approaches
JSON 函数:https://clickhouse.ac.cn/docs/en/sql-reference/functions/json-functions