SQL Server数据迁移到udb

本节讲述 data2udb 工具的使用,此工具可以将 表结构/数据/视图/函数/存储过程/触发器等 导出到文件,并对语法自动转换,最后将文件导入到udb。
结合ogg实现增量数据同步。

注意事项

  • 目标端对应数据库的表将被清除,如有重要数据请先备份

  • 增量同步功能仅支持 SQL Server 2012, SQL Server 2014, SQL Server 2016, SQL Server 2017, SQL Server 2019

  • Sql Server 2008 不支持SEQUENCE

  • 增量同步功能源端需要开启Sql Server 代理

  • 需要 docker 环境

  • 不支持arm架构服务器

  • 内存需大于4G

  • 数据导出时需要落到磁盘上,确保磁盘空间充足

  • 确保系统时间和源端目标端一致

  • 账号,密码,库名不建议有特殊字符

  • 增量同步期间不支持表结构变更

  • 增量同步需先停止源端数据写入

操作步骤

准备连接信息—–下载容器镜像—–启动容器—–准备增量同步—–全量导出—–全量导入—–开启增量同步

准备

以下用source库名为例

USE [master]  
GO  
CREATE LOGIN [ogguser] WITH PASSWORD=N'oggpwd', DEFAULT_DATABASE=[source], CHECK_EXPIRATION=OFF, CHECK_POLICY=OFF  
GO  
EXEC master..sp_addsrvrolemember @loginame = N'ogguser', @rolename = N'sysadmin'  
GO  
--创建增量同步ogguser账号,并设置sysadmin权限  


USE [source]
GO
CREATE SCHEMA [ogg] AUTHORIZATION [dbo];
GO
alter database source set recovery full;
GO
EXECUTE sys.sp_cdc_enable_db;
--创建ogg模式,用于记录同步信息;  
--设置为完整恢复模式;  
--开启cdc;  

USE [source]
SELECT name,is_cdc_enabled FROM sys.databases WHERE is_cdc_enabled = 1  and name='source';  
SELECT name,is_tracked_by_cdc FROM source.sys.tables WHERE is_tracked_by_cdc = 1;  
--检查启用cdc的库和表  


BACKUP DATABASE [source] TO DISK= N'E:\source.bak' WITH CHECKSUM,COMPRESSION
--首次备份数据库(配置完成前不要截断日志)  

use source;
create table dbo.test1 (id int primary key, name varchar(50));
create table dbo.test2( id int, name varchar(50), age int);
insert into test1(id,name) values(1,'1a');
insert into test2(id,name) values(1,'2a');
-- 创建模拟表和数据  

例如:
s_h=192.168.4.53 #源端地址
s_p=1633 #源端端口
s_s=dbo #源端模式名如dbo
s_d=source #源端数据库名
s_u=sa #源端账号
s_w=admin #源端密码

d_h=192.168.4.11 #目标端地址
d_p=5678 #目标端口
d_d=ms2012_source #目标端库名
d_u=unvdb #目标端账号
d_w=tx #目标端密码

下载容器镜像文件

x86_64 http://download.unvdb.com/您的服务编码/source/tool/ogg/app/x86_64/data2udb.tar.gz
arm64 http://download.unvdb.com/您的服务编码/source/tool/ogg/app/aarch64/data2udb.tar.gz
服务编码联系技术支持获取

启动容器

导入docker镜像

docker load -i 容器镜像文件    
Loaded image: data2udb_x86:v24.8 
#导入的镜像是 data2udb_x86:v24.8,下文使用此名称创建容器 

创建容器

docker run --network=host -it --name data2udb -e TZ=Asia/Shanghai -e s_h=源端地址 -e s_p=源端端口 -e s_s=源端模式名如dbo -e s_d=源端数据库名 -e s_u=源端账号 -e s_w=源端密码 -e d_h=目标端地址 -e d_p=目标端口 -e d_d=目标端数据库名 -e d_u=目标端账号 -e d_w=目标端密码 -e ty=full  data2udb_x86:v24.8 /bin/bash
#请对应修改账号信息和镜像名称  

