跳到主要内容

ClickHouse 存储

ClickHouse 是专为分析工作负载设计的列式数据库。@mastra/clickhouse 包为多个 Mastra storage domain 提供存储适配器,是生产环境 observability 的推荐后端。

ClickHouse 最常作为组合存储配置中的专用 observability 后端,其他 domain 则由另一数据库提供服务。

何时使用 ClickHouse
何时使用 ClickHouse的直接链接

用于生产环境的 traces、logs、metrics、scores 和 feedback 的 observability。

本地开发时,请使用组合 LibSQL(用于 memory 和 workflows)与 @mastra/duckdb(用于 observability)的 composite store。二者单独使用都不能覆盖开发环境:LibSQL 未实现 observability domain,而 DuckDB 未实现其他 domain。示例请参阅 observability 概览

安装
安装的直接链接

npm install @mastra/clickhouse@latest

你还需要运行中的 ClickHouse server。有关托管和自托管选项,请参阅托管选项

使用方法
使用方法的直接链接

ObservabilityStorageClickhouseVNext 是当前的 observability domain 实现。它使用由 ReplacingMergeTree 支持的仅插入 schema,并针对 traces、logs、metrics、scores 和 feedback 产生的数据量进行了优化。

将其与另一存储适配器组合,以避免 observability 写入与应用程序数据争抢资源:

src/mastra/index.ts
import { Mastra } from '@mastra/core'
import { MastraCompositeStore } from '@mastra/core/storage'
import { PostgresStore } from '@mastra/pg'
import { ObservabilityStorageClickhouseVNext } from '@mastra/clickhouse'
import { Observability, MastraStorageExporter } from '@mastra/observability'

const observabilityStore = new ObservabilityStorageClickhouseVNext({
url: process.env.CLICKHOUSE_URL!,
username: process.env.CLICKHOUSE_USERNAME!,
password: process.env.CLICKHOUSE_PASSWORD!,
})

export const mastra = new Mastra({
storage: new MastraCompositeStore({
id: 'composite-storage',
default: new PostgresStore({
id: 'pg',
connectionString: process.env.DATABASE_URL!,
}),
domains: {
observability: observabilityStore,
},
}),
observability: new Observability({
configs: {
default: {
serviceName: 'mastra',
exporters: [new MastraStorageExporter()],
},
},
}),
})

当 ClickHouse 是 observability 后端时,MastraStorageExporter 会自动选择 insert-only 策略,以获得最高写入吞吐量。详情请参阅 tracing 策略

使用 legacy domain 的 observability
使用 legacy domain 的 observability的直接链接

ObservabilityStorageClickhouse 是原始的 observability adapter,尚未迁移到 vNext schema 的项目仍受支持。其配置形式与 vNext class 相同。

import { ObservabilityStorageClickhouse } from '@mastra/clickhouse'

const observabilityStore = new ObservabilityStorageClickhouse({
url: process.env.CLICKHOUSE_URL!,
username: process.env.CLICKHOUSE_USERNAME!,
password: process.env.CLICKHOUSE_PASSWORD!,
})

新项目应改用 ObservabilityStorageClickhouseVNext

从 legacy 迁移到 vNext
从 legacy 迁移到 vNext的直接链接

要将历史 spans 从 legacy mastra_ai_spans 表迁移到 vNext schema,请运行:

npx mastra migrate

迁移会按天大小的批次将 span 数据从 mastra_ai_spans 复制到 mastra_span_events。它会处理列映射并对 legacy rows 去重。原始表将保留作为备份。迁移后,traces 会通过 vNext adapter 显示在 Studio 中。

备注

legacy 表不会被删除。验证迁移结果后,请手动删除它。

将 ClickHouse 用于每个 domain
将 ClickHouse 用于每个 domain的直接链接

ClickhouseStoreVNext 使用 ClickHouse 支持 memoryworkflowsobservability domains,并自动使用 vNext observability adapter。当你希望 ClickHouse 支持整个应用程序、且不想手动配置 composite store 时,请使用它。

src/mastra/index.ts
import { Mastra } from '@mastra/core'
import { ClickhouseStoreVNext } from '@mastra/clickhouse'

export const mastra = new Mastra({
storage: new ClickhouseStoreVNext({
id: 'clickhouse-storage',
url: process.env.CLICKHOUSE_URL!,
username: process.env.CLICKHOUSE_USERNAME!,
password: process.env.CLICKHOUSE_PASSWORD!,
}),
})

ClickhouseStoreVNext 接受与 ClickhouseStore 相同的配置,并在每个 domain 中复用同一个 ClickHouse client。

手动组合
手动组合的直接链接

ClickhouseStore 是长期以来使用 legacy observability adapter 支持每个 domain 的 class。新项目应优先使用 ClickhouseStoreVNext。如果需要自定义 composite(例如,使用不同后端覆盖某个 domain),请手动构建:

src/mastra/index.ts
import { Mastra } from '@mastra/core'
import { MastraCompositeStore } from '@mastra/core/storage'
import { ClickhouseStore, ObservabilityStorageClickhouseVNext } from '@mastra/clickhouse'

