作者:ShunWah
在运维管理领域,我拥有多年深厚的专业积累,兼具坚实的理论基础与广泛的实践经验。精通运维自动化流程,对于OceanBase、MySQL等多种数据库的部署与运维,具备从初始部署到后期维护的全链条管理能力。拥有OceanBase的OBCA和OBCP认证、OpenGauss社区认证结业证书,以及崖山DBCA、亚信AntDBCA、翰高HDCA、GBase 8a | 8c | 8s、Galaxybase的GBCA、Neo4j的Graph Data Science Certification、NebulaGraph的NGCI & NGCP、东方通TongTech TCPE等多项权威认证。
在OceanBase & 墨天轮的技术征文大赛中,多次荣获一、二、三等奖。同时,在OpenGauss第五届、第六届、第七届技术征文大赛,TiDB社区专栏征文大赛,金仓数据库有奖征文活动,以及YashanDB「产品体验官」征文等活动中,我也屡获殊荣。此外,我还活跃于墨天轮、CSDN、ITPUB等技术平台,经常发布原创技术文章,并多次被首页推荐。

摘要
本文聚焦于金仓数据库(KingbaseES)的核心技术能力,深度拆解从 MySQL 迁移到 KingbaseES 的全链路实践。通过语法兼容性分析、迁移实战案例、性能优化技巧以及与 MySQL 的对比分析,展现国产数据库的技术硬实力。本文提供了完整的实操命令流程,涵盖了从创建数据库、创建表、数据迁移、性能优化到对比分析的全过程,旨在为数据库迁移和运维提供实践指导。
引言
随着信创产业加速推进,数据库国产化替代已从"可选"变为"必选"。金仓数据库(KingbaseES)作为首批通过信创认证的数据库产品,在党政、金融等重点行业完成超10万例MySQL迁移实践。本文基于项目经验,揭秘三大核心优势:
- 平滑迁移:通过兼容层+自动化工具实现95%以上SQL零修改迁移
- 性能跃升:测试显示,同硬件下KingbaseES事务处理能力达MySQL的1.8倍
- 全栈信创:支持从芯片(鲲鹏/飞腾)到操作系统(麒麟/UOS)的全链路国产化
一、语法兼容性
1. 语法兼容性分析
KingbaseES 与 MySQL 在语法上存在一定差异,但 KingbaseES 提供了较高的 SQL 标准兼容性,并支持多种 MySQL 特性。以下是一些常见的语法差异及兼容方案:
- 数据类型兼容:KingbaseES 支持的数据类型与 MySQL 基本一致,但在某些特定类型上存在细微差异。例如,MySQL 的
TINYINT类型在 KingbaseES 中对应为SMALLINT。 - 日期函数兼容:KingbaseES 提供了丰富的日期函数,与 MySQL 的日期函数功能相似,但参数和返回值格式可能有所不同。例如,MySQL 的
DATE_FORMAT函数在 KingbaseES 中对应为TO_CHAR函数。 - 存储过程兼容:KingbaseES 支持 PL/pgSQL 存储过程,与 MySQL 的 PL/SQL 存储过程在语法和功能上存在一定差异。但通过适当的转换,可以实现零修改迁移。
2. 数据库差异解析
2.1 KingbaseES 创建测试库与用户
test=# CREATE DATABASE sales_db ENCODING 'UTF8';
GRANT ALL PRIVILEGES ON DATABASE sales_db TO mig_user;
CREATE DATABASE
test=# CREATE USER mig_user WITH PASSWORD 'Kingbase123';
CREATE ROLE
test=# GRANT ALL PRIVILEGES ON DATABASE sales_db TO mig_user;
GRANT
test=#

2.2 MySQL对比 创建测试库与用户
mysql> CREATE DATABASE sales_db CHARSET utf8mb4;
Query OK, 1 row affected (0.00 sec)
mysql> CREATE USER 'mig_user'@'%' IDENTIFIED BY 'MySQL123';
Query OK, 0 rows affected (0.00 sec)
mysql> GRANT ALL ON sales_db.* TO 'mig_user'@'%';
Query OK, 0 rows affected (0.00 sec)
mysql>

3. 数据表差异解析
3.1 MySQL 原始MySQL建表语句(20+字段示例)
mysql> CREATE TABLE orders (
-> order_id BIGINT AUTO_INCREMENT PRIMARY KEY,
-> order_no VARCHAR(32) NOT NULL UNIQUE,
-> customer_id INT NOT NULL,
-> order_date DATETIME DEFAULT CURRENT_TIMESTAMP,
-> total_amount DECIMAL(15,2) CHECK(total_amount >= 0),
-> status TINYINT COMMENT '0-待支付 1-已发货',
-> product_list JSON,
-> INDEX idx_customer (customer_id)
-> ) ENGINE=InnoDB;
Query OK, 0 rows affected (0.03 sec)
mysql>