准备增量同步,仅当需要增量同步时操作

开启目标端接收

export LD_LIBRARY_PATH=/app/udb/lib/:/app/instantclient_19_3/
cat << EOF > /etc/odbc.ini
[unvdb]
Description = unvdb
Driver = /app/udb/lib/GGpsql25.so
Database = $d_d
Servername = $d_h
UserName = $d_u
Password = $d_w
PortNumber = $d_p
ReadOnly = 0
ConnSettings = set client_encoding to UTF8

[msodbcsql1710]
Description = mssql
Driver=ODBC Driver 17 for SQL Server
Server = $s_h,$s_p            
Database = $s_d
User=$s_u
Password=$s_w
TrustServerCertificate=YES
EOF

cat << EOF > /app/udb/dirprm/repz.prm
replicat repz
SETENV(ODBCINI=/etc/odbc.ini)
SETENV(NLS_LANG=AMERICAN_AMERICA.ZHS16GBK)
TARGETDB unvdb,userid $d_u,password $d_w
DISCARDFILE ./dirrpt/repz.rpt, append
reportcount every 60 seconds, rate
cachemgr  cachesize  1G 
container  mem_limit  2G
map $s_s.*, target public.*;
EOF

启动目标端接收

cat << EOF > /tmp/2
start mgr
dblogin sourcedb unvdb
add checkpointtable public.checkpointtab
add replicat repz,exttrail ./dirdat/ea,checkpointtable public.checkpointtab
exit
EOF

/app/udb/ggsci paramfile /tmp/2 

开启源端提取

echo 'config ogg src ......'
echo "GGSCHEMA OGG" > /app/mssql/GLOBALS
cat << EOF > /app/mssql/dirprm/extz.prm
extract extz
--SETENV (NLS_LANG=AMERICAN_AMERICA.ZHS16GBK)
sourcedb msodbcsql1710 userid $s_u,password $s_w
discardfile ./dirrpt/extz.dsc, append
rmthost 127.0.0.1, mgrport 8030, tcpbufsize 288750, tcpflushbytes 288750
RMTTRAIL ./dirdat/ea
IGNOREREPLICATES
--TRANLOGOPTIONS DBLOGREADER
reportcount every 60 seconds, rate
table $s_s.*;
EOF

启动源端提取


cat << EOF > /tmp/2
start mgr
dblogin sourcedb msodbcsql1710 userid $s_u password $s_w
add trandata $s_s.*
ADD CHECKPOINTTABLE ogg.ckpt
shell sqlcmd -S $s_h,$s_p -U $s_u -P $s_w -d $s_d -Q "EXECUTE sys.sp_cdc_drop_job 'cleanup';"
shell ./ogg_cdc_cleanup_setup.sh createJob $s_u $s_w $s_d $s_h,$s_p ogg

add ext extz, tranlog, begin now
add exttrail ./dirdat/ea, extract extz
start extz
exit
EOF

/app/mssql/ggsci paramfile /tmp/2

检查增量数据

在源端插入数据, /app/udb/dirdat/ 目录下存在ea开头的文件,说明增量数据已经开始接收

ls /app/udb/dirdat/ -al
total 4
drwxr-x--- 1 root root   33 Aug  5 10:50 .
drwxr-xr-x 1 root root  140 Aug  5 10:45 ..
-rw-r----- 1 root root 1336 Aug  5 10:50 ea000000000

执行全量导出

准备ddl导出配置文件

mkdir /data/oradata -p
cat <<EOF > /tmp/ddl.conf 
ORACLE_HOME        /usr/lib/oracle/19.10/client64/
ORACLE_DSN         dbi:ODBC:driver=msodbcsql1710;server=$s_h,$s_p;database=$s_d;TrustServerCertificate=yes
ORACLE_USER        $s_u
ORACLE_PWD         $s_w
SCHEMA             $s_s

