DMHS是DM对标OGG推出的实时同步工具,目前在生产系统中承担众多数据回流工具的角色,以实现数据库柔性替代方案。其对于主流数据库的支持已经相对成熟,本次将以一台DM8(大小写敏感)和MySQL(大小写敏感)进行非落地复制双向同步来展示安装部署流程,其中会展示图形/非图形安装,并进行交叉测试和一些故障处理分享
类别 | MySQL | DM8 |
---|---|---|
CPU | i5 1.60GHz * 2 core | i5 1.60GHz * 2 core |
DISK | 20G | 20G |
MEM | 4G | 4G |
NC | 1000MB * 2 | 1000MB * 2 |
Database | DMHS | OS |
---|---|---|
MySQL5.7.33 | dmhs_V4.2.60_mysql_rev106302_rh6_64_veri_20220225 | Red Hat Enterprise Linux Server 7.6 (Maipo) |
DM8 | dmhs_V4.2.60_dm8_rev106302_rh6_64_veri_20220225 | Red Hat Enterprise Linux Server 7.6 (Maipo) |
DMHS对部分MySQL字段类型尚不支持,具体如下
类型 | 名称 | 支持 |
---|---|---|
字符类型 | CHAR | Y |
- | VARCHAR | Y |
数值类型 | BIT | Y |
- | BOOL/BOOLEAN | Y |
- | TINYINT | Y |
- | SMALLINT | Y |
- | MEDIUMINT | Y |
- | INT/INTEGER | Y |
- | BIGINT | Y |
- | NUMERIC | Y |
- | DECIMAL/DEC | Y |
- | REAL | Y |
- | FLOAT | Y |
- | DOUBLE | Y |
日期和时间类型 | DATE | Y |
- | TIMESTAMP | Y |
- | YEAR | Y |
- | TIME | Y |
- | DATETIME | Y |
LOB 类型 | TINYBLOB | Y |
- | BLOB | Y |
- | MEDIUMBLOB | Y |
- | LONGBLOB | Y |
- | TINYTEXT | Y |
- | TEXT | Y |
- | MEDIUMTEXT | Y |
- | LONGTEXT | Y |
二进制类型 | BINARY | Y |
- | VARBINARY | Y |
集合类型 | ENUM | N |
- | SET | N |
类型 | 名称 | 支持 |
---|---|---|
TABLE | INSERT | Y |
- | UPDATE | Y |
- | DELETE | Y |
- | DROP | Y |
- | CREATE | Y |
- | TRUNCATE | Y |
COLUMN | RENAME | Y |
- | ADD | Y |
- | DROP | Y |
- | MODIFY LEN | Y |
- | MODIFY PRECISION/SCALE | Y |
- | MODIFY TYPE | Y |
- | MODIFY DEFAULT | Y |
CONSTRAINT | ADD PRIMARY KEY | Y |
- | DROP PRIMARY KEY | Y |
- | ADD FOREIGN KEY | Y |
- | DROP FOREIGN KEY | Y |
- | ADD UNIQUE KEY | Y |
- | DROP UNIQUE KEY | Y |
- | ADD CHECK | Y |
- | DROP CHECK | Y |
- | ADD NOT NULL | Y |
- | DROP NOT NULL | Y |
INDEX | CREATE | Y |
- | DROP | Y |
SEQUENCE | CREATE | N |
- | DROP | N |
PROCEDURE | CREATE | N |
- | DROP | N |
PACKAGE | CREATE | N |
- | DROP | N |
TRIGGERCREATE | N | |
- | DROP | N |
VIEW/MATERIALIZED VIEW | CREATE | N |
- | DROP | N |
USER | CREATE | N |
- | DROP | N |
通用限制
MySQL限制
不支持加密数据的同步;
对主键的更新,不支持类似 update t set key=key+n 的操作;
仅支持mysql单机数据库(仅主库)
需将将执行端MySQL表中的外键禁用,否则,在对外键引用表进行更新时,可能会引起执行端MYSQL数据库操作错误。
需将执行端MySQL中的存在DML操作的触发器禁用,否则,在通过过程中,会对触发器操作的表进行二次操作,导致同步结果错误。
关闭防火墙
[root@ dmdsc0 ~]# systemctl stop firewalld
[root@ dmdsc0 ~]# systemctl disable firewalld
Removed symlink /etc/systemd/system/multi-user.target.wants/firewalld.service.
Removed symlink /etc/systemd/system/dbus-org.fedoraproject.FirewallD1.service.
日志配置
确认已经打开本地归档和逻辑日志标记,未打开则做相应配置
[dmdba@dmdsc0 bin]$ ./disql SYSDBA/[email protected]:6236
服务器[192.168.56.7:6236]:处于普通打开状态
登录使用时间 : 32.412(ms)
disql V8
SQL> select para_name,para_value from v$dm_ini where para_name in('ARCH_INI','RLOG_APPEND_LOGIC');
行号 PARA_NAME PARA_VALUE
---------- ----------------- ----------
1 RLOG_APPEND_LOGIC 1
2 ARCH_INI 1
已用时间: 274.439(毫秒). 执行号:1000.
SQL> SELECT ARCH_DEST, ARCH_FILE_SIZE FROM SYS.V$DM_ARCH_INI WHERE ARCH_TYPE='LOCAL' AND ARCH_IS_VALID='Y';
行号 ARCH_DEST ARCH_FILE_SIZE
---------- ------------- --------------
1 /opt/dsc/arch 256
在线日志确认
SQL> SELECT PATH FROM SYS.V$RLOGFILE;
行号 PATH
---------- ----------------------------------------
1 /opt/dsc/dmdbms/data/DAMENG/DAMENG01.log
2 /opt/dsc/dmdbms/data/DAMENG/DAMENG02.log
参数配置
确认FAST_COMMIT为0 (批量提交会导致RLOG文件的滞后或顺序问题)
SQL> select para_value from v$dm_ini where para_name = 'FAST_COMMIT';
行号 PARA_VALUE
---------- ----------
1 0
已用时间: 16.457(毫秒). 执行号:1003.
DDL复制配置
DDL捕捉依赖于事件触发器和辅助表实现,需要执行脚本进行创建
该脚本中用到一些位与运算,所以disql执行需要处理一下变量前缀,如果通过manager执行则会自动转义
[dmdba@dmdsc0 bin]$ ./disql SYSDBA/[email protected]:6236
服务器[192.168.56.7:6236]:处于普通打开状态
登录使用时间 : 11.222(ms)
disql V8
SQL> show define
DEFINE & (hex 26)
SQL> set define off
SQL> start /opt/dmhs/app/scripts/ddl_sql_dm8.sql
[dmdba@dmdsc0 bin]$ ./disql SYSDBA/[email protected]:6236 \`/opt/dmhs/app/scripts/ddl_sql_dm8.sql
……
END IF;
EXCEPTION
WHEN E1 THEN RAISE E1;
WHEN NO_DATA_FOUND THEN
RETURN;
WHEN OTHERS THEN
INSERT INTO SYSDBA.DMHS_DDL_LOG(OBJ_ID, OBJ_OPUSER, OBJ_SCHNAME, OBJ_NAME, OP_TIME, CONTENT, ERRCODE, ERRMSG)
VALUES(TBL_ID, :EVENTINFO.OPUSER, :EVENTINFO.SCHEMANAME, :EVENTINFO.OBJECTNAME, :EVENTINFO.OPTIME, SUBSTR(SF_CUR_SQL_STR(1), 1, 512), SQLCODE, SQLERRM);
END;
操作已执行
已用时间: 32.028(毫秒). 执行号:2303.
执行后确认辅助表和触发器已经生成,9个辅助表,4个触发器
SQL> select owner, table_name from dba_tables where owner = 'SYSDBA' and table_name like 'DMHS%' and status = 'VALID';
行号 OWNER TABLE_NAME
---------- ------ ----------------
1 SYSDBA DMHS_DDL_COL
2 SYSDBA DMHS_DDL_SQL
3 SYSDBA DMHS_DDL_CONS
4 SYSDBA DMHS_DDL_IDX
5 SYSDBA DMHS_DDL_RENAME
6 SYSDBA DMHS_DDL_SEQ
7 SYSDBA DMHS_DDL_PART
8 SYSDBA DMHS_DDL_COMMENT
9 SYSDBA DMHS_DDL_LOG
9 rows got
SQL> select owner, trigger_name from dba_triggers where owner = 'SYSDBA' and trigger_name like 'DMHS%' and status = 'Y';
行号 OWNER TRIGGER_NAME
---------- ------ -----------------------
1 SYSDBA DMHS_DDL_TRIGGER_AFTER
2 SYSDBA DMHS_DDL_TRIGGER_BEFORE
3 SYSDBA DMHS_DDL_TRIGGER_GRANT
4 SYSDBA DMHS_DDL_TRIGGER_REVOKE
创建数据库用户
实际权限可以进一步细化,本文主要做流程展示
SQL> create user dmhs identified by 123456789;
操作已执行
已用时间: 90.501(毫秒). 执行号:3500.
SQL> grant dba to dmhs;
操作已执行
已用时间: 20.222(毫秒). 执行号:3501.
创建操作系统用户
DM8侧要加到dinstall组
[root@dmdsc0 dmhs]# useradd -g dinstall dmhs -m
[root@dmdsc0 dmhs]# echo "123456"|passwd dmhs --stdin
更改用户 dmhs 的密码 。
passwd:所有的身份验证令牌已经成功更新。
添加环境变量
PATH=$PATH:$HOME/.local/bin:$HOME/bin
export PATH
export DMHS_HOME=/opt/dmhs/app
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/dmhs/app/bin
export PATH=$DMHS_HOME/bin:$PATH
创建目录
[root@dmdsc0 dmhs]# mkdir -p /opt/dmhs/setup
[root@dmdsc0 dmhs]# mkdir -p /opt/dmhs/app
[root@dmdsc0 dmhs]# mkdir -p /opt/dmhs/data
[root@dmdsc0 dmhs]# mkdir -p /opt/dmhs/inst1
[root@dmdsc0 dmhs]# chown -R dmhs.dinstall /opt/dmhs/
上传介质
修改权限
[root@dmdsc0 dmhs]# chown dmhs.dinstall /opt/dmhs/setup/dmhs_V4.2.60_dm8_rev106302_rh6_64_veri_20220225.bin
[root@dmdsc0 dmhs]# ls -la /opt/dmhs/setup/
总用量 492416
drwxr-xr-x 2 dmhs dinstall 65 4月 25 08:15 .
drwxr-xr-x 5 dmhs dinstall 42 4月 25 08:14 ..
-rw-r--r-- 1 dmhs dinstall 504233086 4月 25 08:15 dmhs_V4.2.60_dm8_rev106302_rh6_64_veri_20220225.bin
需要用到WEB页面就完整版,基本同步功能可以精简版,安装目录按规划调整
远程代理,实际上是一个位于本机的agent,提供了对外web服务,可以在不ssh到这台机器情况下,通过web访问进行配置文件的编写和实现简单的状态监控,后台使用一个只有本地能访问的单实例数据库
关闭防火墙
[root@myOracle01 ~]# systemctl stop firewalld
[root@myOracle01 ~]# systemctl disable firewalld
Removed symlink /etc/systemd/system/multi-user.target.wants/firewalld.service.
Removed symlink /etc/systemd/system/dbus-org.fedoraproject.FirewallD1.service.
日志配置
确认已经打开binlog,未打开则做相应配置
binlog_format = row
log-bin = /opt/mysql/log/mysql-bin
expire_logs_days = 5
创建测试库
MySQL为单实例多库架构,需要用库进行对应
mysql> create database dmhs;
Query OK, 1 row affected (0.01 sec)
确认字符集
mysql> show create database dmhs;
+----------+-----------------------------------------------------------------+
| Database | Create Database |
+----------+-----------------------------------------------------------------+
| testdb | CREATE DATABASE `dmhs` /*!40100 DEFAULT CHARACTER SET utf8 */ |
+----------+-----------------------------------------------------------------+
1 row in set (0.00 sec)
创建复制用户
mysql> create user 'dmhs'@'%' identified by '123456789';
Query OK, 0 rows affected (0.00 sec)
mysql> grant all on *.* to 'dmhs'@'%';
Query OK, 0 rows affected (0.01 sec)
mysql> flush privileges;
Query OK, 0 rows affected (0.01 sec)
禁用约束/触发器
可以通过环境变量来临时关闭,但可能无法全局生效,与ogg相同直接生成脚本对表关闭更为稳妥
mysql> alter table dmhs.test disable keys;
Query OK, 0 rows affected, 1 warning (0.00 sec)
MySQL并不提供关闭触发器的方法,所以要么在开发时加入全局变量检测来判断,要么将触发器语句导出后删除
ODBC配置
[root@tpcc Packages]# rpm -ivh unixODBC-2.3.1-11.el7.x86_64.rpm
警告:unixODBC-2.3.1-11.el7.x86_64.rpm: 头V3 RSA/SHA256 Signature, 密钥 ID fd431d51: NOKEY
准备中... ################################# [100%]
正在升级/安装...
1:unixODBC-2.3.1-11.el7 ################################# [100%]
[root@tpcc Packages]# rpm -ivh unixODBC-devel-2.3.1-11.el7.x86_64.rpm
警告:unixODBC-devel-2.3.1-11.el7.x86_64.rpm: 头V3 RSA/SHA256 Signature, 密钥 ID fd431d51: NOKEY
准备中... ################################# [100%]
正在升级/安装...
1:unixODBC-devel-2.3.1-11.el7 ################################# [100%]
[root@tpcc odbc]# rpm -ivh mysql-connector-odbc-5.3.13-1.el7.x86_64.rpm
警告:mysql-connector-odbc-5.3.13-1.el7.x86_64.rpm: 头V3 DSA/SHA1 Signature, 密钥 ID 5072e1f5: NOKEY
准备中... ################################# [100%]
正在升级/安装...
1:mysql-connector-odbc-5.3.13-1.el7################################# [100%]
确认驱动文件
[root@tpcc lib]# ls
libmyodbc5a.so libmyodbc5S.so libmyodbc5w.so
[root@tpcc lib]# pwd
/opt/dmhs/odbc/mysql-connector-odbc-5.3.13-linux-el7-x86-64bit/lib
[root@myOracle01 setup]# unzip instantclient-odbc-linux.x64-12.2.0.1.0-2.zip
配置odbc.ini
[MYSQL]
Description = MYSQL ODBC DSN
Driver = MySQL ODBC 5.3 ANSI Driver
SERVER = 192.168.56.188
PORT = 3306
USER = dmhs
Password = 123456789
Database = dmhs
OPTION = 3
CHARSET = UTF8
配置odbcinst.ini
[MySQL ODBC 5.3 Unicode Driver]
Driver=/usr/lib64/libmyodbc5w.so
UsageCount=1
[MySQL ODBC 5.3 ANSI Driver]
Driver=/usr/lib64/libmyodbc5a.so
UsageCount=1
测试连通性
[root@tpcc odbc]# isql -v MYSQL
+---------------------------------------+
| Connected! |
| |
| sql-statement |
| help [tablename] |
| quit |
| |
+---------------------------------------+
SQL> select 1 from dual;
+---------------------+
| 1 |
+---------------------+
| 1 |
+---------------------+
SQLRowCount returns 1
1 rows fetched
实际上DMHS连接MYSQL是通过DSN-LESS方式,所以odbc.ini仅为了isql测试使用
创建操作系统用户,DM8侧要加到mysql组
[root@tpcc lib]# useradd -g mysql dmhs -m
[root@tpcc lib]# echo "123456"|passwd dmhs --stdin
更改用户 dmhs 的密码 。
passwd:所有的身份验证令牌已经成功更新。
添加用户环境变量(包含mysql的部分)
PATH=$PATH:$HOME/.local/bin:$HOME/bin
export PATH
export DMHS_HOME=/opt/dmhs/app
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/dmhs/app/bin
export DMHS_HOME=/opt/dmhs/app
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/dmhs/app/bin
export PATH=$DMHS_HOME/bin:$PATH
创建目录
[root@tpcc lib]# vi ~dmhs/.bash_profile
[root@tpcc lib]# mkdir -p /opt/dmhs/setup
[root@tpcc lib]# mkdir -p /opt/dmhs/app
[root@tpcc lib]# mkdir -p /opt/dmhs/data
[root@tpcc lib]# mkdir -p /opt/dmhs/inst1
[root@tpcc lib]# chown -R dmhs.dinstall /opt/dmhs
上传介质,修改权限
[root@tpcc setup]# chown dmhs.mysql /opt/dmhs/setup/dmhs_V4.2.60_mysql_rev106302_rh6_64_veri_20220225.bin
[root@tpcc setup]# ls -la
总用量 490384
drwxr-xr-x 2 dmhs dinstall 67 4月 25 15:24 .
drwxr-xr-x 7 dmhs dinstall 67 4月 25 15:23 ..
-rw-r--r-- 1 dmhs mysql 502149695 4月 25 15:24 dmhs_V4.2.60_mysql_rev106302_rh6_64_veri_20220225.bin
[dmhs @ tpcc setup]$ chmod +x dmhs_V4.2.60_oracle12_rev106302_rh6_64_veri_20220225.bin -i
[dmhs@tpcc setup]$ ./dmhs_V4.2.60_mysql_rev106302_rh6_64_veri_20220225.bin -i
Extract install files.........
1.英文(English)
2.简体中文(简体中文)
请选择安装语言[2.简体中文(简体中文)]:
/tmp/DMHSInstall/install.log
1.免费试用达梦数据实时同步
2.使用已申请的Key文件
验证许可证文件[1.免费试用达梦数据实时同步]:
1.精简版
2.完整版(web客户端)
3.自定义
安装类型[1.精简版]:2
1.实时同步软件服务器
2.远程部署工具
3.实时同步软件客户端
4.内置数据库
5.实时同步软件配置助手
6.手册
所需磁盘空间:891 MB
安装目录: [/home/dmhs/dmhs]/opt/dmhs/app
该路径不为空,是否继续安装?[Y or N]Y
安装路径可能存在覆盖安装
1.统一部署
2.现在初始化
是否初始化达梦数据实时同步系统[1.统一部署]:
正在安装
default start ... default finished.
server start ... server finished.
hs_agent start ... hs_agent finished.
webmanager start ... webmanager finished.
db start ... db finished.
hsca start ... hsca finished.
doc start ... doc finished.
doc start ... doc finished.
postinstall start ... postinstall finished.
正在创建快捷方式
安装成功
远程部署工具配置
远程部署工具名称[HsAgent]:
主机Ip(外网)[192.168.122.1](192.168.122.1,192.168.88.154,10.30.5.188,192.168.56.188):192.168.56.188
远程部署工具管理端口[5456](1000-65535):
内置数据库轮询间隔[3](1-60):
内置数据库IP[192.168.122.1]:
内置数据库端口[15236]:
内置数据库用户名[SYSDBA]:
内置数据库密码[SYSDBA]:
服务脚本环境变量设置
依赖库路径
提示:此配置项供用户配置源或目的数据库依赖库路径和odbc依赖库路径, 多个路径以":"隔开(例:/opt/dmdbms/bin:/usr/local/lib),此配置项会添加到服务脚本的NEED_LIB_PATH的变量值中。
请配置依赖库路径:/opt/dmhs/odbc/mysql-connector-odbc-5.3.13-linux-el7-x86-64bit/lib:/usr/local/lib
内置数据库服务
1.自动
2.手动
启动方式:[2.手动]
正在创建内置数据库服务
初始化内置数据库
远程控制服务
1.自动
2.手动
启动方式:[2.手动]
正在创建远程控制服务
web服务
1.自动
2.手动
启动方式:[2.手动]
正在创建web服务
达梦数据实时同步V4.0安装完成
更多安装信息,请查看安装日志文件:
/opt/dmhs/app/log/install.log
配置文件与OGG相似,功能上是MGR/CPT/DP/RPT组成,区别在于其结构以XML的方式进行包裹,其中引入了相应的NET类功能进行传输控制,具体这里不展开细说,本次配置为非落地所以不存在TRANSFER(DP)模块
对于DMHS不支持的对象,如SEQUENCE/PKG/VIEW等(详见文章开头),有实际需求可以借助DTS进行迁移,但由于其不涉及DMHS复制本身的工作流程,在此不展开讨论,仅展示对象统计的方法,以便外部工具迁移后核对数量,详细名称比对应当通过迁移工具进行
DM8侧
统计对象数量
SELECT OBJECT_TYPE,COUNT(*) FROM ALL_OBJECTS WHERE OWNER='[USERNAME]' GROUP BY OBJECT_TYPE;
MySQL侧
统计对象数量
SELECT 'TABLE',COUNT(*) AS NUM FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'dmhs' group by TABLE_SCHEMA
union all
SELECT 'VIEW' ,COUNT(*) FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_SCHEMA = 'dmhs' group by TABLE_SCHEMA
union all
SELECT 'CONSTRAINT' CONSTRAINT_SCHEMA ,COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA = 'dmhs' GROUP BY CONSTRAINT_SCHEMA
union all
SELECT 'TRIGGER' ,COUNT(*) FROM INFORMATION_SCHEMA.TRIGGERS WHERE TRIGGER_SCHEMA = 'dmhs' GROUP BY TRIGGER_SCHEMA
union all
select 'FUNCTION',count(*) from routines where ROUTINE_SCHEMA='dmhs' and routine_type='FUNCTION' group by routine_schema
union all
select 'PROCEDURE' ,count(*) from routines where ROUTINE_SCHEMA='dmhs' and routine_type='PROCEDURE ' group by routine_schema;
除了通用的MGR部分外,双向复制中同时包含了EXEC和CPT两个模块,其中EXEC被RECV包裹,CPT则包裹SEND子模块
en
7345
60
3 //全局唯一
// RECV要在EXEC上层
7346
0 //双向确保LEVEL为0
MYSQL
192.168.56.188
dmhs
123456789
3306
dmhs
PG_UTF8
MySQL ODBC 5.3 ANSI Driver //驱动名根据实际配置
1
1
32
1000
20
0
2
8000
MYSQL
192.168.56.188
dmhs
123456789
3306
dmhs
1
PARSE:POST
op:obj
PG_UTF8
MySQL ODBC 5.3 ANSI Driver //驱动名根据实际配置
600
2
/opt/mysql/log //指向binlog位置
192.168.56.7
7345
7346
1
1
1
- dmhs.*
//MYSQL侧小写,不要过滤掉DMHS_TRXID_TABLE
en //程序输出固定GBK,可以从console适应,但en更通用
7345
60
1 //全局唯一
//RECV必须在EXEC外层
7346
0 //双向确保LEVEL为0
DM8
127.0.0.1
DMHS
123456789
6236
PG_UTF8
1
1
32
1000
20
0
2
8000
DM8
127.0.0.1
DMHS
123456789
6236
1
op:obj
600
2
/opt/dsc/arch //指向本地归档位置
192.168.56.188
7345
7346
1
1
1
- DMHS.*
//DM8侧大写,不要过滤掉DMHS_TRXID_TABLE
DM8侧
[dmhs@dmdsc0 db]$ dmhs_server /opt/dmhs/inst1/dmhs_init.hs
MGR[INFO]: DMHS start up, current version: V4.2.60-Build(2022.02.24-106302trunc)_D64 (The beta)(Enterprise Edition)
MGR[WARN]: License will expire on 2022-05-24
MGR[INFO]: load config file successful,site no:10, manager port :9345, poll interval:3
MGR[INFO]: manager listening port:9345
MySQL侧
[dmhs@tpcc bin]$ dmhs_server /opt/dmhs/inst1/dmhs_init.hs
MGR[INFO]: DMHS start up, current version: V4.2.60-Build(2022.02.24-106302trunc)_D64 (The beta)(Enterprise Edition)
MGR[WARN]: License will expire on 2022-05-24
MGR[INFO]: load config file successful,site no:9, manager port :7345, poll interval:3
MGR[WARN]: site not config exec module !
MGR[INFO]: manager listening port:7345
DMHS后续加载和初始化步骤是在线进行,所以必须要先将EXEC端启动,否则会持续挂起
DM8侧
[dmhs@tpcc ~]$ dmhs_console /opt/dmhs/inst1/dmhs_init2.hs
DMHS console tool: V4.2.60-Build(2022.02.24-106302trunc)_D64
Copyright (c) 2020, DMHS. All rights reserved.
Type ? or "help" for help, type "quit" to quit console.
DMHS> connect 127.0.0.1:7345
execute success
DMHS> start exec
execute success
MySQL侧
[dmhs@tpcc ~]$ dmhs_console /opt/dmhs/inst1/dmhs_init2.hs
DMHS console tool: V4.2.60-Build(2022.02.24-106302trunc)_D64
Copyright (c) 2020, DMHS. All rights reserved.
Type ? or "help" for help, type "quit" to quit console.
DMHS> connect 127.0.0.1:7345
execute success
DMHS> start exec
execute success
加载离线字典的同时会完成目标端辅助表的创建,这是双向同步的重要基础,与OGG的exclude user方式不同,DMHS通过DMHS_TRXID_TABLE来进行事务过滤,避免其循环执行
DM8侧
DMHS> COPY 0 "sch.name='DMHS'" DICT
copy mask is : |DICT|PARTITION|REP
execute finish, please look up log file of exec module to check data load result
MySQL侧
DMHS> COPY 0 "sch.name='dmhs'" DICT
copy mask is : |DICT|PARTITION|REP
execute finish, please look up log file of exec module to check data load result
后台日志会提示加载表信息
成功之后EXEC端会创建RECV线程
如果存在不重叠的数据,则要从希望作为基准的一侧加载。如果存在重叠部分则需要停止应用确定从某一侧加载,具体可以根据表来划分,语句中的大小写要按库来,比如mysql测小写(因为其验证对象过程是从infomation_schema.tables来查询,具体可以打开general_log看),DM8侧大写,根据从哪一侧发起调整,另外关键字可以根据需求调整,比如是直接带上INDEX还是之后自己建立,以及是否要记录此刻的LSN还是从0开始分析
DM8侧
DMHS> COPY 0 "sch.name='DMHS'" LSN|CREATE|INSERT|UPDATE|BLOB|INDEX|THREAD|2
CSL[WARN]: Detect the LSN mask, the mask will be ignored in the log is less than the current LSN all operations, please confirm whether to continue?(Y/N)
Y
copy mask is : |CREATE|INSERT|UPDATE|BLOB|THREAD|INDEX|TABLE|LSN|PARTITION|OBJID|REP
MySQL侧
DMHS> COPY 0 "sch.name='dmhs'" LSN|CREATE|INSERT|UPDATE|BLOB|INDEX|THREAD|2
CSL[WARN]: Detect the LSN mask, the mask will be ignored in the log is less than the current LSN all operations, please confirm whether to continue?(Y/N)
Y
copy mask is : |CREATE|INSERT|UPDATE|BLOB|THREAD|INDEX|TABLE|LSN|PARTITION|OBJID|REP
发起端日志可以看到具体加载了多少个表
目标端也可以看到初始同步表行数和相关信息
特殊装载(增量)
DMHS中有两种LSN,分别为起始LSN和提交LSN,分别用于CPT分析位置的确定和判断EXEC端是否执行该语句,CPT启动时会先从目标端获取LSN,再从dmhs.conf中获取LSN,取较大者作为起始LSN,在装载时候如果带上LSN关键字,则会将这一时刻源端库查询到的LSN设置为CPT起始LSN,以便完成后从这一时间点继续抓取后续操作,这一特性可以用于表的修复等操作中,例如当某个表存在问题需要重新构造时候就可以
1.源端锁定使其成为静态,或通过其他方式保证静态
2.数据通过DTS或其他工具完成同步
3.验证源端和目标端数据
4.停止CPT
5.LOAD 0 “sch.name=‘DMHS’ AND tab.name=‘XXXXX’” CLEAR|DICT|LSN
6.启动CPT
7.解锁源端表,验证同步
也可以在dbms.conf中通过start_lsn=() 或cpt_lsn=() 来指定CPT的起始LSN
特殊装载(LOB)
对于包含LOB的表,默认会一起装载,由于增加了句柄的操作,使其无法发挥并行效率。所以可以将其分离为NOBLOB和BLOB两个步骤来完成,等价于EMPTY_BLOB和UPDATE_BLOB
DMHS> COPY 0 "sch.name='dmhs' and tab.name='t100'" LSN|CREATE|INSERT|NOBLOB|INDEX|THREAD|2
CSL[WARN]: Detect the LSN mask, the mask will be ignored in the log is less than the current LSN all operations, please confirm whether to continue?(Y/N)
Y
copy mask is : |CREATE|INSERT|NOBLOB|THREAD|INDEX|TABLE|LSN|PARTITION|OBJID|REP
execute finish, please look up log file of exec module to check data load result
UPDATE时还可以通过WHERE条件分批进行LOB的UPDATE操作,也可以不指定一次性进行,目前MySQL侧尚存在一些问题,应当以INSERT|BLOB方式进行,此处仅作展示,后续版本可能会支持
DMHS> LOAD 0 "sch.name='dmhs' and tab.name='t100'" UPDATE|BLOB|"id=1"
copy mask is : |WHERE|UPDATE|BLOB|TABLE|PARTITION|REP
命令与start exec操作相同,只是换成start cpt
成功启动后可以观察到两台机器上CPT和EXEC的状态
这里因为一些问题的排查,我将之前初始化表删除,从空库开始验证
MYSQL建表
mysql> create table dmhs.t99(id int,name varchar(100),dt datetime(6));
Query OK, 0 rows affected (0.00 sec)
DM8验证
SQL> select * from dmhs."t99";
未选定行
DM8插入
SQL> SQL> insert into DMHS."t99"("id","name","dt") values(0,'kabasdf',now());
影响行数 1
已用时间: 0.766(毫秒). 执行号:317219.
SQL> commit;
操作已执行
MYSQL验证
mysql> select * from dmhs.t99;
+------+---------+----------------------------+
| id | name | dt |
+------+---------+----------------------------+
| 0 | kabasdf | 2022-04-26 13:36:44.756768 |
+------+---------+----------------------------+
1 row in set (0.00 sec)
MYSQL修改
mysql> update dmhs.t99 set name='superman' where id=0;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
DM8验证
SQL> select * from DMHS."t99";
行号 id name dt
---------- -------------------- -------- ---------------------------
1 0 superman 2022-04-26 13:36:44.756768
已用时间: 0.623(毫秒). 执行号:317221.
DM8修改
SQL> update DMHS."t99" set "name"='burgerman' where "id"=0;
影响行数 1
已用时间: 2.207(毫秒). 执行号:317222.
SQL> commit;
操作已执行
已用时间: 1.793(毫秒). 执行号:317223.
MYSQL验证
mysql> select * from dmhs.t99;
+------+-----------+----------------------------+
| id | name | dt |
+------+-----------+----------------------------+
| 0 | burgerman | 2022-04-26 13:36:44.756768 |
+------+-----------+----------------------------+
1 row in set (0.00 sec)
MYSQL添加二级索引
mysql> create index test_idx on dmhs.t99(name);
Query OK, 0 rows affected (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 0
DM8验证
SQL> select INDEX_NAME from dba_indexes where table_name='t99' and owner='DMHS' and INDEX_TYPE<>'CLUSTER';
行号 INDEX_NAME
---------- ------------
1 test_idx_t99
DM8添加
SQL> alter table dmhs."t99" add constraint cons_pk primary key("id");
操作已执行
已用时间: 53.468(毫秒). 执行号:317214.
MYSQL验证
mysql> desc dmhs.t99;
+-------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+-------+
| id | int(11) | NO | PRI | NULL | |
| name | varchar(100) | YES | MUL | NULL | |
| dt | datetime(6) | YES | | NULL | |
+-------+--------------+------+-----+---------+-------+
3 rows in set (0.00 sec)
MYSQL删除
mysql> drop table dmhs.t99;
Query OK, 0 rows affected (0.00 sec)
DM8验证
SQL> select * from DMHS."t99";
select * from DMHS."t99";
第1 行附近出现错误[-2106]:无效的表或视图名[t99].
已用时间: 0.341(毫秒). 执行号:0.
确认功能正常后可以将dmhs_server注册为服务交由操作系统管理
这里仅以MySQL端展示操作
[root@tpcc ~]# cd /opt/dmhs/app/scripts/root/
[root@tpcc root]# sh dmhs_service_installer.sh -t dmhs_server -d /opt/dmhs/app/bin -x /opt/dmhs/inst1/dmhs_init.hs -p EXEC
Created symlink from /etc/systemd/system/multi-user.target.wants/DmhsServiceEXEC.service to /usr/lib/systemd/system/DmhsServiceEXEC.service.
创建服务(DmhsServiceEXEC)完成
默认情况下XML中的LANG配置为zh,但其编码为GB2312,在Linux默认UTF8的终端上较为不便,处理方法有两种,一是终端和操作系统LANG统一为GB2312,而是将LANG改为en使其按英文输出
新装环境可能会遇到类似下面的这种库缺失问题
2022-04-24 21:18:57 MGR[INFO]: 来自 127.0.0.1(dmhs_console) 的连接已经断开!
2022-04-24 21:20:12 MGR[INFO]: 监控到来自 127.0.0.1(dmhs_console) 的登录
2022-04-24 21:20:18 MGR[INFO]: 来自 127.0.0.1(dmhs_console) 的连接已经断开!
2022-04-24 21:20:19 MGR[INFO]: 监控到来自 127.0.0.1(dmhs_console) 的登录
2022-04-24 21:20:36 MGR[INFO]: 正在初始化分析模块...
2022-04-24 21:20:36 PUB[INFO]: set enable_directio = 0
2022-04-24 21:20:36 MGR[INFO]: 正在加载DM8日志分析模块..
2022-04-24 21:20:36 MGR[ERROR]: 库文件 libcpt_dm8.so 未找到, 出错: 0, libdmoci.so: cannot open shared object file: No such file or directory
2022-04-24 21:20:36 MGR[INFO]: 日志分析模块加载失败!
2022-04-24 21:20:58 MGR[INFO]: 来自 127.0.0.1(dmhs_console) 的连接已经断开!
可以确认一下具体缺少的依赖库,如果是系统库则需要进行补齐
[dmdba@dmdsc0 DMSERVER]$ ldd libcpt_dm8.so
linux-vdso.so.1 => (0x00007ffc8917d000)
libc.so.6 => /lib64/libc.so.6 (0x00007f32c9f7b000)
libm.so.6 => /lib64/libm.so.6 (0x00007f32c9c79000)
librt.so.1 => /lib64/librt.so.1 (0x00007f32c9a71000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f32c9855000)
libdl.so.2 => /lib64/libdl.so.2 (0x00007f32c9651000)
libdmhs_pub.so => ./libdmhs_pub.so (0x00007f32c931a000)
libdmhs_net.so => ./libdmhs_net.so (0x00007f32c90bb000)
libdmhs_ld_dm8.so => ./libdmhs_ld_dm8.so (0x00007f32c8e51000)
libdmhs_ucvt.so => ./libdmhs_ucvt.so (0x00007f32c88f6000)
/lib64/ld-linux-x86-64.so.2 (0x00007f32ca651000)
libdmoci.so => not found
libdmhs_bool_parse.so => ./libdmhs_bool_parse.so (0x00007f32c86e6000)
libdmoci.so => not found
如果是dmhs自身的库,则可以通过添加全局库路径来解决,但由于其依赖可能有下层,所以我个人的处理方法是借鉴了其服务注册脚本的思路,进入到/opt/dmhs/app/db/bin路径中调用命令即可
未正确打开附加日志则会看到如下错误,具体需要结合数据库层面排查附加日志
2022-04-24 21:31:22 MGR[INFO]: 监控到来自 127.0.0.1(dmhs_console) 的登录
2022-04-24 21:31:24 MGR[INFO]: 正在初始化分析模块...
2022-04-24 21:31:24 PUB[INFO]: set enable_directio = 0
2022-04-24 21:31:24 MGR[INFO]: 正在加载DM8日志分析模块...
2022-04-24 21:31:24 CPT[INFO]: DM8_V3.1.2_D64
2022-04-24 21:31:24 CPT[INFO]: 发送队列长度(send_lst)为:3
2022-04-24 21:31:24 CPT[INFO]: DM8是否为UNICODE库: TRUE
2022-04-24 21:31:24 CPT[INFO]: DM8 parameter LENGTH_IN_CHAR: 0
2022-04-24 21:31:24 CPT[ERROR]: DM8未开启逻辑附加日志
2022-04-24 21:31:24 CPT[INFO]: DM8 CPT is free.
2022-04-24 21:31:24 MGR[ERROR]: 初始化日志分析模块失败
2022-04-24 21:31:24 MGR[ERROR]: ID号为1的日志分析模块启动失败!
2022-04-24 21:31:27 MGR[INFO]: 来自 127.0.0.1(dmhs_console) 的连接已经断开!
DMHS对于DDL同步方式只能二选一,目前似乎都是通过脚本创建触发器和辅助表来进行了
2022-04-24 21:33:23 MGR[INFO]: 监控到来自 127.0.0.1(dmhs_console) 的登录
2022-04-24 21:33:26 MGR[INFO]: 正在初始化分析模块...
2022-04-24 21:33:26 PUB[INFO]: set enable_directio = 0
2022-04-24 21:33:26 MGR[INFO]: 正在加载DM8日志分析模块...
2022-04-24 21:33:26 CPT[INFO]: DM8_V3.1.2_D64
2022-04-24 21:33:26 CPT[INFO]: 发送队列长度(send_lst)为:3
2022-04-24 21:33:26 CPT[INFO]: DM8是否为UNICODE库: TRUE
2022-04-24 21:33:26 CPT[INFO]: DM8 parameter LENGTH_IN_CHAR: 0
2022-04-24 21:33:26 CPT[INFO]: DDL同步方式:使用DM8系统表逻辑附加日志方式同步DDL
2022-04-24 21:33:26 CPT[ERROR]: 两种DDL同步方式都存在:dm.ini中RLOG_APPEND_SYSTAB_LOGIC=1,DMHS辅助表和触发器,请二选一。
2022-04-24 21:33:26 CPT[INFO]: DM8 CPT is free.
2022-04-24 21:33:26 MGR[ERROR]: 初始化日志分析模块失败
2022-04-24 21:33:26 MGR[ERROR]: ID号为1的日志分析模块启动失败!
2022-04-24 21:33:28 MGR[INFO]: 来自 127.0.0.1(dmhs_console) 的连接已经断开!
出现下面这种报错,其实际上是找不到libcrypto.so,从前面提到的特定位置启动即可
对于一些代码中未明确捕获并提示的外部调用报错,有测试条件的情况建议通过gdb或者strace跟一下,具体操作比较繁琐,在此简单描述一下思路:
小的模块的可以直接通过strace调用,运气好也许直接找到了外部调用文件,服务类可以通过gdb attach来连接到进程,通过stop将其阻塞,打开disassemble和eax寄存器的显示,设置一个断点后通过单步调用找到前台报错时其eax异常的位置并审查前后调用进行下层排查。
例如我这里在call mgr_start之后就出现问题了,所以可以依次递归将断点向下层转移,直到找到外部调用的位置,跟踪其寄存器数据来找到可能的问题点
(gdb)
13446 in /opt/qza/src/mgr/mgr.c
=> 0x000000000042d662: 83 7d b8 7a cmpl $0x7a,-0x48(%rbp)
0x000000000042d666: 0f 87 d8 08 00 00 ja 0x42df44
0x000000000042d66c: 8b 45 b8 mov -0x48(%rbp),%eax
0x000000000042d66f: 48 8d 14 85 00 00 00 00 lea 0x0(,%rax,4),%rdx
0x000000000042d677: 48 8d 05 5e f4 04 00 lea 0x4f45e(%rip),%rax # 0x47cadc
0x000000000042d67e: 8b 04 02 mov (%rdx,%rax,1),%eax
0x000000000042d681: 48 63 d0 movslq %eax,%rdx
0x000000000042d684: 48 8d 05 51 f4 04 00 lea 0x4f451(%rip),%rax # 0x47cadc
0x000000000042d68b: 48 8d 04 02 lea (%rdx,%rax,1),%rax
0x000000000042d68f: ff e0 jmpq *%rax
2: $eax = 932718576
1: $eax = 932718576
(gdb)
13459 in /opt/qza/src/mgr/mgr.c
=> 0x000000000042d6f8: 48 8b 45 b0 mov -0x50(%rbp),%rax
0x000000000042d6fc: 48 8b 00 mov (%rax),%rax
0x000000000042d6ff: 48 89 c7 mov %rax,%rdi
0x000000000042d702: e8 5a ae fe ff callq 0x418561
2: $eax = 4380408
1: $eax = 4380408
(gdb)
13461 in /opt/qza/src/mgr/mgr.c
=> 0x000000000042d707: e9 7c 08 00 00 jmpq 0x42df88
2: $eax = -2100
1: $eax = -2100
…
递归查找后最终会找到其外部调用真正错误的地方,此时寄存器信息就是传递给调用的参数
(gdb) n
597 in /opt/qza/src/mgr/mgr.c
=> 0x000000000040ab21: 48 8d 85 d0 fa ff ff lea -0x530(%rbp),%rax
0x000000000040ab28: be 02 00 00 00 mov $0x2,%esi
0x000000000040ab2d: 48 89 c7 mov %rax,%rdi
0x000000000040ab30: e8 3b d9 ff ff callq 0x408470 dlopen@plt
0x000000000040ab35: 48 8b 15 f4 88 28 00 mov 0x2888f4(%rip),%rdx # 0x693430
0x000000000040ab3c: 48 89 02 mov %rax,(%rdx)
(gdb) x /s $rdi
0x7fff4cb22e40: “libcrypto.so”
在这一步之后变回进入错误处理流程,同时前台也会返回错误信息
600 in /opt/qza/src/mgr/mgr.c
=> 0x000000000040ab4e: e8 cd d0 ff ff callq 0x407c20 dlerror@plt
0x000000000040ab53: 49 89 c4 mov %rax,%r12
0x000000000040ab56: e8 f5 d5 ff ff callq 0x408150 __errno_location@plt
0x000000000040ab5b: 8b 18 mov (%rax),%ebx
0x000000000040ab5d: b8 00 00 00 00 mov $0x0,%eax
0x000000000040ab62: e8 69 da ff ff callq 0x4085d0 dmhs_get_lang@plt
0x000000000040ab67: 85 c0 test %eax,%eax
0x000000000040ab69: 74 09 je 0x40ab74
0x000000000040ab6b: 48 8d 05 c6 cf 06 00 lea 0x6cfc6(%rip),%rax # 0x477b38
0x000000000040ab72: eb 07 jmp 0x40ab7b
0x000000000040ab74: 48 8d 05 dd cf 06 00 lea 0x6cfdd(%rip),%rax # 0x477b58
0x000000000040ab7b: 48 8b 15 fe 86 28 00 mov 0x2886fe(%rip),%rdx # 0x693280
0x000000000040ab82: 8b 12 mov (%rdx),%edx
0x000000000040ab84: c1 e2 08 shl $0x8,%edx
0x000000000040ab87: 89 d7 mov %edx,%edi
0x000000000040ab89: 81 cf 00 00 00 83 or $0x83000000,%edi
0x000000000040ab8f: 48 8d 95 e0 fe ff ff lea -0x120(%rbp),%rdx
0x000000000040ab96: 4d 89 e0 mov %r12,%r8
0x000000000040ab99: 89 d9 mov %ebx,%ecx
0x000000000040ab9b: 48 89 c6 mov %rax,%rsi
0x000000000040ab9e: b8 00 00 00 00 mov $0x0,%eax
0x000000000040aba3: e8 68 cd ff ff callq 0x407910 dmhs_elog_report_ex@plt
对于大小写敏感的库需要尤其注意,例如下面这个例子中"dmhs"与库中的DMHS并不是同一个schema,将会导致2103错误,即无效SCHEMA
需要调整cpt测的MAP,把dmhs映射到DMHS上
- dmhs.*==DMHS.*
对于一些错误语句,可能会执行不通过造成一直循环,此时可以将其执行位置向前推进来跳过
DMHS> SET EXEC LSN 0
execute success
对于DELETE和UPDATE在操作时会对其进行转化,以拆分成多个子句,当有PK时会记录PK和ROWID ROWNUM,当没有PK时会带入所有字段和ROWID确保唯一性,当下面这种情况时候就会出问题
MySQL表中含有datetime类型,将会默认被映射为DM8 timestamp(6),此后如果从DM8侧插入数据则会在复制时出现数据截断
此后从MySQL测进行该语句的UPDATE/DELETE则会出现问题
其条件将无法再匹配DM8侧的数据,造成异常
加上主键后,记录为主键和ROWID,ROWNUM即可正常
此外DMHS目前不支持default now(6)和default current_timestamp(6),应当将其拆分为创建和插入明确数据两个步骤来规避
CPT[WARN]: sql state : [create table dmhs.t99(id int,name varchar(100),dt datetime(6) default now(6))] not support
CPT[WARN]: sql state : [create table dmhs.t99(id int,name varchar(100),dt datetime(6) default current_timestamp(6))] not support
本次通过DMHS搭建DM8到MySQL双向同步进行了最简单架构的搭建及功能测试,实际生产环境要复杂的多,应当进一步考虑其架构的容错性及对原生产环境的入侵性等问题。
达梦云适配技术社区
https://eco.dameng.com/