const credentials = {
url: process.env.CLICKHOUSE_URL!,
username: process.env.CLICKHOUSE_USERNAME!,
password: process.env.CLICKHOUSE_PASSWORD!,
}

export const mastra = new Mastra({
storage: new MastraCompositeStore({
id: 'composite-storage',
default: new ClickhouseStore({ id: 'clickhouse-storage', ...credentials }),
domains: {
observability: new ObservabilityStorageClickhouseVNext(credentials),
},
}),
})

使用自己的 ClickHouse client
使用自己的 ClickHouse client的直接链接

当你需要请求超时、压缩或 interceptors 等自定义连接设置时,请传入预先配置的 client:

import { createClient } from '@clickhouse/client'
import { ClickhouseStore } from '@mastra/clickhouse'

const client = createClient({
url: process.env.CLICKHOUSE_URL!,
username: process.env.CLICKHOUSE_USERNAME!,
password: process.env.CLICKHOUSE_PASSWORD!,
request_timeout: 60_000,
compression: { request: true, response: true },
})

const storage = new ClickhouseStore({ id: 'clickhouse-storage', client })

ObservabilityStorageClickhouseObservabilityStorageClickhouseVNext 也接受相同形式的 client

配置
配置的直接链接

ClickhouseStore 选项
clickhousestore-options的直接链接

id:

string
此存储实例的唯一标识符。

url?:

string
ClickHouse server URL(例如,https://your-instance.clickhouse.cloud:8443http://localhost:8123)。未传入预先配置的 client 时必填。

username?:

string
ClickHouse username。未传入预先配置的 client 时必填。

password?:

string
ClickHouse password。未传入预先配置的 client 时必填。本地实例的默认 user 可以使用空字符串。

client?:

ClickHouseClient
来自 @clickhouse/client 的预先配置的 ClickHouse client。需要自定义请求设置时使用它。与上述凭据字段互斥。

ttl?:

object
在创建表时应用的每表 TTL 配置。接受从 NANOSECONDYEAR 的 interval 单位的 row-level 和 column-level TTL。

replication?:

{ cluster?: string; zookeeperPath?: string; replicaName?: string }
适用于多副本 ClickHouse cluster 的可选 replicated table 配置。设置后,Mastra 会创建 replicated MergeTree tables。设置 cluster 后,Mastra 还会向由 Mastra 管理的 data definition language (DDL) 添加 ON CLUSTER

disableInit?:

boolean
= false
true 时,store 在首次使用时不会创建表或运行 migrations。请从 deployment scripts 中显式调用 storage.init()

ClickhouseStore 还接受 ClickHouseClientConfigOptions 中的所有选项(例如 databaserequest_timeoutcompressionkeep_alivemax_open_connections)。

Replicated clusters
Replicated clusters的直接链接

当 Mastra 通过 load balancer 向多副本 ClickHouse cluster 写入数据时,请使用 replication

const storage = new ClickhouseStoreVNext({
id: 'clickhouse-storage',
url: process.env.CLICKHOUSE_URL!,
username: process.env.CLICKHOUSE_USERNAME!,
password: process.env.CLICKHOUSE_PASSWORD!,
replication: {
cluster: 'company_cluster',
},
})

设置 replication 后,Mastra 会将其 MergeTreeReplacingMergeTree table engines 改写为 ReplicatedMergeTreeReplicatedReplacingMergeTree。默认 engine arguments 为:

  • zookeeperPath: '/clickhouse/tables/{shard}/{database}/{table}'
  • replicaName: '{replica}'

默认值符合最常见的 self-managed convention。如果 cluster 中的现有 tables 使用不同的布局(例如不含 {database} 部分的 /clickhouse/tables/{shard}/{table}),请显式设置 zookeeperPath 以匹配该布局。Mastra 不会从 Keeper 读取 cluster 的 convention,因此不匹配的默认值会将 Mastra 的 metadata 写入与 cluster 其他部分分离的 branch。

new ClickhouseStoreVNext({
url: process.env.CLICKHOUSE_URL!,
username: process.env.CLICKHOUSE_USERNAME!,
password: process.env.CLICKHOUSE_PASSWORD!,
replication: {
cluster: 'company_cluster',
zookeeperPath: '/clickhouse/tables/{shard}/{table}',
},
})

设置 cluster 可将 ON CLUSTER 添加到由 Mastra 管理的 DDL,例如创建表、创建 materialized view、column migrations、TTL changes 和删除表。

设置 cluster 后,optimizeTable()materializeTtl() 等手动维护操作会在每个 replica 上运行。这些操作在大型 cluster 上可能成本很高。请优先在非高峰时段运行它们,并让常规 merges 在 background merge queue 上进行,而不是在每次重启时触发。

如果现有 Mastra tables 使用本地 MergeTreeReplacingMergeTree engines,在启用 replication 时初始化会失败。由于 copy-and-swap 在 replicas 间并不安全,Mastra 拒绝静默转换 local tables。要迁移,请先将受影响的 tables 重新创建为 Replicated*,再启用 replication。为安全迁移,请重命名 local table,运行 CREATE TABLE ... ENGINE = ReplicatedMergeTree(...) ON CLUSTER ...,运行 INSERT INTO ... SELECT * FROM <renamed_local>,然后删除 <renamed_local>

不要在 ClickHouse Cloud 上设置 replication。Cloud 会在 server-side 将 MergeTree 改写为 SharedMergeTree,而显式的 ReplicatedMergeTree engines 会生成错误的 DDL。replication 仅适用于 self-managed 多副本 clusters。

Observability domain 选项
Observability domain 选项的直接链接

ObservabilityStorageClickhouseObservabilityStorageClickhouseVNext 接受与 ClickhouseStore 相同的连接选项(urlusernamepassword 或预先配置的 client)。

托管选项
托管选项的直接链接

只要可以通过 HTTP 访问,ClickHouse 就能运行。常见选择包括:

本地开发时:

docker run -d --name mastra-clickhouse \
-p 8123:8123 -p 9000:9000 \
-e CLICKHOUSE_USER=default \
-e CLICKHOUSE_PASSWORD=password \
clickhouse/clickhouse-server
new ObservabilityStorageClickhouseVNext({
url: 'http://localhost:8123',
username: 'default',
password: 'password',
})

部署到 Railway 及类似平台
部署到 Railway 及类似平台的直接链接

RailwayFly.ioRender 和 Heroku 等平台会在 ephemeral filesystems 上运行 application containers。DuckDB 等嵌入式 observability backends 需要可写的 persistent local file,因此它们要么会在重启时丢失数据,要么根本无法部署到这些平台。

请改用 ClickHouse。由于 ClickHouse 可通过 HTTP 访问,相同的连接可从任意 host 使用:

src/mastra/index.ts
import { Mastra } from '@mastra/core'
import { MastraCompositeStore } from '@mastra/core/storage'
import { PostgresStore } from '@mastra/pg'
import { ObservabilityStorageClickhouseVNext } from '@mastra/clickhouse'
import { Observability, MastraStorageExporter } from '@mastra/observability'

export const mastra = new Mastra({
storage: new MastraCompositeStore({
id: 'composite-storage',
default: new PostgresStore({
id: 'pg',
connectionString: process.env.DATABASE_URL!,
}),
domains: {
observability: new ObservabilityStorageClickhouseVNext({
url: process.env.CLICKHOUSE_URL!,
username: process.env.CLICKHOUSE_USERNAME!,
password: process.env.CLICKHOUSE_PASSWORD!,
}),
},
}),
observability: new Observability({
configs: {
default: {
serviceName: 'mastra',
exporters: [new MastraStorageExporter()],
},
},
}),
})

可采用以下任一方式配置 database:

  • Managed:使用 ClickHouse Cloud。在 hosting platform 中将 CLICKHOUSE_URLCLICKHOUSE_USERNAMECLICKHOUSE_PASSWORD 设置为 environment variables。
  • Self-hosted on Railway:从官方 Docker image 向 Railway project 添加 ClickHouse service,然后通过 Railway 的 private networking 在 application service 中引用它。

同一方法适用于其他使用 ephemeral filesystems 的 hosts。对于也应存储在 host 外的 application data,请将此配置与用于 default storage 的托管 PostgreSQL 或 LibSQL/Turso instance 配合使用。

注意

不要将 DuckDB 等嵌入式 backend 指向 ephemeral container filesystem 内的 path。container 重启时,写入该处的数据会丢失;在某些平台上,该 path 是只读的。

初始化
初始化的直接链接

传入 Mastra class 后,ClickhouseStore 会自动调用 init() 以创建 schema 并运行所有待处理的 migrations。通过 MastraCompositeStore 使用 ObservabilityStorageClickhouseVNext 时也是如此。

如果你在 Mastra 外管理 storage,请显式调用 init()

import { ObservabilityStorageClickhouseVNext } from '@mastra/clickhouse'

const observability = new ObservabilityStorageClickhouseVNext({
url: process.env.CLICKHOUSE_URL!,
username: process.env.CLICKHOUSE_USERNAME!,
password: process.env.CLICKHOUSE_PASSWORD!,
})

await observability.init()

在 CI/CD pipelines 中,请在 ClickhouseStore 上设置 disableInit: true,并从使用 elevated credentials 的 deployment step 运行 init()。这样,runtime application credentials 就可以限制为 read 和 insert。

Observability
Observability的直接链接

ClickHouse 是生产环境 observability 的推荐 backend:

  • Insert-only strategyMastraStorageExporter 批量写入已完成的 spans,且不进行逐 span 更新,这是可用的最高吞吐量策略。
  • Columnar compression:与 row-oriented databases 中的相同数据相比,span attributes 和 log payloads 的压缩效果更佳。

完整的策略矩阵和生产环境指南请参阅 MastraStorageExporter 参考