NLS_LANG  UTF8
#NLS_LANG           AMERICAN_AMERICA.UTF8
#设置字符编码
CLIENT_ENCODING UTF8
USE_RESERVED_WORDS 1
#启用后关键字用双引号包起来
#INDEXES_SUFFIX _idx
#给索引名添加后缀,当索引跟表同名时,建议配置该项
INDEXES_RENAMING 1
#按照“表名_列名“的格式给索引重命名,当多次使用相同索引名或相同表名的数据库,建议开启
NULL_EQUAL_EMPTY 1
#把Null设置为空字符串
USE_RESERVED_WORDS 1
INDEXES_RENAMING 1
DEFAULT_NUMERIC bigint

CASE_INSENSITIVE_SEARCH none
#EXPORT_SCHEMA 1
#CREATE_SCHEMA 1
TYPE               TYPE,TABLE,FUNCTION,MVIEW,PARTITION,PROCEDURE,VIEW,TRIGGER,SEQUENCE,SYNONYM,SEQUENCE_VALUES
PG_NUMERIC_TYPE    0
#PG_INTEGER_TYPE    1
#DATA_TYPE          NUMBER:bigint
#DATA_TYPE          NUMBER(18, 0):bigint,NUMBER(9, 0):bitnit
#DEFAULT_NUMERIC    numeric
ENABLE_MICROSECOND      1
#开启时间类型YYYY-MM-DD HH24:MI:SS.FF,设置为0时间类型则是YYYY-MM-DD HH24:MI:SS
#REPLACE_ZERO_DATE 
#默认将0000-00-00 00:00:00 替换为 NULL。但当列有非空约束时,需配置该项, 替换为 1970-01-01 00:00:00
EMPTY_LOB_NULL 1
#将空的empty数据指定为null,如果有大量的空lob数据列,建议开启,可以提升迁移速度
#PACKAGE_AS_SCHEMA	0
#不将包导出为模式,而是导出为简单函数
#SKIP               fkeys checks
#SKIP              keys pkeys ukeys indexes checks


OUTPUT_DIR         /data/oradata
#输出sql目录
OUTPUT             ddl.sql
PG_VERSION         14
#STOP_ON_ERROR 0
PACKAGE_AS_SCHEMA 1
PREFIX_PARTITION 1
PREFIX_SUB_PARTITION 1
DISABLE_PARTITION 1
RENAME_PARTITION 1
TRANSACTION readonly
EOF

准备数据导出配置文件

cat << EOF > /tmp/data.conf
ORACLE_HOME        /usr/lib/oracle/19.10/client64/
ORACLE_DSN         dbi:ODBC:driver=msodbcsql1710;server=$s_h,$s_p;database=$s_d;TrustServerCertificate=yes
ORACLE_USER        $s_u
ORACLE_PWD         $s_w
SCHEMA             $s_s
NLS_LANG  UTF8
#NLS_LANG           AMERICAN_AMERICA.UTF8
#设置字符编码
CLIENT_ENCODING UTF8
USE_RESERVED_WORDS 1
#启用后关键字用双引号包起来
#INDEXES_SUFFIX _idx
#给索引名添加后缀,当索引跟表同名时,建议配置该项
INDEXES_RENAMING 1
#按照“表名_列名“的格式给索引重命名,当多次使用相同索引名或相同表名的数据库,建议开启
NULL_EQUAL_EMPTY 1
#把Null设置为空字符串
USE_RESERVED_WORDS 1
INDEXES_RENAMING 1
DEFAULT_NUMERIC bigint
CASE_INSENSITIVE_SEARCH none
TYPE               COPY
STOP_ON_ERROR 0
PG_NUMERIC_TYPE    0
PG_INTEGER_TYPE    1
DEFAULT_NUMERIC    numeric
ENABLE_MICROSECOND      1
#REPLACE_ZERO_DATE 
EMPTY_LOB_NULL 1
OUTPUT_DIR         /data/oradata
OUTPUT             data.sql
FILE_PER_TABLE    1
PREFIX_PARTITION 1
PREFIX_SUB_PARTITION 1
DISABLE_PARTITION 1
RENAME_PARTITION 1
TRANSACTION readonly
#JOBS                 32
#DATA_LIMIT 50000
#PARALLEL_TABLES 4
#开启后可以加快导出速度,但有可能部分表导出异常

