云数据库ClickHouse企业版已将实验性的Object(JSON)数据类型替换为生产就绪的新版JSON类型。如果您在25.10或以下版本中使用了Object(JSON)类型,需要在升级到25.12版本前完成迁移。
背景信息
旧的半结构化数据类型Object(JSON)(等价写法Object('json'))已被弃用,并从25.12版本的代码库中彻底移除。所有使用该类型的实例都需要迁移到新版JSON类型。
迁移的核心目标是将所有Object(JSON)列就地迁移为JSON列,且不丢失数据、不长时间中断业务。迁移可以通过一条ALTER TABLE ... MODIFY COLUMN变更完成。
迁移必须在仍支持Object(JSON)的版本(≤25.10)上完成,然后才能升级到25.12。升级到25.12后将无法执行迁移操作。
迁移前的版本与设置
建议先升级到25.10版本再迁移,新的JSON类型表现更稳定。
迁移操作对版本有如下要求:
集群版本 | 迁移操作说明 |
25.3~25.10 | 新版 |
24.10~25.2 | 需要在执行迁移的会话中显式开启 |
24.8~24.9 | 需要在执行迁移的会话中显式开启 |
相关参数说明如下:
参数 | 作用 | 默认值演变 | 迁移时处理方式 |
| 允许使用新版 | 24.10引入(默认false)→ 25.3起默认true → 现已obsolete(恒为true) | ≥25.3无需设置;24.10~25.2需在会话中加 |
| 同上( | 24.8引入(默认false)→ 25.3起默认true → 现已obsolete | 24.8~24.9需 |
| 让 | 24.8起由true改为false → 现已obsolete(恒为false) | 保持默认false,任何时候都不要开启,否则 |
| 允许创建或使用旧 | 默认false → 现已obsolete | 迁移目标是新类型,无需开启 |
操作步骤
步骤一:查找需要迁移的列
如果您管理多个实例,可以先通过聚合查询统计仍在使用旧类型的实例和表数量:
SELECT
uniqExact(spoken_name) AS instances,
groupUniqArray(spoken_name),
uniqExact(database, name) AS tables,
any(create_table_query)
FROM merge('tables.*')
WHERE create_table_query LIKE '%Object(\'json\')%'
AND spoken_name NOT LIKE '%stress%'
AND scrape_time_microseconds > now() - INTERVAL 40 DAY
AND database NOT IN ('system', 'information_schema', 'INFORMATION_SCHEMA')在单个实例上,执行以下SQL查询获取所有使用旧Object(JSON)类型的列清单:
SELECT database, table, name AS column, type
FROM system.columns
WHERE type LIKE '%Object(%json%)%'
AND database NOT IN ('system', 'information_schema', 'INFORMATION_SCHEMA')
ORDER BY database, table, name;查询结果将列出所有需要迁移的数据库名、表名、列名和当前类型。
步骤二:执行迁移
迁移前请注意以下事项:
建议先在测试环境或克隆表上验证迁移后查询的兼容性。新版
JSON的路径访问语法与旧Object存在差异,详情请参见步骤三:迁移后查询适配。变更需要读取整列并重写数据Part,大表的内存和磁盘开销较高。建议选择业务低峰期执行,大表可按分区分批迁移。
您可以根据业务需求选择以下任一方案执行迁移。
方案一:原地修改列类型(推荐)
对每个需要迁移的列,执行ALTER TABLE ... MODIFY COLUMN语句将类型改为新版JSON:
ALTER TABLE <db>.<table>
MODIFY COLUMN <column> JSON
SETTINGS mutations_sync = 1;
-- 在25.3以下的版本上,会话需额外开启 enable_json_type = 1参数说明:
mutations_sync = 1:同步等待变更在当前副本上完成。该变更会重写包含该列的所有数据Part,将旧
Object存储格式转换为新JSON类型的存储格式。
方案二:新建表并迁移数据
如果您希望通过新建表的方式迁移数据,可以按照以下步骤操作:
1. 创建新表,结构与旧表一致:
CREATE TABLE new_table AS old_table;2. 修改新表的列类型为新JSON:
ALTER TABLE new_table MODIFY COLUMN `your_json_column` JSON;3. 将旧表数据插入新表:
INSERT INTO new_table SELECT * FROM old_table;步骤三:迁移后查询适配
迁移完成后,写入操作(INSERT)通常无需修改,但查询操作(SELECT)可能需要适配。核心差异在于:旧Object的路径访问返回的是推断出的具体类型,而新JSON的路径访问返回的是Dynamic类型。
写入操作(无需修改)
新旧类型都接受JSON文档作为输入,插入侧代码通常无需改动。唯一需要留意的是:如果代码里硬编码了类型名字符串(如建表DDL、CAST目标类型里写死了Object('json')),需要把类型名改成JSON。纯数据写入路径不受影响。
写入方式 | 旧Object(JSON) | 新JSON |
行格式导入 |
| 完全一致 |
整列当字符串导入 |
| 完全一致 |
|
| 完全一致 |
从 |
|
|
查询操作(需要逐条检查)
查询操作需要根据以下场景逐一检查和适配:
场景 | 旧Object语法 | 新JSON语法 |
只取值(展示或透传,不做运算) |
|
|
读标量路径并参与运算、函数或比较 |
|
|
|
|
|
对象数组 |
| 是 |
读嵌套子对象 |
| 用 |
新语法示例如下:
-- 标量取类型
SELECT json.a.g.:Float64, json.d.:Date FROM t;
-- 分组 / 排序需放开设置
SELECT json.repo.name, count() AS c
FROM t
GROUP BY json.repo.name
ORDER BY c DESC
SETTINGS allow_suspicious_types_in_group_by = 1, allow_suspicious_types_in_order_by = 1;
-- 对象数组:[] 表示数组层级,可配合 ARRAY JOIN
SELECT json.payload.commits[].author.name FROM t;
-- 读整个子对象
SELECT json.^metadata FROM t;步骤四:验证迁移完成
确认集群中不再存在Object(JSON)类型的列:
SELECT count() FROM system.columns
WHERE type LIKE '%Object(%json%)%'
AND database NOT IN ('system', 'information_schema', 'INFORMATION_SCHEMA');确认所有JSON相关变更均已完成:
SELECT count() FROM system.mutations
WHERE command ILIKE '%JSON%' AND is_done = 0;两者均返回0即视为该实例迁移完成。
常见问题
大表迁移内存超限
大表在把整列转换为JSON时可能触发内存超限错误。典型错误信息如下:
latest_fail_reason: (total) memory limit exceeded: would use 7.20 GiB
... while executing 'FUNCTION _CAST(<column> :: 0, 'JSON' :: 1) -> _CAST(<column>, 'JSON') JSON'
(while reading from part .../all_1_1_0 located on disk s3WithKeeperDisk of type s3)
While executing MergeTreeSequentialSource
latest_fail_error_code_name: MEMORY_LIMIT_EXCEEDED可通过以下方式缓解:
扩容或临时提升实例内存上限。
升级到更新的版本(建议升级到25.10)后重试。
对超大表按分区分批迁移,降低单次变更的峰值内存。
变更失败不会丢失数据。修正问题后,mutation会在失败的Part上自动重试,直至is_done = 1。
查看迁移进度
可通过以下查询查看正在执行的JSON相关变更及其进度:
SELECT *
FROM system.mutations
WHERE command ILIKE '%JSON%'
AND is_done = 0;关注parts_to_do(剩余待处理Part数)、is_done和latest_fail_reason字段。