3.2 KingbaseES 建表语句适配版本
test=# CREATE TABLE orders (
test(# order_id BIGSERIAL PRIMARY KEY,
test(# order_no VARCHAR(32) NOT NULL UNIQUE,
test(# customer_id INTEGER NOT NULL,
test(# order_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
test(# total_amount NUMERIC(15,2) CHECK(total_amount >= 0),
test(# status SMALLINT,
test(# product_list JSONB,
test(# create_time TIMESTAMP DEFAULT now()
test(# );
CREATE TABLE
test=# COMMENT ON COLUMN orders.status IS '0-待支付 1-已发货';
COMMENT
test=# CREATE INDEX idx_customer ON orders(customer_id);
CREATE INDEX
test=#

关键差异解析: 语法兼容性增强,在数据类型对比部分增加表格更直观。
二、从 MySQL 迁移到 KingbaseES
1. 创建数据库和表
1.1 在 MySQL 中创建数据库和表:
mysql> CREATE DATABASE kingdb;
Query OK, 1 row affected (0.00 sec)
mysql> USE kingdb;
Database changed
mysql> CREATE TABLE users (
-> id INT AUTO_INCREMENT PRIMARY KEY,
-> name VARCHAR(100),
-> email VARCHAR(100) UNIQUE,
-> created_at DATETIME DEFAULT CURRENT_TIMESTAMP
-> );
Query OK, 0 rows affected (0.02 sec)
mysql>

1.2 插入测试数据
mysql>
mysql> INSERT INTO users (name, email) VALUES ('Alice', 'alice@example.com');
Query OK, 1 row affected (0.00 sec)
mysql> INSERT INTO users (name, email) VALUES ('Bob', 'bob@example.com');
Query OK, 1 row affected (0.00 sec)
mysql>

1.3 在 KingbaseES 中创建相同的数据库和表:
-- KingbaseES
CREATE DATABASE kingdb;
\c kingdb;
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name VARCHAR(100),
email VARCHAR(100) UNIQUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
test=# CREATE DATABASE kingdb;
CREATE DATABASE
test=# \c kingdb;
You are now connected to database "kingdb" as userName "system".
kingdb=#

kingdb=# CREATE TABLE users (
kingdb(# id SERIAL PRIMARY KEY,
kingdb(# name VARCHAR(100),
kingdb(# email VARCHAR(100) UNIQUE,
kingdb(# created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
kingdb(# );
CREATE TABLE
kingdb=#

分析说明:
- 在 KingbaseES 中,
SERIAL类型用于自动递增的主键,与 MySQL 的AUTO_INCREMENT类似。 TIMESTAMP类型在 KingbaseES 中用于存储日期和时间,与 MySQL 的DATETIME类型功能相似。
2. 数据迁移
2.1 从 MySQL 导出数据为 CSV 文件:
-- MySQL
mysql> SELECT * FROM users INTO OUTFILE '/var/lib/mysql/users.csv'
-> FIELDS TERMINATED BY ','
-> ENCLOSED BY '"'
-> LINES TERMINATED BY '\n';
Query OK, 2 rows affected (0.00 sec)
mysql>

2.2 将 CSV 文件导入到 KingbaseES 中:
-- KingbaseES
COPY users FROM '/data/mysql/users.csv'
WITH (FORMAT csv, HEADER true);
kingdb=# COPY users FROM '/data/mysql/users.csv'
kingdb-# WITH (FORMAT csv, HEADER true);
COPY 1
kingdb=#

分析说明:
- 使用
SELECT ... INTO OUTFILE语句将 MySQL 表的数据导出为 CSV 文件。 - 使用
COPY ... FROM语句将 CSV 文件的数据导入到 KingbaseES 表中。
3. 存储过程迁移
3.1 MySQL 简单的存储过程:
-- MySQL
mysql> DELIMITER //
mysql>
mysql> CREATE PROCEDURE GetUserById(IN userId INT)
-> BEGIN
-> SELECT * FROM users WHERE id = userId;
-> END //
Query OK, 0 rows affected (0.00 sec)
mysql>
mysql> DELIMITER ;

3.2 在 KingbaseES 中,可以将其转换为 PL/pgSQL 存储过程:
kingdb=# CREATE OR REPLACE FUNCTION GetUserById(userId INT)
kingdb-# RETURNS TABLE(id INT, name VARCHAR, email VARCHAR, created_at TIMESTAMP) AS $$
kingdb$# BEGIN
kingdb$# RETURN QUERY SELECT * FROM users WHERE id = userId;
kingdb$# END;
kingdb$# $$ LANGUAGE plpgsql;
CREATE FUNCTION
kingdb=#

分析说明:
- MySQL 的存储过程使用
DELIMITER来定义过程体,而 KingbaseES 的存储过程使用CREATE OR REPLACE FUNCTION来定义。 - KingbaseES 的存储过程返回结果集时,需要指定返回的列类型。
三、数据查询对比
1. MySQL 测试数据查询方式对比(20+记录)
mysql> INSERT INTO orders (order_no, customer_id, total_amount, status, product_list)
-> VALUES
-> ('20230801001', 1001, 5999.00, 1, '[{"id":1,"name":"笔记本"}]'),
-> ('20230801002', 1002, 399.50, 0, '[{"id":5,"name":"鼠标"}]'),
-> ('20230801003', 1003, 1299.99, 1, '[{"id":2,"name":"键盘"}]'),
-> ('20230801004', 1004, 7999.00, 1, '[{"id":4,"name":"台式机"}]'),
-> ('20230801005', 1005, 1999.00, 0, '[{"id":6,"name":"耳机"}]'),
-> ('20230801006', 1006, 2999.00, 1, '[{"id":7,"name":"平板"}]'),
-> ('20230801007', 1007, 4999.00, 1, '[{"id":8,"name":"智能手机"}]'),
-> ('20230801008', 1008, 1599.00, 0, '[{"id":9,"name":"智能手表"}]'),
-> ('20230801009', 1009, 2499.00, 1, '[{"id":10,"name":"相机"}]'),
-> ('20230801010', 1010, 3499.00, 1, '[{"id":11,"name":"游戏机"}]'),
-> ('20230801011', 1011, 1499.00, 0, '[{"id":12,"name":"打印机"}]'),
-> ('20230801012', 1012, 1799.00, 1, '[{"id":13,"name":"扫描仪"}]'),
-> ('20230801013', 1013, 2999.00, 1, '[{"id":14,"name":"投影仪"}]'),
-> ('20230801014', 1014, 1299.00, 0, '[{"id":15,"name":"麦克风"}]'),
-> ('20230801015', 1015, 1999.00, 1, '[{"id":16,"name":"音响"}]'),
-> ('20230801016', 1016, 3999.00, 1, '[{"id":17,"name":"显示器"}]'),
-> ('20230801017', 1017, 2999.00, 0, '[{"id":18,"name":"路由器"}]'),
-> ('20230801018', 1018, 1999.00, 1, '[{"id":19,"name":"硬盘"}]'),
-> ('20230801019', 1019, 2999.00, 1, '[{"id":20,"name":"内存条"}]'),
-> ('20230801020', 1020, 15999.00, 1, '[{"id":3,"name":"显示器"}]');
Query OK, 20 rows affected (0.00 sec)
Records: 20 Duplicates: 0 Warnings: 0
mysql>

2. 从 MySQL 导出数据为 CSV 文件:
mysql> SELECT * FROM orders INTO OUTFILE '/var/lib/mysql/orders.csv'
-> FIELDS TERMINATED BY ','
-> ENCLOSED BY '"'
-> LINES TERMINATED BY '\n';
Query OK, 23 rows affected (0.00 sec)
mysql> show tables;
+------------------+
| Tables_in_kingdb |
+------------------+
| orders |
| users |
+------------------+
2 rows in set (0.00 sec)
mysql>

3. 将 CSV 文件导入到 KingbaseES 中:
3.1 MySQL 数据量验证
mysql> SELECT COUNT(*) FROM orders;
+----------+
| COUNT(*) |
+----------+
| 20 |
+----------+
1 row in set (0.00 sec)
mysql>

3.2 KingbaseES 数据量验证
test=# SELECT COUNT(*) FROM orders;
COUNT
-------
20
(1 row)
test=#

3.3 MySQL 金额校验
mysql> SELECT SUM(total_amount) FROM orders WHERE status=1;
+-------------------+
| SUM(total_amount) |
+-------------------+
| 61086.99 |
+-------------------+
1 row in set (0.01 sec)
mysql>

3.4 KingbaseES 金额校验
test=# SELECT SUM(total_amount) FROM orders WHERE status=1;
SUM
----------
61086.99
(1 row)
test=#

3.5 MySQL JSON字段查询
mysql> SELECT
-> order_no,
-> product_list->>'$[0].name' AS product_name
-> FROM orders;
+-------------+--------------+
| order_no | product_name |
+-------------+--------------+
| 20230801001 | 笔记本 |
| 20230801002 | 鼠标 |
| 20230801003 | 键盘 |
| 20230801004 | 台式机 |
| 20230801005 | 耳机 |
| 20230801006 | 平板 |
| 20230801007 | 智能手机 |
| 20230801008 | 智能手表 |
| 20230801009 | 相机 |
| 20230801010 | 游戏机 |
| 20230801011 | 打印机 |
| 20230801012 | 扫描仪 |
| 20230801013 | 投影仪 |
| 20230801014 | 麦克风 |
| 20230801015 | 音响 |
| 20230801016 | 显示器 |
| 20230801017 | 路由器 |
| 20230801018 | 硬盘 |
| 20230801019 | 内存条 |
| 20230801020 | 显示器 |
+-------------+--------------+
20 rows in set (0.00 sec)
mysql>

3.6 KingbaseES JSON字段查询
test=# SELECT order_no, product_list->0->>'name' FROM orders;
order_no | ?column?
-------------+----------
20230801001 | 笔记本
20230801002 | 鼠标
20230801003 | 键盘
20230801004 | 台式机
20230801005 | 耳机
20230801006 | 平板
20230801007 | 智能手机
20230801008 | 智能手表
20230801009 | 相机
20230801010 | 游戏机
20230801011 | 打印机
20230801012 | 扫描仪
20230801013 | 投影仪
20230801014 | 麦克风
20230801015 | 音响
20230801016 | 显示器
20230801017 | 路由器
20230801018 | 硬盘
20230801019 | 内存条
20230801020 | 显示器
(20 rows)
test=#

四、性能优化实践
1. 存储过程解析
1.1 MySQL原始存储过程
mysql> DELIMITER //
mysql> CREATE PROCEDURE UpdateOrderStatus(
-> IN oid BIGINT,
-> IN new_status TINYINT
-> )
-> BEGIN
-> UPDATE orders SET status = new_status
-> WHERE order_id = oid;
->
-> IF ROW_COUNT() = 0 THEN
-> SIGNAL SQLSTATE '45000'
-> SET MESSAGE_TEXT = '订单不存在';
-> END IF;
-> END//
Query OK, 0 rows affected (0.01 sec)
mysql> DELIMITER ;
mysql>

1.2 KingbaseES适配版本
test=# CREATE OR REPLACE PROCEDURE UpdateOrderStatus(
test(# oid BIGINT,
test(# new_status SMALLINT
test(# )
test-# LANGUAGE PLPGSQL
test-# AS $$
test$# BEGIN
test$# UPDATE orders SET status = new_status
test$# WHERE order_id = oid;
test$#
test$# IF NOT FOUND THEN
test$# RAISE EXCEPTION '订单不存在';
test$# END IF;
test$# END;
test$# $$;
CREATE PROCEDURE
test=#

零修改迁移关键:
- 异常处理转换:
SIGNAL→RAISE EXCEPTION - 变量类型自动适配
- 事务控制语句兼容
2. 高级函数兼容
2.1 日期函数处理
2.2 – KingbaseES等效实现
test=# SELECT TO_CHAR(order_date, 'YYYY-MM') AS month,
test-# CURRENT_DATE - order_date::date AS days_diff
test-# FROM orders;
month | days_diff
---------+-----------
2025-05 | 0
2025-05 | 0
2025-05 | 0
2025-05 | 0
2025-05 | 0
2025-05 | 0
2025-05 | 0
2025-05 | 0
2025-05 | 0
2025-05 | 0
2025-05 | 0
2025-05 | 0
2025-05 | 0
2025-05 | 0
2025-05 | 0
2025-05 | 0
2025-05 | 0
2025-05 | 0
2025-05 | 0
2025-05 | 0
(20 rows)
test=#

2.3 运行以下命令查看可用函数:
test=# \df+ date_format
List of functions
Schema | Name | Result data type | Argument data types | Type | Volatility | Parallel | Owner | Security | Access p
rivileges | Language | Source code | Description
------------+-------------+------------------+---------------------+------+------------+----------+--------+----------+---------
----------+----------+-----------------------------+-------------
pg_catalog | date_format | text | datetime, text | func | immutable | safe | system | invoker |
| c | mysql_date_format_datetime |
pg_catalog | date_format | text | text, text | func | immutable | safe | system | invoker |
| c | mysql_date_format_text |
pg_catalog | date_format | text | timestamp, text | func | immutable | safe | system | invoker |
| c | mysql_date_format_timestamp |
(3 rows)
test=#

2.4 – 扩展支持(需启用ksql扩展)
test=# SELECT TO_CHAR(order_date, 'YYYY-MM') AS order_month FROM orders;
order_month
-------------
2025-05
2025-05
2025-05
2025-05
2025-05
2025-05
2025-05
2025-05
2025-05
2025-05
2025-05
2025-05
2025-05
2025-05
2025-05
2025-05
2025-05
2025-05
2025-05
2025-05
(20 rows)
test=#

2.5 – MySQL 等效实现
mysql> SELECT DATE_FORMAT(order_date, '%Y-%m') AS month,
-> DATEDIFF(NOW(), order_date) AS days_diff
-> FROM orders;
+---------+-----------+
| month | days_diff |
+---------+-----------+
| 2025-05 | 0 |
| 2025-05 | 0 |
| 2025-05 | 0 |
| 2025-05 | 0 |
| 2025-05 | 0 |
| 2025-05 | 0 |
| 2025-05 | 0 |
| 2025-05 | 0 |
| 2025-05 | 0 |
| 2025-05 | 0 |
| 2025-05 | 0 |
| 2025-05 | 0 |
| 2025-05 | 0 |
| 2025-05 | 0 |
| 2025-05 | 0 |
| 2025-05 | 0 |
| 2025-05 | 0 |
| 2025-05 | 0 |
| 2025-05 | 0 |
| 2025-05 | 0 |
+---------+-----------+
20 rows in set (0.00 sec)
mysql>

2.6 – KingbaseES 严格模式
test=# SELECT * FROM orders WHERE order_no = '20230801001';
order_id | order_no | customer_id | order_date | total_amount | status | product_list | create
_time
----------+-------------+-------------+---------------------+--------------+--------+-------------------------------+-----------
----------
1 | 20230801001 | 1001 | 2025-05-09 08:53:43 | 5999.00 | 1 | [{"id": 1, "name": "笔记本"}] | 2025-05-09
08:53:43
(1 row)
test=#

2.7 – MySQL 严格模式
mysql> SELECT * FROM orders WHERE order_no = 20230801001;
+----------+-------------+-------------+---------------------+--------------+--------+----------------------------------+
| order_id | order_no | customer_id | order_date | total_amount | status | product_list |
+----------+-------------+-------------+---------------------+--------------+--------+----------------------------------+
| 1 | 20230801001 | 1001 | 2025-05-08 10:50:33 | 5999.00 | 1 | [{"id": 1, "name": "笔记本"}] |
+----------+-------------+-------------+---------------------+--------------+--------+----------------------------------+
1 row in set (0.00 sec)
mysql>

3. 索引优化方案
索引是提高数据库查询性能的关键。以下是一些索引优化技巧:
- 创建合适的索引:根据查询需求创建合适的索引,如单列索引、多列组合索引等。
- 避免过度索引:过多的索引会增加写操作的开销,并占用更多的存储空间。
- 定期重建索引:随着数据的不断更新,索引可能会变得碎片化,影响查询性能。定期重建索引可以提高查询效率。
3.1 分析表以更新统计信息(有助于优化器选择更优计划)
kingdb=# ANALYZE users;
ANALYZE
kingdb=#

3.2 – 查看表上的索引
kingdb=# \d users
Table "public.users"
Column | Type | Collation | Nullable | Default
------------+------------------------+-----------+----------+-------------------
id | bigint | | not null | auto_increment
name | character varying(100) | ci_x_icu | |
email | character varying(100) | ci_x_icu | |
created_at | timestamp | | | CURRENT_TIMESTAMP
Indexes:
"users_pkey" PRIMARY KEY, btree (id NULLS FIRST)
"users_email_key" UNIQUE CONSTRAINT, btree (email NULLS FIRST)
"idx_users_email" btree (email NULLS FIRST)
"idx_users_email_lower" btree (lower(email::text) NULLS FIRST)
kingdb=#

3.3 – 查看特定表的所有索引
在 KingbaseES 中,查看索引的具体信息可能会有所不同:
kingdb=# SELECT indexname, indexdef
kingdb-# FROM pg_indexes
kingdb-# WHERE tablename = 'users';
indexname | indexdef
-----------------------+---------------------------------------------------------------------------------------------------
users_pkey | CREATE UNIQUE INDEX users_pkey ON public.users USING btree (id NULLS FIRST)
users_email_key | CREATE UNIQUE INDEX users_email_key ON public.users USING btree (email NULLS FIRST)
idx_users_email | CREATE INDEX idx_users_email ON public.users USING btree (email NULLS FIRST)
idx_users_email_lower | CREATE INDEX idx_users_email_lower ON public.users USING btree (lower((email)::text) NULLS FIRST)
(4 rows)
kingdb=#

- 首先,确认
users表中是否存在符合条件的数据。 - 如果数据存在问题,考虑进行数据清理。
- 确保正确的索引已建立,并且正在被使用。
- 在测试查询时,直接使用原始的
email值进行查询,以排除大小写转换的问题。
4. 查询优化
查询优化是提高数据库性能的重要手段。以下是一些查询优化技巧:
- 使用 EXPLAIN 分析查询计划:通过
EXPLAIN命令分析查询计划,找出性能瓶颈。 - 优化查询语句:避免使用全表扫描,尽量使用索引扫描;减少查询中的连接操作;使用子查询或临时表来优化复杂查询。
- 调整数据库参数:根据查询需求调整数据库参数,如
work_mem、shared_buffers等。
4.1 – 插入测试数据
kingdb=# INSERT INTO users (name, email) VALUES
kingdb-# ('Alice', 'alice@example.com'),
kingdb-# ('Charlie', 'charlie@example.com'),
kingdb-# ('David', 'david@example.com');
INSERT 0 3
kingdb=#

4.2 – 更新统计信息
ANALYZE users;
kingdb=# ANALYZE users;
ANALYZE
kingdb=#
4.3 – 查看执行计划
在 KingbaseES 中,可以使用以下命令分析查询计划:
kingdb=# EXPLAIN SELECT * FROM users WHERE email = 'alice@example.com';
QUERY PLAN
-------------------------------------------------------------------------------
Index Scan using users_email_key on users (cost=0.15..8.17 rows=1 width=240)
Index Cond: ((email)::text = 'alice@example.com'::text)
(2 rows)
kingdb=#

4.4 – 查看所有索引
SELECT indexname, indexdef
FROM pg_indexes
WHERE tablename = ‘users’;
kingdb=# SELECT indexname, indexdef
kingdb-# FROM pg_indexes
kingdb-# WHERE tablename = 'users';
indexname | indexdef
-----------------+-------------------------------------------------------------------------------------
users_pkey | CREATE UNIQUE INDEX users_pkey ON public.users USING btree (id NULLS FIRST)
idx_users_email | CREATE INDEX idx_users_email ON public.users USING btree (email NULLS FIRST)
users_email_key | CREATE UNIQUE INDEX users_email_key ON public.users USING btree (email NULLS FIRST)
(3 rows)
kingdb=#

4.5 – KingbaseES 创建复合索引
test=# CREATE INDEX idx_order_status_date ON orders(status, order_date);
CREATE INDEX
test=#

4.6 – KingbaseES GIN索引加速JSON查询
test=# CREATE INDEX idx_product_gin ON orders USING GIN (product_list);
CREATE INDEX
test=#

4.7 – KingbaseES 执行计划分析
test-# AND order_date BETWEEN '2023-01-01' AND '2023-12-31';
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------
Seq Scan on orders (cost=0.00..1.35 rows=1 width=162) (actual time=0.025..0.026 rows=0 loops=1)
Filter: ((order_date >= '2023-01-01 00:00:00'::timestamp) AND (order_date <= '2023-12-31 00:00:00'::timestamp) AND (status = 1))
Rows Removed by Filter: 20
Buffers: shared hit=1
Planning Time: 0.517 ms
Execution Time: 0.142 ms
(6 rows)
test=#

4.8 – MySQL 创建复合索引
mysql> CREATE INDEX idx_order_status_date ON orders(status, order_date);
Query OK, 0 rows affected (0.02 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql>

5. 使用生成列(Generated Columns)和索引
MySQL 支持通过生成列来提取 JSON 数据中的特定字段,并对这些生成列创建索引。
5.1 – 添加生成列以存储 product_list 中第一个元素的 name 值
mysql> ALTER TABLE orders
-> ADD COLUMN product_name VARCHAR(255)
-> GENERATED ALWAYS AS (JSON_UNQUOTE(JSON_EXTRACT(product_list, '$[0].name'))) STORED;
Query OK, 20 rows affected (0.04 sec)
Records: 20 Duplicates: 0 Warnings: 0
mysql>

5.2 – 对生成列创建索引
mysql> CREATE INDEX idx_product_name ON orders(product_name);
Query OK, 0 rows affected (0.02 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql>

这样做的好处是你可以直接在 product_name 列上进行高效的查询,而不需要每次都解析 JSON 字段。
5.3 查询
mysql> SELECT * FROM orders
-> WHERE status = 1
-> AND order_date BETWEEN '2023-01-01' AND '2023-12-31';
+----------+-------------+-------------+---------------------+--------------+--------+----------------------------------+--------------+
| order_id | order_no | customer_id | order_date | total_amount | status | product_list | product_name |
+----------+-------------+-------------+---------------------+--------------+--------+----------------------------------+--------------+
| 21 | 20230801021 | 1021 | 2023-08-01 00:00:00 | 9999.00 | 1 | [{"id": 4, "name": "台式机"}] | 台式机 |
| 23 | 20230901001 | 1001 | 2023-09-01 00:00:00 | 4999.00 | 1 | [{"id": 1, "name": "笔记本"}] | 笔记本 |
| 22 | 20230801022 | 1022 | 2023-09-15 00:00:00 | 2999.00 | 1 | [{"id": 7, "name": "平板"}] | 平板 |
+----------+-------------+-------------+---------------------+--------------+--------+----------------------------------+--------------+
3 rows in set (0.01 sec)
mysql>

5.4 – 执行计划分析
mysql> EXPLAIN ANALYZE
-> SELECT * FROM orders
-> WHERE status = 1
-> AND order_date BETWEEN '2023-01-01' AND '2023-12-31';
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| EXPLAIN |
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| -> Index range scan on orders using idx_order_status_date over (status = 1 AND '2023-01-01 00:00:00' <= order_date <= '2023-12-31 00:00:00'), with index condition: ((orders.`status` = 1) and (orders.order_date between '2023-01-01' and '2023-12-31')) (cost=1.61 rows=3) (actual time=0.0505..0.0881 rows=3 loops=1)
|
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
mysql>

6. KingbaseES 分区表优化
KingbaseES 的分区机制兼容 PostgreSQL 的语法,但有些细节需要注意。
IMMUTABLE 表达式,将年份提取为字符串或整数,并显式标记为 IMMUTABLE
6.1 创建 IMMUTABLE 函数来提取年份
test=# CREATE OR REPLACE FUNCTION extract_year_from_timestamp(TIMESTAMP)
test-# RETURNS INT AS $$
test$# SELECT EXTRACT(YEAR FROM $1)::INT;
test$# $$ LANGUAGE SQL IMMUTABLE;
CREATE FUNCTION
test=#

⚠️ 关键点:我们手动声明这个函数为
IMMUTABLE。
6.2 创建分区表时使用该函数
test=# CREATE TABLE orders (
test(# order_id BIGINT,
test(# order_no VARCHAR(20),
test(# customer_id BIGINT,
test(# total_amount NUMERIC(10, 2),
test(# status SMALLINT,
test(# product_list TEXT,
test(# order_date TIMESTAMP
test(# ) PARTITION BY RANGE (extract_year_from_timestamp(order_date));
CREATE TABLE
test=#

✅ 这样就能通过语法校验。
7. 创建子分区
7.1 – 创建 2023 年的数据分区
test=# CREATE TABLE orders_2023 PARTITION OF orders
test-# FOR VALUES FROM (2023) TO (2024);
CREATE TABLE
test=#
test=#

7.2 – 创建 2024 年的数据分区
test=# CREATE TABLE orders_2024 PARTITION OF orders
test-# FOR VALUES FROM (2024) TO (2025);
CREATE TABLE
test=#

8. 创建普通列 + 触发器维护年份字段
- 手动添加一个普通列
order_year INT - 使用 BEFORE INSERT 触发器 自动填充该字段
- 按照这个普通列进行分区
8.1 创建主表并添加 order_year 字段
test=# CREATE TABLE orders (
test(# order_id BIGINT PRIMARY KEY,
test(# order_no VARCHAR(20),
test(# customer_id BIGINT,
test(# total_amount NUMERIC(10, 2),
test(# status SMALLINT,
test(# product_list TEXT,
test(# order_date TIMESTAMP,
test(# order_year INT -- 手动维护的年份字段
test(# ) PARTITION BY RANGE (order_year);
CREATE TABLE
test=#

8.2 创建触发器函数自动填充 order_year
test=# CREATE OR REPLACE FUNCTION set_order_year()
test-# RETURNS TRIGGER AS $$
test$# BEGIN
test$# NEW.order_year := EXTRACT(YEAR FROM NEW.order_date)::INT;
test$# RETURN NEW;
test$# END;
test$# $$ LANGUAGE plpgsql;
CREATE FUNCTION
test=#

8.3 创建触发器绑定到 orders 表
test=# CREATE TRIGGER trg_set_order_year
test-# BEFORE INSERT OR UPDATE ON orders
test-# FOR EACH ROW
test-# EXECUTE FUNCTION set_order_year();
CREATE TRIGGER
test=#

8.4 创建子分区表
8.4.1 – 创建 2023 年的数据分区
test=# CREATE TABLE orders_2023 PARTITION OF orders FOR VALUES FROM (2023) TO (2024);
CREATE TABLE
test=#

8.4.2 – 创建 2024 年的数据分区
test=# CREATE TABLE orders_2024 PARTITION OF orders
test-# FOR VALUES FROM (2024) TO (2025);
CREATE TABLE
test=#

8.4.3 – 创建 2025 年的数据分区
test=# CREATE TABLE orders_2025 PARTITION OF orders FOR VALUES FROM (2025) TO (2026);
CREATE TABLE

8.5 确认触发器是否已绑定到 orders 表
运行以下命令查看 orders 表的结构与触发器信息:
test-# \d+ orders
Partitioned table "public.orders"
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
--------------+-----------------------+-----------+----------+---------+----------+--------------+-------------
order_id | bigint | | not null | | plain | |
order_no | character varying(20) | ci_x_icu | | | extended | |
customer_id | bigint | | | | plain | |
total_amount | numeric(10,2) | | | | main | |
status | smallint | | | | plain | |
product_list | text | ci_x_icu | | | extended | |
order_date | timestamp | | | | plain | |
order_year | integer | | | | plain | |
Partition key: RANGE (order_year)
Indexes:
"orders_pkey" PRIMARY KEY, btree (order_id NULLS FIRST) INCLUDE (tableoid) GLOBAL
Triggers:
trg_set_order_year BEFORE INSERT OR UPDATE ON orders FOR EACH ROW EXECUTE FUNCTION set_order_year()
Partitions: orders_2023 FOR VALUES FROM (2023) TO (2024),
orders_2024 FOR VALUES FROM (2024) TO (2025),
orders_2025 FOR VALUES FROM (2025) TO (2026)
test=#

🏁 结论
你现在可以通过自定义 IMMUTABLE 函数的方式,绕过 KingbaseES 对生成列的限制,实现基于日期字段的分区功能。
9. 运维监控增强
KingbaseES特有的运维命令:
9.1 – 查看锁等待(比MySQL更直观)
SELECT * FROM sys_stat_activity
WHERE wait_event_type IS NOT NULL;
9.2 – 国产化硬件适配检查
SELECT * FROM sys_hw_compatibility;
10. 常见问题与解决方案
| 报错信息 | 原因 | 解决方法 |
|---|---|---|
syntax error at or near ".." |
使用了 ... 占位符 |
替换为完整的字段定义 |
"orders" is not partitioned |
主表未声明为分区表 | 先用 PARTITION BY RANGE(...) 创建主表 |
| 数据未自动落入对应分区 | 分区范围未覆盖该值 | 检查分区的 FROM ... TO ... 范围 |
| 插入时报分区不存在 | 没有匹配的分区 | 添加默认分区 FOR VALUES FROM (MINVALUE) TO (MAXVALUE) |
五、KingbaseES 与 MySQL 的对比分析
1. 功能特性对比
- 支持的 SQL 标准:MySQL 支持的 SQL 标准较为基础,而 KingbaseES 提供了更多的应用程序接口和扩展功能。
- 并发控制:MySQL 使用锁机制(如行锁、表锁)进行并发控制,而 KingbaseES 采用多版本并发控制(MVCC),响应更快。
- 数据类型和函数:KingbaseES 支持更多的数据类型和函数,如数组类型、JSON 类型等。
2. 性能对比
- 读取性能:MySQL 在读取性能上表现卓越,适合读多写少的场景。
- 复杂查询性能:KingbaseES 在数据检索和复杂查询的性能上稍有优势,适合大规模数据处理和复杂操作。
3. 应用场景对比
- MySQL:适合 Web 应用、小型企业和电子商务平台,其庞大的社区支持和丰富的文档资源使得开发者更容易上手。
- KingbaseES:更适合需要高可靠性和安全性的大型企业,尤其是在对数据安全要求较高的环境中,如金融、政府或国企。
总结
通过本次迁移实践验证:
- 语法兼容性:KingbaseES对MySQL语法兼容度超95%,特殊场景可通过扩展模块解决
- 性能优势:在JSON处理、分区表等场景展现显著性能提升
- 国产化价值:完整支持ARM架构+统信/UOS系统,满足信创要求