#EXCLUDE           table1 example_.*
#ALLOW   T_APM_MONITORSET_L
#PG_DSN             dbi:Pg:dbname=oracle;host=192.168.2.81;port=5678
#PG_USER            unvdb
#PG_PWD             udb
#PG_SCHEMA          soe
PG_VERSION         14
EOF

开始导出

data2udb -c /tmp/ddl.conf
[========================>] 11/11 tables (100.0%) end of scanning.          
[========================>] 0/0 types (100.0%) end of output.     
[========================>] 11/11 tables (100.0%) end of table export. 
[========================>] 0/0 functions (100.0%) end of functions export.
[========================>] 0/0 materialized views (100.0%) end of output. 
[========================>] 0/0 partitions (100.0%) end of output.        
[========================>] 0/0 procedures (100.0%) end of procedures export.
[========================>] 0/0 views (100.0%) end of output.                
[========================>] 0/0 triggers (100.0%) end of output.
[========================>] 0/0 synonyms (100.0%) end of output.
data2udb -c /tmp/data.conf
[========================>] 11/11 tables (100.0%) end of scanning.          
[========================>] 119121/119121 rows (100.0%) Table FRGH_BILL (29 sec., 4107 recs/sec)                     
[========================>] 161699/161699 rows (100.0%) Table FRGH_ORDER (18 sec., 8983 recs/sec)                     
[========================>] 1765938/1765938 rows (100.0%) Table INV_ENTRY (61 sec., 28949 recs/sec)                     
[========================>] 115629/115629 rows (100.0%) Table INV_SKU (7 sec., 16518 recs/sec)                          
[========================>] 0/0 rows (100.0%) Table INV_STACK (0 sec., 0 recs/sec)                                    
[========================>] 1385773/1385773 rows (100.0%) Table INV_TALLY (71 sec., 19517 recs/sec)                     
[========================>] 2165/2165 rows (100.0%) Table REF_VESSEL (1 sec., 2165 recs/sec)                            
[========================>] 444288/444288 rows (100.0%) Table TALLY_TICKET (23 sec., 19316 recs/sec)                       
[========================>] 3507/3507 rows (100.0%) Table VSL_VISIT (2 sec., 1753 recs/sec)                                
[========================>] 7014/7014 rows (100.0%) Table VSL_VOYAGE (2 sec., 3507 recs/sec)                            
[========================>] 1/1 rows (100.0%) Table t1 (1 sec., 1 recs/sec)                                          
[========================>] 4005135/4005135 rows (100.0%) on total estimated data (216 sec., avg: 18542 tuples/sec)

执行全量导入

导入之前通过以下命令调整导入顺序,如主键外键触发器等需要在导入数据后执行.

提取主键外键

grep 'FOREIGN KEY' /data/oradata/TABLE_ddl.sql > /tmp/foreign.sql ;
grep '^ALTER TABLE' /data/oradata/TABLE_ddl.sql | grep -v 'FOREIGN KEY' > /tmp/alter.sql ;
\cp /data/oradata/TABLE_ddl.sql /tmp/TABLE_ddl.sql && sed -i '/FOREIGN KEY/d' /data/oradata/TABLE_ddl.sql ;
sed -i "s/^ALTER TABLE/-- ALTER TABLE/g" /data/oradata/TABLE_ddl.sql ;

导入对象

export LD_LIBRARY_PATH=/app/udbclient/lib PGPASSWORD=$d_w

