【项目实战】记录一次PG数据库迁移至GaussDB测试(下)

上一篇分享了安装、迁移,本篇将继续分享迁移前操作、 DRS迁移数据、迁移后一致性检查、问题总结及解决方法。

目录

四、迁移前操作

[4.1 源端(PG)](#4.1 源端(PG))

[4.2 目标端(GaussDB库)](#4.2 目标端(GaussDB库))

五、DRS迁移数据

[5.1 创建复制用户](#5.1 创建复制用户)

5.2创建迁移任务。

六、迁移后一致性检查

6.1使用DRS工具对比

6.2手动对比

七、问题总结及解决方法

[7.1 用户迁移](#7.1 用户迁移)

[7.2 序列迁移](#7.2 序列迁移)

[7.3 分区表迁移](#7.3 分区表迁移)

[7.4 函数迁移](#7.4 函数迁移)

[7.5 外键改造](#7.5 外键改造)

[7.6 索引改造](#7.6 索引改造)


四、迁移前操作

4.1 源端(PG)

确定迁移的对象

统计源数据库对象

1、统计数据库个数

psql --d postgres -p 15432

\l+ --统计数据库名称以及大小

\du --统计数据库所有用户

\c 数据库名称 --

\dn 统计数据中schema的名称

3.1.1.2 创建迁移用户及赋权

创建只读用户用于读取数据库全部对象,该用户用于DRS工具迁移数据

Create user drs_read password 'Drs_read#2023';

grant usage on schema power_tf,power_reliability,power_work,power_ds,power_tech,power_quality,power_sch,power_common to drs_read;

grant usage on schema power_tf,power_reliability,power_work,power_ds,power_tech,power_quality,power_sch,power_common to drs_read;

grant select on all sequence in schema power_common,power_tf,power_reliability,power_work,power_ds,power_tech,power_quality,power_sch to drs_read;

grant select on all sequences in schema power_work,power_ds,power_tech,power_quality,power_sch to drs_read;

grant select on all tables in schema power_work,power_ds,power_tech,power_quality,power_sch to drs_read;

4.2 目标端(GaussDB库)

创建复制用户及赋权

gsql --d postgres --p 15432

gauss=#create user drs_rep password 'Drs_rep#2023';

gauss=#alter user drs_rep sysadmin;

UGO迁移表结构

使用UGO工具,可以迁移数据库结构、表结构、索引、函数等元数据。

1、创建项目,进行源端数据库评估。

迁移结构后会提示迁移成功和失败的对象百分比,如图所示:

五、DRS迁移数据

5.1 创建复制用户

通过管理员账号登录

创建资源组

5.2创建迁移任务。

使用复制账号drs登录,创建同步任务

点击测试连接,成功后执行下一步

选择要迁移的对象

从列表中可以看出,工具仅支持表和序列的迁移,同时只能迁移一个库。下方限速可针对迁移带宽速率进行调整。

下一步进入迁移前的检查,如果权限不足会提示需要哪些权限,需完成整改后进入下一步

六、迁移后一致性检查

6.1使用DRS工具对比

在DRS任务管理中,可以看到数据迁移进度,在任务结束后可以进行数据对比,如图所示

也可以通过日志看到迁移结果,如图所示:

6.2手动对比

通过登录源库和目标库,分别使用脚本统计各个对象数量。

七、问题总结及解决方法

7.1 用户迁移

**问题描述:**DRS工具和UGO工具无法迁移用户,需在目标库创建用户以及授权。

**处理方法:**手动创建。

# su -- omm

gsql --d postgres --p 15432 --r

create user drmc password 'PAssw0rd_2023';
授权

连接测试

gsql -d postgres -p 15432 -U drmc -W PAssw0rd_2023 -r 

7.2 序列迁移

**问题描述:**DRS工具或UGO工具迁移序列到目标库,序列会重新从初始值开始递增,如果业务切换至目标库会导致采用序列做为主键的列值冲突

**解决方法:**列举所有scheme的序列,收集当前序列值,在目标库重置当前值。

源库收集序列当前值

select * from pg_sequence;

收集每个序列last_value

目标库重置每个序列的当前值

Select setval('序列名称','当前值');

Gsql --p 15432 --d postgres -r

Gaussdb=#\d+ 序列名---查看当前值

7.3 分区表迁移

**问题描述:**DRS工具在迁移分区表时会将分区表转化成普通表。UGO在PG迁移至GaussDB数据库中,由于语法不兼容问题,需要手动修改语法来实现。

**解决方法:**1、手动创建分区表,然后导入数据。

# su -- opostgres

收集所有分区表

select nmsp_parent.nspname as

parent_schema,

parent.relname as parent,

nmsp_child.nspname as child_schema,

child.relname as child_name

from pg_inherits

join pg_class parent

on pg_inherits.inhparent=parent.oid

join pg_class child

on pg_inherits.inhrelid=child.oid

join pg_namespace nmsp_parent

on nmsp_parent.oid=parent.relnamespace

join pg_namespace nmsp_child

on nmsp_child.oid=child.relnamespace;

手动创建分区表

示例:CREATE TABLE power_sch.abc (

obj_id NVARCHAR2(42) NOT NULL,

sun_year_month NVARCHAR2(42)

CONSTRAINT " abc_pkey" PRIMARY KEY (sun_year_month, obj_id)

)

PARTITION BY LIST (sun_year_month);

添加分区:

alter table power_sch.abc add partition abc_201201 VALUES ('201201');

7.4 函数迁移

**问题描述:**支持迁移普通自定义函数,依赖PG插件的函数需手动改写。

**解决方法:**1、手动改写实现相关功能。

select a.usename,b.proname from pg_user a,pg_proc b where a.usesysid=b.proowner and a.usename<>'postgres';

7.5 外键改造

**问题描述:**在迁移中发现GaussDB分布式数据库不支持外键,需要将表的外键用触发器形式实现,在UGO迁移过程中提示:FOREIGN KEY ...REFERENCES constraint is not yet supported.

**解决方法1:**使用GaussDB集中式支持外键约束。

**解决方法2:**GaussDB分布式数据库处理外键约束的方法。

场景1:一个父表,一个子表,trigger完全等价

1.创建两个表,一个主表和一个从表,主表中包含主键列,从表中包含外键列。

CREATE TABLE parent (

id INT PRIMARY KEY,

name VARCHAR(50) NOT NULL

);

CREATE TABLE child (

id INT,

parent_id INT,

name VARCHAR(50) NOT NULL,

FOREIGN KEY (parent_id) REFERENCES parent(id)

); 

2.向主表中插入数据。

INSERT INTO parent (id, name) VALUES (1, 'John');

INSERT INTO parent (id, name) VALUES (2, 'Jane');

INSERT INTO parent (id, name) VALUES (3, 'Jane'); 

3.向从表中插入数据,其中外键列的值应该与主表中的主键列的值相匹配。

INSERT INTO child (id, parent_id, name) VALUES (1, 1, 'Tom');

INSERT INTO child (id, parent_id, name) VALUES (2, 2, 'Jerry');

4.尝试插入一条不匹配的从表数据,即外键列的值与主表中不存在的主键列的值相匹配。此时应该抛出异常。

INSERT INTO child (id, parent_id, name) VALUES (4, 4, 'Spike');

---结果:ORA-02291: 违反完整约束条件 (UGO.SYS_C0087731) - 未找到父项关键字

5.update子表,无法成功

update child set parent_id=22 where id=2;

---结果:ORA-02291: 违反完整约束条件 (UGO.SYS_C0087731) - 未找到父项关键字

1.2GaussDB替代方案:分布式不支持外键,使用trigger保证外键约束的一致性,如果依赖的父表存在,insert,不存在raise

1.创建两个表,一个主表和一个从表,主表中包含主键列,从表中包含外键列。

CREATE TABLE parent (

id INT PRIMARY KEY,

name VARCHAR(50) NOT NULL

);

CREATE TABLE child (

id INT PRIMARY KEY,

parent_id INT,

name VARCHAR(50) NOT NULL/*,

FOREIGN KEY (parent_id) REFERENCES parent(id)*/ --注释掉外键

);

使用trigger实现等价功能--外键一致性

--触发函数

create or replace function func_child() return trigger

as

id_values int;

BEGIN

SELECT COUNT(id) INTO id_values

FROM parent

WHERE id = NEW.parent_id;

IF id_values=0 THEN

RAISE EXCEPTION '%' , 'Foreign key constraint violated because the id column value does not exist in the parent table.';

else

RETURN NEW;

END IF;

end;

/

--触发器

DROP TRIGGER if exists tri_child on child;

CREATE TRIGGER tri_child

before insert or update on child

FOR EACH ROW

EXECUTE PROCEDURE func_child();

2.向主表中插入数据。

INSERT INTO parent (id, name) VALUES (1, 'John');

INSERT INTO parent (id, name) VALUES (2, 'Jane');

INSERT INTO parent (id, name) VALUES (3, 'Jane'); 

3.向从表中插入数据,其中外键列的值应该与主表中的主键列的值相匹配。

INSERT INTO child (id, parent_id, name) VALUES (1, 1, 'Tom');

INSERT INTO child (id, parent_id, name) VALUES (2, 2, 'Jerry'); 

4.尝试插入一条不匹配的从表数据,即外键列的值与主表中不存在的主键列的值相匹配。此时应该抛出异常。

INSERT INTO child (id, parent_id, name) VALUES (4, 4, 'Spike');

ugo=> INSERT INTO child (id, parent_id, name) VALUES (4, 4, 'Spike');

ERROR: Foreign key constraint violated because the id column value does not exist in the parent table. 

5.update子表,无法成功

update child set parent_id=22 where id=2;

ugo=> update child set parent_id=22 where id=2;

ERROR: Foreign key constraint violated because the id column value does not exist in the parent table. 

场景2:多个父表,一个子表,trigger完全等价

2.1多外键:

1.创建两个表,两个主表和一个从表,主表中包含主键列,从表中包含外键列。

drop table parent;

CREATE TABLE parent (

id INT PRIMARY KEY,

name VARCHAR(50) NOT NULL

);

drop table parent1;

CREATE TABLE parent1 (

id INT ,

idname VARCHAR(50) PRIMARY KEY

);

drop table child;

CREATE TABLE child (

id INT ,

parent_id INT,

name VARCHAR(50) NOT NULL,

FOREIGN KEY (parent_id) REFERENCES parent(id),

FOREIGN KEY (name) REFERENCES parent1(idname)

);

2.向主表中插入数据。

INSERT INTO parent (id, name) VALUES (1, 'John');

INSERT INTO parent (id, name) VALUES (2, 'Jane');

INSERT INTO parent (id, name) VALUES (3, 'Jane');

INSERT INTO parent1 (id, idname) VALUES (1, 'John1');

INSERT INTO parent1 (id, idname) VALUES (2, 'Jane1');

INSERT INTO parent1 (id, idname) VALUES (3, 'xxJane1');

3.向从表中插入数据,其中外键列的值应该与主表中的主键列的值相匹配。

INSERT INTO child (id, parent_id, name) VALUES (1, 1, 'John1');

INSERT INTO child (id, parent_id, name) VALUES (2, 2, 'xxJane1'); 

4.尝试插入一条不匹配的从表数据,即外键列的值与主表中不存在的主键列的值相匹配。此时应该抛出异常。

INSERT INTO child (id, parent_id, name) VALUES (1, 1, 'Tom');

INSERT INTO child (id, parent_id, name) VALUES (1, 4, 'John1');

INSERT INTO child (id, parent_id, name) VALUES (4, 4, 'Spike');

---结果:ORA-02291: 违反完整约束条件 (UGO.SYS_C0087731) - 未找到父项关键字

5.update子表,无法成功

update child set parent_id=22 where id=2;

---结果:ORA-02291: 违反完整约束条件 (UGO.SYS_C0087731) - 未找到父项关键字 

2.2GaussDB替代方案:分布式,使用trigger完全等价

1.创建两个表,两个主表和一个从表,主表中包含主键列,从表中包含外键列。

drop table parent;

CREATE TABLE parent (

id INT PRIMARY KEY,

name VARCHAR(50) NOT NULL

);

drop table parent1;

CREATE TABLE parent1 (

id INT ,

idname VARCHAR(50) PRIMARY KEY

);

drop table child;

CREATE TABLE child (

id INT ,

parent_id INT,

name VARCHAR(50) NOT NULL/*,

FOREIGN KEY (parent_id) REFERENCES parent(id),

FOREIGN KEY (name) REFERENCES parent1(idname)*/

);

trigger实现等价功能

--触发函数

create or replace function func_child() return trigger

as

parent_id_values int;

name_values int;

BEGIN

SELECT COUNT(id) INTO parent_id_values

FROM parent

WHERE id = NEW.parent_id;



SELECT COUNT(idname) INTO name_values

FROM parent1

WHERE idname = NEW.name;



IF parent_id_values=0 or name_values=0 THEN

RAISE EXCEPTION '%', 'Foreign key constraint violated because the id column value and idname column value does not exist in the parent table and parent1 table .';

else

RETURN NEW;

end if;

end;

/

--触发器

DROP TRIGGER if exists tri_child on child;

CREATE TRIGGER tri_child

before insert or update on child

FOR EACH ROW

EXECUTE PROCEDURE func_child(); 

2.向主表中插入数据。

INSERT INTO parent (id, name) VALUES (1, 'John');

INSERT INTO parent (id, name) VALUES (2, 'Jane');

INSERT INTO parent (id, name) VALUES (3, 'Jane');

INSERT INTO parent1 (id, idname) VALUES (1, 'John1');

INSERT INTO parent1 (id, idname) VALUES (2, 'Jane1');

INSERT INTO parent1 (id, idname) VALUES (3, 'xxJane1'); 

3.向从表中插入数据,其中外键列的值应该与主表中的主键列的值相匹配。

INSERT INTO child (id, parent_id, name) VALUES (1, 1, 'John1');

INSERT INTO child (id, parent_id, name) VALUES (2, 2, 'xxJane1');

4.尝试插入一条不匹配的从表数据,即外键列的值与主表中不存在的主键列的值相匹配。此时应该抛出异常。

INSERT INTO child (id, parent_id, name) VALUES (1, 1, 'Tom');

INSERT INTO child (id, parent_id, name) VALUES (1, 4, 'John1');

INSERT INTO child (id, parent_id, name) VALUES (4, 4, 'Spike');

ugo=> INSERT INTO child (id, parent_id, name) VALUES (1, 1, 'Tom');

ERROR: Foreign key constraint violated because the id column value and idname column value does not exist in the parent table and parent1 table .

ugo=> INSERT INTO child (id, parent_id, name) VALUES (1, 4, 'John1');

ERROR: Foreign key constraint violated because the id column value and idname column value does not exist in the parent table and parent1 table .

ugo=> INSERT INTO child (id, parent_id, name) VALUES (4, 4, 'Spike');

ERROR: Foreign key constraint violated because the id column value and idname column value does not exist in the parent table and parent1 table . 

5.update子表,不成功

update child set parent_id=22 where id=1;

update child set name='xiaowang' where parent_id=2;

ERROR: Foreign key constraint violated because the id column value and idname column value does not exist in the parent table and parent1 table .

7.6 索引改造

**问题描述1:**目标库采用GaussDB分布式数据库时,在使用UGO工具迁移索引过程中,提示Cannot create index whose evaluation cannot be enforced to remote nodes.

**问题描述2:**在使用UGO工具迁移索引过程中,分区表索引无法迁移,需要手动获取源端索引定义手动创建。

**处理方法1:**到数据库中查找表对象的分布键,添加分布列id到索引个。

示例:power_ocp.config_info_bak对象,可以在数据库中使用

\d+ power_ocp.config_info_bak

来查找该对象的分布键,即下图红线中显示的Distribute By后的关键字

**处理方法2:**手动获取源端分区表索引定义

Select * from pg_indexes where tablename in ('',''); 

手动执行索引定义

PG数据库迁移至GaussDB测试分享到此结束,欢迎大家一起交流学习~

相关推荐
油丶酸萝卜别吃10 分钟前
MyBatis中XML文件的模板
xml·数据库·mybatis
三天不学习12 分钟前
【Select 语法全解密】.NET开源ORM框架 SqlSugar 系列
数据库·.net·orm·微软技术·sqlsugar
CC呢25 分钟前
基于单片机的智能婴儿床监护系统多功能婴儿床摇篮系统
数据库·mongodb
喜欢猪猪1 小时前
Java技术专家视角解读:SQL优化与批处理在大数据处理中的应用及原理
android·python·adb
林的快手1 小时前
209.长度最小的子数组
java·数据结构·数据库·python·算法·leetcode
HEU_firejef2 小时前
Redis——缓存预热+缓存雪崩+缓存击穿+缓存穿透
数据库·redis·缓存
eventer1232 小时前
KylinOS V10 SP3下编译openGauss与dolphin插件
gaussdb
KELLENSHAW2 小时前
MySQL45讲 第三十七讲 什么时候会使用内部临时表?——阅读总结
数据库·mysql
一只淡水鱼663 小时前
【mybatis】详解 # 和 $ 的区别,两者分别适用于哪种场景,使用 $ 不当会造成什么影响
sql·spring·mybatis·sql注入
zhangphil3 小时前
Android adb查看某个进程的总线程数
android·adb