Table of Contents
每日一句正能量
理解不是妥协,而是打开格局的钥匙。
看清事物因果与逻辑。钥匙打开了门,让你看清全貌,但进不进去、如何行动,主动权依然在你。
一、前言:数据类型是迁移的基石
前面四篇文章,我分享了MySQL迁移KaiwuDB的整体流程、KDTS图形化工具、DataX脚本化迁移、CSV离线兜底方案。有读者反馈:“迁移过程中最头疼的不是工具使用,而是数据类型不匹配导致的各种报错。”
确实,数据类型映射是数据库迁移中最基础也最容易出问题的环节。MySQL和KaiwuDB虽然都兼容SQL标准,但在具体实现上存在差异。一个小小的类型不匹配,可能导致数据截断、精度丢失,甚至迁移失败。
本文就把MySQL到KaiwuDB的数据类型映射完整梳理一遍,包括类型差异对比、常见陷阱、解决方案,以及我踩过的坑。
二、数据类型对比总览
2.1 整数类型
| MySQL类型 | KaiwuDB类型 | 存储范围 | 注意事项 |
|---|---|---|---|
| TINYINT | INT2 | -128 ~ 127 | 直接映射 |
| TINYINT UNSIGNED | INT2 | 0 ~ 255 | 直接映射 |
| SMALLINT | INT2 | -32768 ~ 32767 | 直接映射 |
| SMALLINT UNSIGNED | INT4 | 0 ~ 65535 | 需放大到INT4 |
| MEDIUMINT | INT4 | -8388608 ~ 8388607 | 直接映射 |
| MEDIUMINT UNSIGNED | INT4 | 0 ~ 16777215 | 需放大到INT4 |
| INT | INT4 | -2147483648 ~ 2147483647 | 直接映射 |
| INT UNSIGNED | INT8 | 0 ~ 4294967295 | 需放大到INT8 |
| BIGINT | INT8 | -9223372036854775808 ~ 9223372036854775807 | 直接映射 |
| BIGINT UNSIGNED | NUMERIC(20) | 0 ~ 18446744073709551615 | 必须映射为NUMERIC(20) |
踩坑点1:BIGINT UNSIGNED映射错误
-- MySQL
CREATE TABLE test (
id BIGINT UNSIGNED PRIMARY KEY
);
-- 错误:直接映射为INT8
CREATE TABLE test (
id INT8 PRIMARY KEY -- 错误!会溢出
);
-- 正确:映射为NUMERIC(20)
CREATE TABLE test (
id NUMERIC(20) PRIMARY KEY
);
原因:KaiwuDB的INT8最大值为9223372036854775807,而MySQL的BIGINT UNSIGNED最大值为18446744073709551615,直接映射会导致溢出。
2.2 浮点类型
| MySQL类型 | KaiwuDB类型 | 精度 | 注意事项 |
|---|---|---|---|
| FLOAT | FLOAT4 | 单精度 | 直接映射 |
| DOUBLE | FLOAT8 | 双精度 | 直接映射 |
| DECIMAL(M,D) | DECIMAL(M,D) | 定点数 | 直接映射 |
| NUMERIC(M,D) | NUMERIC(M,D) | 定点数 | 直接映射 |
踩坑点2:DECIMAL精度丢失
-- MySQL
CREATE TABLE test (
amount DECIMAL(18,2)
);
-- KaiwuDB(正确)
CREATE TABLE test (
amount DECIMAL(18,2)
);
-- 错误:改为FLOAT8
CREATE TABLE test (
amount FLOAT8 -- 错误!会丢失精度
);
原因:FLOAT8是浮点数,无法精确表示小数,会导致金额计算错误。DECIMAL是定点数,可以精确表示小数。
2.3 字符串类型
| MySQL类型 | KaiwuDB类型 | 最大长度 | 注意事项 |
|---|---|---|---|
| CHAR(N) | CHAR(N) | N字符 | 直接映射 |
| VARCHAR(N) | VARCHAR(N) | N字符 | 直接映射 |
| TINYTEXT | TEXT | 255字节 | 直接映射 |
| TEXT | TEXT | 65535字节 | 直接映射 |
| MEDIUMTEXT | TEXT | 16777215字节 | 直接映射 |
| LONGTEXT | TEXT | 无限制 | 直接映射 |
| BINARY(N) | BYTES | N字节 | 直接映射 |
| VARBINARY(N) | VARBYTES | N字节 | 直接映射 |
| TINYBLOB | BYTES | 255字节 | 直接映射 |
| BLOB | BYTES | 65535字节 | 直接映射 |
| MEDIUMBLOB | BYTES | 16777215字节 | 直接映射 |
| LONGBLOB | BYTES | 无限制 | 直接映射 |
踩坑点3:TEXT字段长度超限
-- MySQL
CREATE TABLE test (
content LONGTEXT
);
-- KaiwuDB(正确)
CREATE TABLE test (
content TEXT -- TEXT在KaiwuDB中无长度限制
);
注意:KaiwuDB的TEXT类型没有长度限制,可以直接映射MySQL的LONGTEXT。
2.4 日期时间类型
| MySQL类型 | KaiwuDB类型 | 精度 | 注意事项 |
|---|---|---|---|
| DATE | DATE | 日期 | 直接映射 |
| TIME | TIME | 时间 | 直接映射 |
| DATETIME | TIMESTAMP | 日期时间 | 注意时区 |
| TIMESTAMP | TIMESTAMPTZ | 日期时间+时区 | 直接映射 |
| YEAR | INT2 | 年份 | 映射为INT2 |
踩坑点4:DATETIME时区问题
-- MySQL
CREATE TABLE test (
created_at DATETIME
);
-- KaiwuDB(正确)
CREATE TABLE test (
created_at TIMESTAMP -- 注意:KaiwuDB的TIMESTAMP会自动处理时区
);
-- 如果需要保留原始时区信息
CREATE TABLE test (
created_at TIMESTAMPTZ -- 带时区的TIMESTAMP
);
原因:MySQL的DATETIME不存储时区信息,而KaiwuDB的TIMESTAMP会转换为UTC存储。如果业务需要保留原始时区,应使用TIMESTAMPTZ。
2.5 JSON类型
| MySQL类型 | KaiwuDB类型 | 说明 | 注意事项 |
|---|---|---|---|
| JSON | JSON | JSON数据 | 直接映射 |
踩坑点5:JSON字段长度超限
-- MySQL
CREATE TABLE test (
ext JSON
);
-- KaiwuDB(正确)
CREATE TABLE test (
ext JSON
);
-- 如果JSON数据过大
CREATE TABLE test (
ext TEXT -- 映射为TEXT,避免长度限制
);
原因:KaiwuDB的JSON类型有默认长度限制,如果JSON数据过大,可以映射为TEXT类型。
2.6 枚举和集合类型
| MySQL类型 | KaiwuDB类型 | 说明 | 注意事项 |
|---|---|---|---|
| ENUM | VARCHAR + CHECK | 枚举值 | 需要手动创建CHECK约束 |
| SET | ARRAY或JSON | 集合值 | 需要转换 |
踩坑点6:ENUM类型映射
-- MySQL
CREATE TABLE test (
status ENUM('active', 'inactive', 'deleted')
);
-- KaiwuDB(正确)
CREATE TABLE test (
status VARCHAR(20) CHECK (status IN ('active', 'inactive', 'deleted'))
);
原因:KaiwuDB不支持ENUM类型,需要使用VARCHAR + CHECK约束实现。
三、特殊类型处理
3.1 自增主键
| MySQL | KaiwuDB | 说明 |
|---|---|---|
| AUTO_INCREMENT | 不支持 | 需要应用层生成或使用序列 |
解决方案:
-- MySQL
CREATE TABLE orders (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
order_no VARCHAR(64)
);
-- KaiwuDB(使用序列)
CREATE SEQUENCE orders_id_seq START 1 INCREMENT 1;
CREATE TABLE orders (
id NUMERIC(20) DEFAULT nextval('orders_id_seq') PRIMARY KEY,
order_no VARCHAR(64)
);
-- 或者应用层生成ID
CREATE TABLE orders (
id NUMERIC(20) PRIMARY KEY,
order_no VARCHAR(64)
);
3.2 位类型
| MySQL | KaiwuDB | 说明 |
|---|---|---|
| BIT(N) | INT2/INT4/INT8 | 根据位数选择 |
解决方案:
-- MySQL
CREATE TABLE test (
flags BIT(8)
);
-- KaiwuDB
CREATE TABLE test (
flags INT2 -- 8位用INT2
);
3.3 空间类型
| MySQL | KaiwuDB | 说明 |
|---|---|---|
| GEOMETRY | 不支持 | 需要转换 |
| POINT | 不支持 | 需要转换 |
| LINESTRING | 不支持 | 需要转换 |
| POLYGON | 不支持 | 需要转换 |
解决方案:
-- MySQL
CREATE TABLE test (
location POINT
);
-- KaiwuDB(使用JSON存储)
CREATE TABLE test (
location JSON -- 存储为{"x": 123.45, "y": 67.89}
);
-- 或者使用两个字段
CREATE TABLE test (
location_x FLOAT8,
location_y FLOAT8
);
四、类型映射工具
4.1 手动映射
根据上面的对照表,手动编写CREATE TABLE语句。
4.2 自动映射(KDTS)
KDTS会自动识别MySQL的类型并映射到KaiwuDB,但需要人工确认。
4.3 脚本自动映射
#!/usr/bin/env python3
# mysql_to_kaiwudb_types.py
mysql_to_kaiwudb = {
'TINYINT': 'INT2',
'SMALLINT': 'INT2',
'MEDIUMINT': 'INT4',
'INT': 'INT4',
'BIGINT': 'INT8',
'FLOAT': 'FLOAT4',
'DOUBLE': 'FLOAT8',
'DECIMAL': 'DECIMAL',
'NUMERIC': 'NUMERIC',
'CHAR': 'CHAR',
'VARCHAR': 'VARCHAR',
'TEXT': 'TEXT',
'BLOB': 'BYTES',
'DATE': 'DATE',
'TIME': 'TIME',
'DATETIME': 'TIMESTAMP',
'TIMESTAMP': 'TIMESTAMPTZ',
'JSON': 'JSON',
'ENUM': 'VARCHAR',
'SET': 'ARRAY',
'BIT': 'INT2',
}
def map_type(mysql_type, unsigned=False):
"""映射MySQL类型到KaiwuDB类型"""
base_type = mysql_type.upper().split('(')[0]
if base_type == 'BIGINT' and unsigned:
return 'NUMERIC(20)'
if base_type == 'INT' and unsigned:
return 'INT8'
return mysql_to_kaiwudb.get(base_type, 'TEXT')
# 测试
print(map_type('BIGINT', unsigned=True)) # NUMERIC(20)
print(map_type('INT', unsigned=True)) # INT8
print(map_type('VARCHAR(64)')) # VARCHAR
print(map_type('DECIMAL(18,2)')) # DECIMAL
五、常见问题排查
5.1 数据截断
问题:导入时数据被截断
ERROR: value too long for type character varying(64)
解决方案:
-- 检查源数据最大长度
SELECT MAX(LENGTH(order_no)) FROM orders;
-- 调整目标字段长度
ALTER TABLE orders ALTER COLUMN order_no TYPE VARCHAR(128);
5.2 精度丢失
问题:金额计算出现精度丢失
-- 错误:使用FLOAT8
SELECT SUM(amount) FROM orders; -- 结果:123456789.01000001
-- 正确:使用DECIMAL
SELECT SUM(amount) FROM orders; -- 结果:123456789.01
解决方案:金额字段必须使用DECIMAL类型。
5.3 时区问题
问题:时间显示不一致
-- MySQL
SELECT created_at FROM orders;
-- 结果:2023-01-01 10:00:00
-- KaiwuDB
SELECT created_at FROM orders;
-- 结果:2023-01-01 02:00:00+00
解决方案:
-- 设置时区
SET TIME ZONE 'Asia/Shanghai';
-- 或者使用TIMESTAMPTZ
ALTER TABLE orders ALTER COLUMN created_at TYPE TIMESTAMPTZ;
六、最佳实践
6.1 迁移前检查
-- 检查MySQL表结构
SHOW CREATE TABLE orders;
-- 检查字段类型和长度
SELECT
column_name,
data_type,
character_maximum_length,
numeric_precision,
numeric_scale
FROM information_schema.columns
WHERE table_name = 'orders';
-- 检查数据范围
SELECT
MIN(id) as min_id,
MAX(id) as max_id,
MAX(LENGTH(order_no)) as max_order_no_length
FROM orders;
6.2 迁移后验证
-- 检查KaiwuDB表结构
SHOW CREATE TABLE orders;
-- 检查数据类型
SELECT
column_name,
data_type,
character_maximum_length
FROM information_schema.columns
WHERE table_name = 'orders';
-- 抽样验证
SELECT * FROM orders WHERE id = 10086;
6.3 类型映射检查清单
- 整数类型:检查UNSIGNED,BIGINT UNSIGNED映射为NUMERIC(20)
- 浮点类型:金额字段使用DECIMAL,避免使用FLOAT/DOUBLE
- 字符串类型:检查长度,VARCHAR(N)直接映射
- 日期时间:检查时区,DATETIME映射为TIMESTAMP
- JSON类型:检查长度,过大时映射为TEXT
- 枚举类型:使用VARCHAR + CHECK约束
- 自增主键:使用序列或应用层生成
七、总结
数据类型映射是数据库迁移的基础,也是最容易出问题的环节。通过本文的介绍,你应该能够:
- 理解类型差异:MySQL和KaiwuDB的类型对应关系
- 避免常见陷阱:BIGINT UNSIGNED、DECIMAL精度、时区等
- 使用映射工具:手动映射、KDTS自动映射、脚本映射
- 验证迁移结果:检查表结构、抽样验证、性能测试
关键要点:
- BIGINT UNSIGNED必须映射为NUMERIC(20)
- 金额字段必须使用DECIMAL,避免使用FLOAT/DOUBLE
- DATETIME注意时区问题,使用TIMESTAMPTZ保留时区
- JSON字段过长时映射为TEXT
- ENUM类型使用VARCHAR + CHECK约束
如果你正在考虑数据库国产化迁移,建议先做好类型映射的规划,避免迁移过程中出现数据问题。
欢迎 👍点赞✍评论⭐收藏,欢迎指正