/app/udbclient/ud_sql -v ON_ERROR_STOP=1 -h $d_h -p $d_p -U $d_u -w $d_d -f /data/oradata/TABLE_ddl.sql;
/app/udbclient/ud_sql -v ON_ERROR_STOP=1 -h $d_h -p $d_p -U $d_u -w $d_d -f /data/oradata/FUNCTION_ddl.sql;
/app/udbclient/ud_sql -v ON_ERROR_STOP=1 -h $d_h -p $d_p -U $d_u -w $d_d -f /data/oradata/MVIEW_ddl.sql;
/app/udbclient/ud_sql -v ON_ERROR_STOP=1 -h $d_h -p $d_p -U $d_u -w $d_d -f /data/oradata/PARTITION_ddl.sql;
/app/udbclient/ud_sql -v ON_ERROR_STOP=1 -h $d_h -p $d_p -U $d_u -w $d_d -f /data/oradata/PROCEDURE_ddl.sql;
/app/udbclient/ud_sql -v ON_ERROR_STOP=1 -h $d_h -p $d_p -U $d_u -w $d_d -f /data/oradata/VIEW_ddl.sql;
/app/udbclient/ud_sql -v ON_ERROR_STOP=1 -h $d_h -p $d_p -U $d_u -w $d_d -f /data/oradata/SEQUENCE_ddl.sql;
/app/udbclient/ud_sql -v ON_ERROR_STOP=1 -h $d_h -p $d_p -U $d_u -w $d_d -f /data/oradata/SYNONYM_ddl.sql;

注意: 自定义类型 /data/oradata/TYPE_ddl.sql 语法转换可能会不正确,需要手动处理.可以参考 https://www.sqlines.com/online 网站 进行转换或联系技术支持协助.

此时所有对象已导入目标库中.

导入数据

默认导入到 public 模式,可以使用以下方法导入到指定模式.

echo 'SET search_path TO cus_schema_dbo;' > ~/.psqlrc 

注意: 通过TDS协议创建的库名对应的是 库名_dbo 模式.对应的拥有者是库名_db_owner

/app/udbclient/ud_sql -v ON_ERROR_STOP=1 -h $d_h -p $d_p -U $d_u -w $d_d -f /data/oradata/data.sql

请务必确认此步骤无报错.

导入主键/外键/触发器

/app/udbclient/ud_sql -v ON_ERROR_STOP=1 -h $d_h -p $d_p -U $d_u -w $d_d -f /tmp/alter.sql 
/app/udbclient/ud_sql -v ON_ERROR_STOP=1 -h $d_h -p $d_p -U $d_u -w $d_d -f /tmp/foreign.sql 
/app/udbclient/ud_sql -v ON_ERROR_STOP=1 -h $d_h -p $d_p -U $d_u -w $d_d -f /data/oradata/TRIGGER_ddl.sql 

此时所有数据已导入目标库中.

开启增量同步,仅当需要增量同步时操作

export LD_LIBRARY_PATH=/app/udb/lib/:/app/instantclient_19_3/
echo "START REPLICAT repz" > /tmp/3
echo "exit" >> /tmp/3
/app/udb/ggsci paramfile /tmp/3

此时在源端的数据修改操作会同步到目标端.

其它命令

docker exec data2udb /bin/bash -c '/app/scripts/status.sh' #查看增量同步状态
docker exec data2udb /bin/bash -c 'cat /app/udb/dirrpt/REPZ.rpt' #查看增量同步接收报告
docker logs -f data2udb #查看容器日志
docker stop data2udb #停止容器
docker rm data2udb #删除容器
cat /app/udb/ggserr.log #查看目标端增量同步进程日志
cat /app/ogg19/ggserr.log #查看源端增量同步进程日志

给TDS账号赋权

如果使用的是TDS(兼容SQL SERVER版的数据库)数据库,此时需要修改表权限才可以用TDS账号访问数据.
例如已经用TDS账号创建了cus_schema 数据库,以下语句修改 cus_schema 模式拥有者为对应的虚拟账号.

DO
$$
DECLARE
    row record;
BEGIN
    FOR row IN SELECT tablename FROM pg_tables WHERE schemaname = 'cus_schema_dbo'
    LOOP
        EXECUTE 'ALTER TABLE ' || quote_ident(row.tablename) || ' OWNER TO cus_schema_db_owner';
    END LOOP;
END
$$;