一、背景
在金融、电商等关键业务领域,数据的准确性与实时性是保障业务稳定运行和决策精准的核心要素。哪怕是瞬间的数据延迟或错误,都可能在这些领域引发严重的连锁反应,造成巨大的经济损失和信誉损害。因此,确保数据库之间的高效同步成为了技术架构中的关键环节。
Oracle GoldenGate(OGG)工具,作为数据同步与集成领域的佼佼者,能够为企业提供强大的数据容灾和迁移解决方案。借助OGG,不仅可以实现数据的实时复制,保障业务连续性,还能在系统升级、架构调整时,平稳地完成数据迁移工作,最大程度降低对业务的影响。
接下来,将为大家详细演示如何运用OGG工具实现数据库之间的同步,助力大家深入理解并掌握这一关键技术。 好多人也利用这种技术完成了数据库的容灾和迁移等工作。
二、OGG原理
它是基于日志捕获技术实现数据实时同步,属于逻辑同步。OGG同步过程大致为源端OGG负责抽取源端数据库变化数据,并将变化数据投递至目标端OGG,目标端OGG负责解析并持久化到目标数据库,OGG进程不多,只有5个进程,影性进程一个(Collector),其他四个都是可见的进程,如下:
数据同步从宏观上分为源端和目的端,也就是source和target。
大家也可以在操作系统系统上查看这几个进程。
[root ~]# ps -ef|grep ogg
1、各进程详细说明:
1)Manager进程
不管是源端还是目标端必须并且只能有一个Manager进程,可以启动、关闭、监控其他进程的健康状态,报告错误事件、分配数据存储空间,发布阀值报告等。
2)Extract 进程
运行在数据库源端,负责从源端数据表或日志中捕获数据,然后捕获到的将写到本地trail文件。 想要复制的对象数据发生改变时,Extract进程 就会进行捕捉,当事务提交时,所有和该事务相关的日志记录被以事务为单元顺序的记录到trai文件中。Extract进程利用其内在的checkpoint机制,周期性的记录其读写的位置,实现断点同步。
3)Pump进程(可选)
运行在数据库源端 ,其作用是将源端产生的本地trail文件,把trai以数据块的形式通过TCP/IP 协议发送到目标。但是如果如果不使用trail文件,那么extract进程在抽取完数据以后,直接投递到目标端,生成远程trail文件,那么这时候,Pump进程就可以不用配置。
4)Replicat进程
运行在数据库目标端,负责读取源端传送到目标端的trail文件中的内容,并将其解析为DML或 DDL语句,然后应用到目标数据库中。
5)Server Collector进程
运行于目标端,作用就是把Extract/Pump进程投递过来的数据块重新组装成Trail文件。运行期间无需任何配置。是一个完全自动的进程。
2、详细图解:
三、环境准备
(一)准备环境
节点 | 操作系统版本 | 数据库版本 | OGG版本 | IP地址 |
---|---|---|---|---|
源端 | centos7.9 | mysql 8037 | 12.2.0.2.0 | 192.168.59.141 |
目的端 | centos7.9 | mysql 8037 | 12.2.0.2.0 | 192.168.59.198 |
这里使用一键安装脚本安装数据库。
链接:https://mp.weixin.qq.com/s/I_7lcbfyjnx6R4AnyQ76lw
安装包链接如下:
通过网盘分享的文件:OGG-MySQL 链接:
https://pan.baidu.com/s/12mFztIk5f7PGMEDn6bJ0wQ
提取码: fe8d
关闭防火墙(两台都操作)
[root@localhost ~]# systemctl stop firewalld
[root@localhost ~]# systemctl disable firewalld
[root@localhost ~]# setenforce 0
(二)下载ogg安装包
官网地址:https://edelivery.oracle.com/
登录后输入:goldengate for non oracle database,找到相应的版本的安装包即可。
点击zip文件下载即可。
上传安装包至服务器
(三)部署OGG
1、源端
[root@localhost ~]# mkdir -p /ogg
[root@localhost ~]# cd /ogg/
[root@localhost ogg]# ll
[root@localhost ogg]# tar -xvf ggs_Linux_x64_MySQL_64bit.tar
[root@localhost ogg]# ./ggsci
创建ogg管理用户
GGSCI (localhost.localdomain) 19> edit params ./GLOBALS
MGRSERVNAME GGMGR
~
GGSCI (localhost.localdomain) 2> create subdirs
源端配置
创建数据库用户ogg,该用户用来同步数据:
mysql> grant system_user on *.* to 'root'@'%';
mysql> create user ogg@'%' identified by '123456';
mysql> grant all privileges on *.* to ogg@'%';
mysql> flush privileges;
mysql>ALTER USER 'ogg'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
mysql>FLUSH PRIVILEGES;
模拟源端的数据
-- 创建 test 数据库
CREATE DATABASE IF NOT EXISTS test;
USE test;
-- 创建表1:示例表,包含整数、字符串、日期等字段
CREATE TABLE table1 (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255),
age INT,
create_date DATE
);
-- 创建表2:示例表,包含浮点数、文本等字段
CREATE TABLE table2 (
id INT AUTO_INCREMENT PRIMARY KEY,
value DECIMAL(10, 2),
description TEXT
);
-- 创建表3:示例表,包含布尔值、时间戳等字段
CREATE TABLE table3 (
id INT AUTO_INCREMENT PRIMARY KEY,
is_active BOOLEAN,
update_time TIMESTAMP
);
-- 创建表4:示例表,包含枚举类型等字段
CREATE TABLE table4 (
id INT AUTO_INCREMENT PRIMARY KEY,
gender ENUM('Male', 'Female'),
email VARCHAR(255)
);
-- 创建存储过程用于插入数据到表1
DELIMITER //
CREATE PROCEDURE insert_data_table1(IN num_records INT)
BEGIN
DECLARE i INT DEFAULT 1;
WHILE i <= num_records DO
INSERT INTO table1 (name, age, create_date)
VALUES (CONCAT('Name_', i), FLOOR(RAND() * 100), CURDATE() - INTERVAL FLOOR(RAND() * 365) DAY);
SET i = i + 1;
END WHILE;
END //
DELIMITER ;
-- 创建存储过程用于插入数据到表2
DELIMITER //
CREATE PROCEDURE insert_data_table2(IN num_records INT)
BEGIN
DECLARE i INT DEFAULT 1;
WHILE i <= num_records DO
INSERT INTO table2 (value, description)
VALUES (RAND() * 100, CONCAT('Description_', i));
SET i = i + 1;
END WHILE;
END //
DELIMITER ;
-- 创建存储过程用于插入数据到表3
DELIMITER //
CREATE PROCEDURE insert_data_table3(IN num_records INT)
BEGIN
DECLARE i INT DEFAULT 1;
WHILE i <= num_records DO
INSERT INTO table3 (is_active, update_time)
VALUES (FLOOR(RAND() * 2), NOW());
SET i = i + 1;
END WHILE;
END //
DELIMITER ;
-- 创建存储过程用于插入数据到表4
DELIMITER //
CREATE PROCEDURE insert_data_table4(IN num_records INT)
BEGIN
DECLARE i INT DEFAULT 1;
WHILE i <= num_records DO
INSERT INTO table4 (gender, email)
VALUES (IF(FLOOR(RAND() * 2) = 0, 'Male', 'Female'), CONCAT('email_', i, '@example.com'));
SET i = i + 1;
END WHILE;
END //
DELIMITER ;
-- 计算大约插入多少条数据能达到1GB(这只是一个估算,实际可能因数据类型和存储方式不同而有所差异)
-- 假设平均每条记录大小为1KB(1024字节),1GB = 1024 * 1024 KB
SET @approx_records = 1024 * 1024;
-- 插入数据到4张表
CALL insert_data_table1(@approx_records / 4);
CALL insert_data_table2(@approx_records / 4);
CALL insert_data_table3(@approx_records / 4);
CALL insert_data_table4(@approx_records / 4);
源端配置管理进程mgr
GGSCI (localhost.localdomain) 4> edit param mgr
新增以下内容:
#指定端口号
port 7809
#绑定可用端口列表
dynamicportlist 7840-7939
#定时清理trail文件
purgeoldextracts /ogg/dirdat/*,usecheckpoints, minkeepdays 1
启动管理进程mgr
#输入启动管理进程命令(关闭:stop mgr )
GGSCI (localhost.localdomain) 5> start mgr
#输入查看状态命令查看是否启动
GGSCI (localhost.localdomain) 6> info all
配置抽取进程Extract
GGSCI (localhost.localdomain) 6> edit param exta
#定义抽取进程名称
extract exta
#指定OGG连接数据库的地址、账户与密码
sourcedb test@192.168.59.141:3306 userid ogg password 123456
#指定写入到本地的哪个队列
exttrail /ogg/dirdat/ea
#定义discardfile文件位置,如果处理中有记录出错会写入到此文件中
discardfile /ogg/dirrpt/exta.dsc,append
#指定在解析数据库日志时所需要的特殊参数
TranLogOptions AltLogDest /data/mysql/mysql-bin.index
#定义需复制的表,后面需以;结尾
table test.*;
GGSCI (localhost.localdomain) 8> add extract exta,tranlog,begin now
GGSCI (localhost.localdomain) 9> add exttrail /ogg/dirdat/ea,extract exta
配置投递进程dmpa
GGSCI (localhost.localdomain) 11> edit param dmpa
#定义投递进程名
extract dmpa
passthru
#源端数据库地址、用户名
sourcedb test@192.168.59.141:3306 userid ogg password 123456
#目标端地址
rmthost 192.168.59.198,mgrport 7809,compress
# 指定写入文件
rmttrail /ogg/dirdat/da
dynamicresolution
numfiles 3000
#投递的表
table test.*;
~
~
GGSCI (localhost.localdomain) 13> add extract dmpa,exttrailsource /ogg/dirdat/ea
GGSCI (localhost.localdomain) 15> add rmttrail /ogg/dirdat/da,extract dmpa
创建表定义文件,传送到目标端
GGSCI (localhost.localdomain) 13> edit param defgen
defsfile ./dirdef/test.def
sourcedb test@192.168.59.141:3306 userid ogg, password 123456
table test.*;
~
~
配置数据结构不一致时的表定义文件
[root@localhost ogg]# ./defgen paramfile ./dirprm/defgen.prm
生成表定义文件(生成成功后复制一份到目标端OGG的dirdef目录下)
[root@localhost ogg]# scp /ogg/dirdef/test.def 192.168.59.198:/ogg/dirdef/
2、目的端
mysql> grant system_user on *.* to 'root'@'%';
mysql> create user ogg@'%' identified by '123456';
mysql> grant all privileges on *.* to ogg@'%';
mysql> flush privileges;
mysql> ALTER USER 'ogg'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
Query OK, 0 rows affected (0.20 sec)
mysql> FLUSH PRIVILEGES;
[root@localhost ~]# mkdir -p /ogg
[root@localhost ~]# cd /ogg/
[root@localhost ogg]# tar -xvf ggs_Linux_x64_MySQL_64bit.tar
[root@localhost ogg]# ls
GGSCI (localhost.localdomain) 2> create subdirs
GGSCI (localhost.localdomain) 8> edit param mgr
port 7809
dynamicportlist 7840-7939
port 7809
dynamicportlist 7840-7939
purgeoldextracts /ogg/dirdat/*,usecheckpoints, minkeepdays 1
~
~
GGSCI (localhost.localdomain) 5> start mgr
GGSCI (localhost.localdomain) 6> info all
配置检查表
在目标端新建库。
mysql> create database test;
mysql> show databases;
mysql> CREATE TABLE `table1` (
-> `id` int NOT NULL AUTO_INCREMENT,
-> `name` varchar(255) DEFAULT NULL,
-> `age` int DEFAULT NULL,
-> `create_date` date DEFAULT NULL,
-> PRIMARY KEY (`id`)
-> ) ENGINE=InnoDB AUTO_INCREMENT=262145 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
Query OK, 0 rows affected (0.12 sec)
mysql> CREATE TABLE `table2` (
-> `id` int NOT NULL AUTO_INCREMENT,
-> `value` decimal(10,2) DEFAULT NULL,
-> `description` text,
-> PRIMARY KEY (`id`)
-> ) ENGINE=InnoDB AUTO_INCREMENT=262145 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
Query OK, 0 rows affected (0.01 sec)
mysql> CREATE TABLE `table3` (
-> `id` int NOT NULL AUTO_INCREMENT,
-> `is_active` tinyint(1) DEFAULT NULL,
-> `update_time` timestamp NULL DEFAULT NULL,
-> PRIMARY KEY (`id`)
-> ) ENGINE=InnoDB AUTO_INCREMENT=262145 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
Query OK, 0 rows affected, 1 warning (0.10 sec)
mysql> CREATE TABLE `table4` (
-> `id` int NOT NULL AUTO_INCREMENT,
-> `gender` enum('Male','Female') DEFAULT NULL,
-> `email` varchar(255) DEFAULT NULL,
-> PRIMARY KEY (`id`)
-> ) ENGINE=InnoDB AUTO_INCREMENT=262145 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
Query OK, 0 rows affected (0.06 sec)
GGSCI (localhost.localdomain) 11> dblogin sourcedb test@192.168.59.198:3306 userid ogg password 123456
GGSCI (localhost.localdomain DBLOGIN as ogg) 12> add checkpointtable test.checkpoint
配置应用进程Replicat
GGSCI (localhost.localdomain DBLOGIN as ogg) 14> edit params repa
#定义应用进程名称
replicat repa
#配置目标端数据库地址、用户名和密码
targetdb test@192.168.59.198:3306 userid ogg password 123456
handlecollisions
#假定两端数据结构不一致,使用此参数指定源端的数据结构定义文件,该文件需要由GlodenGate工具产生
sourcedefs /ogg/dirdef/test.def
#定义discardfile文件位置,如果处理中有记录出错会写入到此文件中,建议参数值1024m
discardfile /ogg/dirrpt/repa.dsc,purge
#用于指定源端与目标端表的映射关系
map test.*, target test.*;
GGSCI (localhost.localdomain DBLOGIN as ogg) 15> add replicat repa,exttrail /ogg/dirdat/da,checkpointtable test.checkpoint
四、开启数据同步
(一)小数据量
在这种情况下,我们可以不用备份数据,直接在目的端新建数据库和表结构即可,就可以开启数据同步了。
1、进入ggsci启动源端抽取进程、投递进程和目标端的应用进程,随后在源端数据库手动插入数据查看目标数据库是否完成了同步
源端:
GGSCI (localhost.localdomain) 22> start exta
GGSCI (localhost.localdomain) 23> start dmpa
目的端:
GGSCI (localhost.localdomain DBLOGIN as ogg) 20> start repa
GGSCI (localhost.localdomain) 28> info all
GGSCI (localhost.localdomain DBLOGIN as ogg) 38> info all
查看日志:
[root@localhost ogg]# tailf /ogg/ggserr.log
测试数据是否同步:
mysql> select count(1) from table1;
新增数据:
在源端新增数据:
– 使用 test 数据库
USE test;
-- 创建存储过程用于插入数据
DELIMITER //
CREATE PROCEDURE insert_data_to_table1()
BEGIN
DECLARE i INT DEFAULT 1;
DECLARE total_records INT DEFAULT 1024 * 1024; -- 大约插入 1GB 数据量的记录数,假设每条记录 1KB
DECLARE start_id INT DEFAULT 262145; -- 基于上一个 ID 262144,这里从 262145 开始
WHILE i <= total_records DO
INSERT INTO table1 (name, age, create_date)
VALUES (
CONCAT('Name_', start_id + i - 1), -- 生成唯一的 name 值
FLOOR(RAND() * 100), -- 生成 0 到 99 之间的随机年龄
CURDATE() - INTERVAL FLOOR(RAND() * 365) DAY -- 生成过去一年中的随机日期
);
SET i = i + 1;
END WHILE;
END //
DELIMITER ;
-- 调用存储过程插入数据
CALL insert_data_to_table1();
我们观察目的端的表数据是否同步,到这里基本就完成数据同步了。
(二)大数据量
如果是之前数据量和库表比较多的情况下,我们可以将源库需要同步的表导出,然后导入到目标数据库。
上面演示了单个库的配置场景,接下来再加一个库,我们来看看如何进行配置。
源端:
GGSCI (localhost.localdomain DBLOGIN as ogg) 51> edit param exta2
extract exta2
sourcedb ceshi@192.168.59.141:3306 userid ogg password 123456
exttrail /ogg/dirdat/ec
discardfile /ogg/dirrpt/exta2.dsc,append
TranLogOptions AltLogDest /data/mysql/mysql-bin.index
table ceshi.*;
注意:这里需要注意的是之前已经有一个库的捕获和传输进程了。这里如果我们要再加一个库的,两个进程名和里面的文件注意一定不要重复了。
源端新建一个库ceshi,并插入数据
-- 创建名为 ceshi 的数据库
mysql> CREATE DATABASE IF NOT EXISTS ceshi;
-- 使用 ceshi 数据库
USE ceshi;
-- 创建表1:员工信息表
CREATE TABLE employee (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
age INT,
department VARCHAR(50)
);
-- 创建表2:订单表
CREATE TABLE orders (
order_id INT AUTO_INCREMENT PRIMARY KEY,
order_number VARCHAR(50) NOT NULL,
customer_name VARCHAR(50),
order_date DATE
);
-- 创建表3:产品表
CREATE TABLE product (
product_id INT AUTO_INCREMENT PRIMARY KEY,
product_name VARCHAR(50) NOT NULL,
price DECIMAL(10, 2),
description TEXT
);
-- 创建插入数据到 employee 表的存储过程
DELIMITER //
CREATE PROCEDURE insert_employee_data()
BEGIN
DECLARE i INT DEFAULT 1;
WHILE i <= 100 DO
INSERT INTO employee (name, age, department)
VALUES (
CONCAT('Employee_', i),
FLOOR(RAND() * 60) + 18, -- 生成 18 到 77 岁之间的随机年龄
CASE
WHEN FLOOR(RAND() * 3) = 0 THEN 'Sales'
WHEN FLOOR(RAND() * 3) = 1 THEN 'Engineering'
ELSE 'Marketing'
END
);
SET i = i + 1;
END WHILE;
END //
DELIMITER ;
-- 创建插入数据到 orders 表的存储过程
DELIMITER //
CREATE PROCEDURE insert_orders_data()
BEGIN
DECLARE i INT DEFAULT 1;
WHILE i <= 100 DO
INSERT INTO orders (order_number, customer_name, order_date)
VALUES (
CONCAT('ORDER_', LPAD(i, 5, '0')),
CONCAT('Customer_', FLOOR(RAND() * 50) + 1),
DATE_ADD(CURDATE(), INTERVAL - FLOOR(RAND() * 365) DAY)
);
SET i = i + 1;
END WHILE;
END //
DELIMITER ;
-- 创建插入数据到 product 表的存储过程
DELIMITER //
CREATE PROCEDURE insert_product_data()
BEGIN
DECLARE i INT DEFAULT 1;
WHILE i <= 100 DO
INSERT INTO product (product_name, price, description)
VALUES (
CONCAT('Product_', i),
ROUND(RAND() * 1000, 2),
CONCAT('This is product ', i, '.')
);
SET i = i + 1;
END WHILE;
END //
DELIMITER ;
-- 调用存储过程插入数据
CALL insert_employee_data();
CALL insert_orders_data();
CALL insert_product_data();
将源库ceshi导出,并传到目标数据库服务器上
[root@localhost ~]# mysqldump -uroot -p'6ThLZGT1mdleYEOh' -h192.168.59.141 --single-transaction --flush-logs --source-data=2 ceshi > ceshi.sql
[root@localhost ~]# scp ceshi.sql root@192.168.59.198:/root/
目的端数据库执行备份数据导入
mysql> create database ceshi;
[root@localhost ~]# mysql -uroot -p'1fXQFNHQX/luUPSQ' ceshi < ceshi.sql
编写dump传输进程
GGSCI (localhost.localdomain DBLOGIN as ogg) 50> edit param dmpa2
extract dmpa2
passthru
sourcedb ceshi@192.168.59.141:3306 userid ogg password 123456
rmthost 192.168.59.198,mgrport 7809,compress
# 指定写入文件
rmttrail /ogg/dirdat/dc
dynamicresolution
numfiles 3000
table ceshi.*;
在源端开启捕获进程
由于数据库数据一直在变化,所以对于导出的ceshi.sql文件,需要记录二进制日志文件的log_file和log_pos。
[root@localhost ~]# cat ceshi.sql |grep "CHANGE MASTER TO MASTER_LOG_FILE"
从导出时的binlog开始抽取
GGSCI (localhost.localdomain) 36> ADD EXTRACT exta2, TRANLOG, BEGIN NOW
GGSCI (masterdb) 5> alter extract exta2,vam,lognum 4,logpos 157
开启dump投递进程
GGSCI (localhost.localdomain DBLOGIN as ogg) 35> add extract dmpa2 ,exttrailsource /ogg/dirdat/ec
GGSCI (localhost.localdomain DBLOGIN as ogg) 36> add rmttrail /ogg/dirdat/dc,extract dmpa2
GGSCI (localhost.localdomain) 42> start dmpa2
GGSCI (localhost.localdomain DBLOGIN as ogg) 52> info all
开启交付进程(目的端)
登录数据库
GGSCI (localhost.localdomain DBLOGIN as ogg) 39> dblogin sourcedb ceshi@192.168.59.198:3306 userid ogg password 123456
GGSCI (localhost.localdomain DBLOGIN as ogg) 40> add checkpointtable ceshi.checkpoint
编辑GLOBALS参数文件
GGSCI (localhost.localdomain DBLOGIN as ogg) 53> edit params ./GLOBALS
checkpointtable test.checkpoint
checkpointtable ceshi.checkpoint
编写交付进程
GGSCI (localhost.localdomain DBLOGIN as ogg) 41> edit params repa2
#定义应用进程名称
replicat repa2
targetdb ceshi@192.168.59.198:3306 userid ogg password 123456
handlecollisions
sourcedefs /ogg/dirdef/ceshi.def
discardfile /ogg/dirrpt/repa2.dsc,purge
map ceshi.*, target ceshi.*;
GGSCI (localhost.localdomain DBLOGIN as ogg) 42> add replicat repa2,exttrail /ogg/dirdat/dc,checkpointtable ceshi.checkpoint
创建表定义文件,传送到目标端
GGSCI (localhost.localdomain) 1> edit param defgen2
defsfile ./dirdef/ceshi.def
sourcedb ceshi@192.168.59.141:3306 userid ogg, password 123456
table ceshi.*;
[root@localhost ogg]# ./defgen paramfile ./dirprm/defgen2.prm
[root@localhost ogg]# scp ./dirdef/ceshi.def root@192.168.59.198:/ogg/dirdef/
开启数据实时同步
GGSCI (localhost.localdomain DBLOGIN as ogg) 12> start mgr
新增测试数据
-- 使用 ceshi 数据库
USE ceshi;
-- 向 product 表插入第一条数据
mysql> INSERT INTO product (product_name, price, description)
VALUES ('New Product 5', 99.99, 'This is the first new product.');
-- 向 product 表插入第二条数据
mysql> INSERT INTO product (product_name, price, description)
VALUES ('New Product 6', 49.50, 'This is the second new product.');
mysql> select count(1) from product;
验证数据
mysql> select count(1) from product;
mysql> select * from product order by product_id desc limit 2;
到这里,我们就完成了MySQL多种场景的数据实时同步了。
五、总结
OGG的功能非常强大,使用过程中一定要注意规范使用,尤其是checkpoint的使用,它可以帮助我们对每个进程的读、写进行检查,提高效率。