在这篇文章中,我们将概述如何将 IBM Db2 LUW 数据库迁移到 或 AmazonRelational Database Service (Amazon RDS) forPostgreSQL。讨论了架构转换过程中可能遇到的挑战,以及如何通过原生的 和
命令执行数据迁移。此外,还会探讨如何自动化数据迁移和进行 和数据验证。
本文重点涵盖了 Db2 LUW 迁移的两个主要里程碑:
在开始数据库迁移之前,我们建议您完成一项预迁移阶段,以准备您的数据库。有关更多信息,请参考 。总体而言,您应执行以下操作:
现在,让我们开始吧!
在开始迁移之前,强烈建议您评估一下数据库迁移所需的工作和可行性。您可以使用 AWS SCT 生成 ,该报告提供有关架构转换的详细行动项目。您可以利用此报告根据复杂性估算架构转换的工作量。有关下载和安装 AWS SCT的信息,请参阅 。
在以下各节中,我们将介绍在从 Db2 LUW 到 PostgreSQL 迁移过程中遇到的一些常见场景。
分区表是一种数据组织方式,根据一个或多个表属性(称为 分区键 )的值将表数据划分到多个存储对象(称为数据分区)中。Db2 LUW 和 PostgreSQL 都支持表分区,但存在一些差异。
首先,Db2 LUW 数据库中的范围分区表有 INCLUSIVE 和 EXCLUSIVE 子句来设置边界值,而 PostgreSQL 从 v15开始仅支持起始边界的 INCLUSIVE 和结束边界的 EXCLUSIVE。
下面是一个示例,表 DIM_SPORTING_EVENT 存储了按月的数据。 在 Db2 LUW中,分区的下限是月份的开始(01),而上限是月份的结束(30/31)。 在 PostgreSQL中,下限是月份的开始(1),上限是下个月的开始,这样就将月份末日期保留在同一分区中。
Db2 LUW sql CREATE TABLE "SAMPLE"."DIM_SPORTING_EVENT" ( "SPORTING_EVENT_ID" BIGINT NOT NULL, "SPORT_LOCATION_ID" BIGINT NOT NULL, "SPORT_TYPE_NAME" VARCHAR(15 BYTE) NOT NULL, "HOME_TEAM_ID" BIGINT NOT NULL, "AWAY_TEAM_ID" BIGINT NOT NULL, "START_DATE_TIME" DATE NOT NULL, "SOLD_OUT" INTEGER, "DIM_SPORT_TEAM_SPORT_TEAM_ID" INTEGER, "DIM_SPORT_TEAM_SPORT_TEAM_ID1" INTEGER, PRIMARY KEY(SPORTING_EVENT_ID) ) PARTITION BY RANGE("START_DATE_TIME") ( PART "JAN2017" STARTING('2017-01-01') ENDING('2017-01-31'), PART "FEB2017" STARTING('2017-02-01') ENDING('2017-02-28'), PART "MAR2017" STARTING('2017-03-01') ENDING('2017-03-31') );
PostgreSQL ```sql CREATE TABLE IF NOT EXISTS sample.dim_sporting_event ( sporting_event_id bigint NOT NULL, sport_location_id bigint NOT NULL, sport_type_name character varying(15) NOT NULL, home_team_id bigint NOT NULL, away_team_id bigint NOT NULL, start_date_time date NOT NULL, sold_out integer, dim_sport_team_sport_team_id integer, dim_sport_team_sport_team_id1 integer, CONSTRAINT sql220915081554000 PRIMARY KEY (sporting_event_id, start_date_time) ) PARTITION BY RANGE (start_date_time);
CREATE TABLE sample.dim_sporting_event_jan2017 PARTITION OFsample.dim_sporting_event FOR VALUES FROM ('2017-01-01') TO ('2017-02-01'); CREATE TABLE sample.dim_sporting_event_feb2017 PARTITION OFsample.dim_sporting_event FOR VALUES FROM ('2017-02-01') TO ('2017-03-01'); CREATE TABLE sample.dim_sporting_event_mar2017 PARTITION OFsample.dim_sporting_event FOR VALUES FROM ('2017-03-01') TO ('2017-04-01');
在 PostgreSQL 中,主键需要包括分区键,而在 Db2 LUW 中并没有此限制。 添加分区键后,现有插入语句可能会导致 PostgreSQL中的重复项。
以下表格包含 `sample.dim_sporting_event` 表中的示例数据条目。
**现有主键 (Db2 LUW) (SPORTING_EVENT_ID)** | **插入操作** | **新主键 (PostgreSQL) (sporting_event_id, start_date_time)** | **插入操作**
---|---|---|---
1234 | 成功 | 1234, 2017-01-02 | 成功
1234 | 失败 | 1234, 2017-01-03 | 成功
这可以根据您的业务逻辑进行调整。例如,解决方案可能包括识别其他主键或唯一键,或者从表中删除分区。在决定从表中删除分区或添加其他键之前,您需要验证功能和非功能要求。
最后,Db2 LUW 分区列可以在生成的列上定义,但在 PostgreSQL v15 及以下版本中无法实现。有关更多信息,请参阅
。
### 序列
您可能在 SEQUENCE 语句中使用了 CACHE 选项来提高性能和调整。在使用时,DB2 在内存中预分配指定数量的顺序值。这有助于最小化锁争用。在
Db2 LUW 服务器上,缓存的值对连接是可见的。尽管 PostgreSQL 中存在 cache关键字,但它仅对正在访问序列的连接或会话进行缓存。如果新连接或会话访问序列,则会为该会话缓存一组新值。比较下面的代码块。
**Db2 LUW** `sql CREATE TABLE "SAMPLE"."PLATFORM" ( "PLATFORM_ID" INTEGER NOTNULL GENERATED BY DEFAULT AS IDENTITY ( START WITH +1 INCREMENT BY +1 MINVALUE
+1 MAXVALUE +2147483647 NO CYCLE CACHE 20 NO ORDER ) , "PLATFORM_NAME"
VARCHAR(255) NOT NULL );`
**PostgreSQL** `sql CREATE TABLE sample.platform( platform_id BIGINT NOT NULLGENERATED BY DEFAULT AS IDENTITY ( MAXVALUE 2147483647 MINVALUE 1 NO CYCLECACHE 20), platform_name CHARACTER VARYING(255) NOT NULL );`
**Db2 LUW** ```shell
# session 1
db2 "insert into PLATFORM(PLATFORM_NAME) values('EC2')" DB20000I The SQLcommand completed successfully.
db2 "select * from PLATFORM" PLATFORM_ID PLATFORM_NAME 1 EC2
# session 2
db2 "insert into PLATFORM(PLATFORM_NAME) values('ECR')" DB20000I The SQLcommand completed successfully.
db2 "select * from PLATFORM" PLATFORM_ID PLATFORM_NAME 1 EC2 2 ECR ```
**PostgreSQL** ```shell
# session 1
postgres=> insert into PLATFORM(PLATFORM_NAME) values('ECR'); INSERT 0 1 postgres=> select * from PLATFORM ; platform_id | platform_name \-------------+--------------- 1 | ECR (1 row)
# session 2
postgres=> insert into PLATFORM(PLATFORM_NAME) values('EC2'); INSERT 0 1 postgres=> select * from PLATFORM ; platform_id | platform_name \-------------+--------------- 1 | ECR 21 | EC2 (2 rows) ```
身份列自动为添加到表中的每一行生成唯一的数字值。通过 INCREMENT BY子句,您可以设置每个新行的后续值增加多少。请注意,迁移后身份列不会在目标上推进。因此,您应重置序列为下一个值,以帮助防止重复。使用
函数可以返回与身份列相关的序列的名称。以下匿名代码块可以帮助您生成 SQL 语句以重新启动身份序列。
以下代码片段生成一个 SELECT SETVAL 语句,以使用最大值 + 1 重启身份序列:
`sql DO $BLOCK$ DECLARE l_record RECORD; l_object_name varchar; l_seq_namevarchar; l_sql varchar; l_tabschema varchar := 'sample'; BEGIN -- for loop FORl_record in SELECT table_schema, table_name, column_name frominformation_schema.columns where table_schema = l_tabschema and is_identity =
'YES' and identity_generation = 'BY DEFAULT' LOOP -- create object name RAISENOTICE ' l_record : %', l_record; l_object_name := NULL; l_object_name :=
concat_ws('', l_record.table_schema, '.', l_record.table_name); RAISE NOTICE '
l_object_name : %', l_object_name; -- get sequence name l_seq_name := NULL;
SELECT pg_get_serial_sequence(l_object_name, l_record.column_name) INTOl_seq_name ; RAISE NOTICE ' l_seq_name : %', l_seq_name; l_sql := NULL; l_sql
:= concat_ws('', 'SELECT SETVAL(''', l_seq_name, ''' ,(SELECT coalesce(MAX(',
l_record.column_name, '),0) + 1 FROM ', l_object_name, '));'); RAISE NOTICE '
l_sql : %', l_sql; EXECUTE l_sql; END LOOP; END $BLOCK$;`
### 物化查询表
(MQT)
是通过查询定义的,它们有助于提高 Db2 LUW 中复杂查询的性能。PostgreSQL 具有
(MV) 来实现类似的功能要求。以下表格比较了这些功能。
**Db2 LUW** | **PostgreSQL**
---|---
被称为 _物化查询表_ | 被称为 _物化视图_
两种类型:系统管理和用户管理,使用 MAINTAINED BY 子句标识 | 仅用户管理
系统管理物化查询表可以使用 REFRESH IMMEDIATE 选项自动刷新 | 本地仅支持手动刷新,通过触发器可实现自动刷新
支持全量和增量刷新 | 仅支持全量刷新
刷新期间独占表锁 | 使用 关键字支持刷新而不锁定选择语句
优化器会在查询重写阶段自动考虑 MQT | 优化器不会在查询重写阶段考虑 MV
允许对用户管理的 MQT 进行 DML 语句 | 不允许对 MV 执行 DML 语句
您可以将 Db2 LUW 的用户管理 MQT 转换为 PostgreSQL 的物化视图。通过将 MQT 转换为常规表并添加触发器,根据源表上的数据操作语言
(DML) 操作来刷新数据,可以将 Db2 LUW 的系统管理 MQT 转换为 PostgreSQL。
### 唯一索引
在 Db2 LUW 中,唯一索引将 NULL 值视为相等。这意味着如果唯一索引中有一个允许 NULL 的列,则只会出现一次。然而,PostgreSQL 将
NULL 视为不同的值。因此,您可以在唯一索引的列中有多个 NULL 值。比较下面的代码块。
**Db2 LUW** ```sql CREATE TABLE "SAMPLE"."TEST_UQ_IDX" ( "ID" INTEGER NOT NULL
, "NAME" CHAR(20) NOT NULL , "PANCARD" VARCHAR(50) );
ALTER TABLE "SAMPLE"."TEST_UQ_IDX" ADD PRIMARY KEY("ID");
CREATE UNIQUE INDEX "SAMPLE"."U_PANCARD" ON "SAMPLE"."TEST_UQ_IDX" ("NAME",
"PANCARD"); ```
**PostgreSQL** ```sql CREATE TABLE IF NOT EXISTS test_uq_idx ( id integer NOTNULL, name character(20) NOT NULL, pancard character varying(50), CONSTRAINTtest_uq_idx_pkey PRIMARY KEY (id) );
CREATE UNIQUE INDEX IF NOT EXISTS u_pancard ON test_uq_idx (name, pancard);
Db2 LUW shell db2inst1@ip-172:~$ db2 "insert into SAMPLE.TEST_UQ_IDXvalues ( 1, 'Nikhil','1234asd')" DB20000I The SQL command completedsuccessfully. db2inst1@ip-172:~$ db2 "insert into SAMPLE.TEST_UQ_IDX values ( 2, 'Nikki',NULL)" DB20000I The SQL command completed successfully. db2inst1@ip-172:~$ db2 "insert into SAMPLE.TEST_UQ_IDX values ( 3, 'Nikki',NULL)" DB21034E The command was processed as an SQL statement becauseit was not a valid Command Line Processor command. During SQL processing, itreturned: SQL0803N One or more values in the INSERT statement, UPDATEstatement, or foreign key update caused by a DELETE statement are not validbecause the primary key, unique constraint or unique index identified by "2" constrains table "DB2INST1.TEST_UQ_IDX" from having duplicate values for theindex key. SQLSTATE=23505
PostgreSQL shell lab=> insert into TEST_UQ_IDX values ( 1, 'Nikhil','1234asd'); INSERT 0 1 lab=> insert into TEST_UQ_IDX values ( 2, 'Nikki',NULL); INSERT 0 1 lab=> insert into TEST_UQ_IDX values ( 3, 'Nikki',NULL); INSERT 0 1
您可以在 PostgreSQL 中使用部分索引模拟 Db2 LUW 的唯一索引行为(最多到版本 14):
sql create unique index IF NOT EXISTS u_pancard ON public.test_uq_idx (name, pancard) where pancard is not null; create unique index IF NOT EXISTSu_pancard1 ON public.test_uq_idx (name) where pancard is null;
mig_lab=> insert into TEST_UQ_IDX values ( 3, 'Nikki',NULL); ERROR: duplicatekey value violates unique constraint "u_pancard1" DETAIL: Key (name)=(Nikki )
already exists. mig_lab=> ```
在 v15 或更高版本中,您可以通过在创建索引语句中添加 来实现。
成功转换架构后,下一步是验证。您可以通过验证 Db2 LUW 和 PostgreSQL 之间对象的数量和属性是否匹配来进行架构验证,可以使用本文提供的
SQL 示例 。您可以将此代码作为脚本或批处理的一部分执行,以实现此里程碑的自动化。
## 使用原生 EXPORT 和 COPY 命令迁移数据
这个场景是一个异构迁移,仅需要一次完整加载
Leave a Reply