案例 16:MGR 相同 GTID 产生不同 transaction 故障
作者:王福祥 MGR 作为MySQL 原生的高可用方案,它的基于共识协议的同步和决策机制,看起来也更为先进。吸引了一 票用户积极尝试,希望通过MGR 架构解决RPO=0 的高可用切换。在实际使用中经常会遇到因为网络抖动 的问题造成集群故障,最近我们某客户就遇到了这类问题,导致数据不一致。 1 问题现象 这是在生产环境中一组MGR 集群,单主架构,我们可以看到在相同的GTID86afb16f-1b8c-11e8-812f- 0050568912a4:57305280 下,本应执行相同的事务,但binlog 日志显示不同事务信息。 Primary 节点binlog: SET @@SESSION.GTID_NEXT= ‘86afb16f-1b8c-11e8-812f-0050568912a4:57305280’/!/;
at 637087357
#190125 15:02:55 server id 3136842491 end_log_pos 637087441 Query thread_id=19132957 exec_time=0 error_code=0 SET TIMESTAMP=1548399775/!/; BEGIN /!/;
at 637087441
#190125 15:02:55 server id 3136842491 end_log_pos 637087514 Table_map:
world.IC_WB_RELEASE mapped to number 398
at 637087514
#190125 15:02:55 server id 3136842491 end_log_pos 637087597 Write_rows: table id flags: STMT_END_F BINLOG ' n7RKXBP7avi6SQAAABov+SUAAI4BAAAAAAEAB2ljZW50ZXIAFUlDX1FVRVJZX1VTRVJDQVJEX0xP ‘/!/;
INSERT INTO world.IC_WB_RELEASE
SET
Secondary 节点binlog: SET @@SESSION.GTID_NEXT= ‘86afb16f-1b8c-11e8-812f-0050568912a4:57305280’/!/;
at 543772830
#190125 15:02:52 server id 3136842491 end_log_pos 543772894 Query
thread_id=19162514 exec_time=318 error_code=0
SET TIMESTAMP=1548399772/!/;

MGR 相同GTID 产生不同transaction 故障分析 BEGIN /!/;
at 543772894
#190125 15:02:52 server id 3136842491 end_log_pos 543772979 Table_map:
world.IC_QUERY_USERCARD_LOG mapped to number 113
at 543772979
#190125 15:02:52 server id 3136842491 end_log_pos 543773612 Delete_rows: table id 113 flags: STMT_END_F BINLOG ' nLRKXBP7avi6VQAAADNRaSAAAHEAAAAAAAEAB2ljZW50ZXIADUlDX1dCX1JFTEVBU0UACw8PEg8 ‘/!/;
DELETE FROM world.IC_QUERY_USERCARD_LOG
WHERE
从以上信息可以推测,primary 节点在这个GTID 下对world.IC_WB_RELEASE 表执行了insert 操作事件没
有同步到secondary 节点,secondary 节点收到主节点的其他事件,造成了数据是不一致的。当在表
IC_WB_RELEASE 发生delete 操作时,引发了下面的故障,导致从节点脱离集群。
2019-01-28T11:59:30.919880Z 6 [ERROR] Slave SQL for channel ‘group_replication_ap
plier’: Could not execute Delete_rows event on table world.IC_WB_RELEASE; Can
’t find record in ‘IC_WB_RELEASE’, Error_code: 1032; handler error HA_ERR_KEY_NOT
_FOUND, Error_code: 1032
2019-01-28T11:59:30.919926Z 6 [Warning] Slave: Can’t find record in ‘IC_WB_RELEAS
E’ Error_code: 1032
2019-01-28T11:59:30.920095Z 6 [ERROR] Plugin group_replication reported: ‘The app
lier thread execution was aborted. Unable to process more transactions, this membe
r will now leave the group.’
2019-01-28T11:59:30.920165Z 6 [ERROR] Error running query, slave SQL thread abort
ed. Fix the problem, and restart the slave SQL thread with “SLAVE START”. We stopp
ed at log ‘FIRST’ position 271.
2019-01-28T11:59:30.920220Z 3 [ERROR] Plugin group_replication reported: ‘Fatal e
rror during execution on the Applier process of Group Replication. The server will
now leave the group.’
2019-01-28T11:59:30.920298Z 3 [ERROR] Plugin group_replication reported: ‘The ser
ver was automatically set into read only mode after an error was detected.’
2 问题分析
- 主节点在向从节点同步事务时,至少有一个GTID 为86afb16f-1b8c-11e8-812f- 0050568912a4:57305280(执行的是insert 操作)的事务没有同步到从节点,此时从实例还不存在 这个GTID;于是主实例GTID 高于从实例。数据就已经不一致了。
- 集群业务正常进行,GTID 持续上涨,新上涨的GTID 同步到了从实例,占用了86afb16f-1b8c-11e8- 812f-0050568912a4:57305280 这个GTID,所以从实例没有执行insert 操作,少了一部分数据。
- 主节点对GTID 为86afb16f-1b8c-11e8-812f-0050568912a4:57305280 执行的insert 数据进行 delete,而从节点由于没有同步到原本的insert 操作;没有这部分数据就不能delete,于是脱离了
集群。 对于该故障的分析,我们要从主从实例GTID 相同,但是事务不同的原因入手,该问题猜测与bug (https://bugs.mysql.com/bug.php?id=92690)相关,我们针对MGR 同步事务的时序做如下分析。 3 相关知识背景 MGR 全组同步数据的Xcom 组件是基于paxos 算法的实现;每当提交新生事务时,主实例会将新生事务发 送到从实例进行协商,组内协商通过后全组成员一起提交该事务;每一个节点都以同样的顺序,接收到 了同样的事务日志,所以每一个节点以同样的顺序回放了这些事务,最终组内保持了一致的状态。 3.1 paxos 包括两个角色:
- 提议者(Proposer):主动发起投票选主,允许发起提议。
- 接受者(Acceptor):被动接收提议者(Proposer)的提议,并记录和反馈,或学习达成共识的提 议。 3.2 paxos 达成共识的过程包括两个阶段: 第一阶段(prepare) a:提议者(Proposer)发送prepare 请求,附带自己的提案标识(ballot,由数值编号加节点编号组 成)以及value 值。组成员接收prepare 请求; b:如果自身已经有了确认的值,则将该值以ack_prepare 形式反馈;在这个阶段中,Proposer 收到ack 回复后会对比ballot 值,数值大的ballot 会取代数值小的ballot。如果收到大多数应答之后进入下一 个阶段。 第二阶段(accept) a:提议者(Proposer)发送accept 请求 b:接收者(Acceptor)收到请求后对比自身之前的bollat 是否相同以及是否接收过value 值。如果未 接受过value 值 以及ballot 相同,则返回ack_accept,如果接收过value 值,则选取最大的ballot 返 回ack_accept。 c:之后接受相同value 值的Proposer 节点发送learn_op,收到learn_op 节点的实例表示确认了数据修 改,传递给上层应用。 针对本文案例我们需要强调几个关键点:
- 该案例最根本的异常对比发生在第二次提案的prepare 阶段。
- prepare 阶段的提案标识由数值编号和节点编号两部分组成;其中数值编号类似自增长数值,而节点 编号不变。 4 分析过程 结合paxos 时序,我们对案例过程进行推测:
MGR 相同GTID 产生不同transaction 故障分析


MGR 相同GTID 产生不同transaction 故障分析 Tips:以下分析过程请结合时序图操作步骤观看
- 【step1】 primary 节点要执行对表world.IC_WB_RELEASE 的insert 操作,向组内发送假设将 ballot 设置为(0.0)以及将value 值world.IC_WB_RELEASE 的prepare 请求,并收到大多数成员的 ack_prepare 返回,于是开始发送accept 请求。primary 节点将ballot(0.0)的提案信息发送至组 内,仍收到了大多数成员ack_accept(ballot=0.0value=world.IC_WB_RELEASE)返回。然后发送 learn_op 信息【step3】。
- 同时其他从节点由于网络原因没有收到主实例的的learn_op 信息【step3】,而其中一台从实例开始 新的prepare 请求【step2】,请求value 值为no_op(空操作)ballot=1.1(此编号中节点编号是关 键,该secondary 节点编号大于primary 节点编号,导致了后续的问题,数值编号无论谁大谁小都 要被初始化)。 其他的从实例由于收到过主节点的value 值;所以将主节点的(ballot=0.0, value=world.IC_WB_RELEASE)返回;而收到的ack_prepare 的ballot 值的数值符号全组内被初始化为 0,整个ballot 的大小完全由节点编号决定,于是从节点选取了ballot 较大的该实例value 值作为新的 提案,覆盖了主实例的value 值并收到大多数成员的ack_accept【step2】。并在组成员之间发送了 learn_op 信息【step3】,跳过了主实例提议的事务。 从源码中可以看到关于handle_ack_prepare 的逻辑。 handle_ack_prepare has the following code: if (gt_ballot(m->proposal,p->proposer.msg->proposal)) { replace_pax_msg(&p->proposer.msg, m); … }
- 此时,主节点在accept 阶段收到了组内大多数成员的ack_accept 并收到了 自己所发送的learn_op 信息,于是把自己的提案(也就是binlog 中对表的insert 操作)提交【step3】,该事务GTID 为 86afb16f-1b8c-11e8-812f-0050568912a4:57305280。而其他节点的提案为no_op【step3】,所以不 会提交任何事务。此时主实例GTID 大于其他从实例的。
- 主节点新生GTID 继续上涨;同步到从实例,占用了从实例的86afb16f-1b8c-11e8-812f- 0050568912a4:57305280 这个GTID,于是产生了主节点与从节点binlog 中GTID 相同但是事务不同 的现象。
- 当业务执行到对表world.IC_WB_RELEASE 的delete 操作时,主实例能进行操作,而其他实例由于没 有insert 过数据,不能操作,就脱离了集群。 5 过程总结:
- 旧主发送prepare 请求,收到大多数ack,进入accept 阶段,收到大多数ack。
- 从实例由于网络原因没有收到learn_op 信息。
- 其中一台从实例发送新的prepare 请求,value 为no_op。
- 新一轮的prepare 请求中,提案标识的数值编号被初始化,新的提案者大于主实例,从实例选取新
提案,执行空操作,不写入relay-log。代替了主实例的事务,使主实例GTID 大于从实例。 5. 集群网络状态恢复,新生事物正常同步到从实例,占用了本该正常同步的GTID,使集群中主节点与 从节点相同GTID 对应的事务时不同的。 6 结论 针对此问题我们也向官方提交SR,官方已经反馈社区版MySQL 5.7.26 和MySQL 8.0.16 中会修复,如果 是企业版客户可以申请最新的hotfix 版本。 在未升级MySQL 版本前,若再发生此类故障,在修复时需要人工检查,检查切换时binlog 中GTID 信息 与新主节点对应GTID 的信息是否一致。如果不一致需要人工修复至一致状态,如果一致才可以将被踢出 的原主节点加回集群。 所以正在使用了MGR 5.7.26 之前社区版的DBA 同学请注意避坑。
~~~~-~~~-$--. ~~~23 •. ~~~3o··-NM-~.
rlg~~.&J!Uft{{., 1m’tfl(, $iJJ!~~ilfJM;, Oracle :lf#, flfUJ}Jfi, ~~$1iit,
M-ffl ~ MySQL IJJ-~~~~0 ~~~-f!PN{:t}f]jX, N!W}~~Jlo .+–-jjj
-~~~~Mff.*·~~~ .. -EOO~.-W7~~~~$~~~~~
ftJXlo
f!JI OGG ~~~ Oracle IIJ MySQL llfallliftif8
lOGG-~
OGG :i:ffl\jg Oracle GoldenGate, EB Oracle B'7Jt!1tHJ<JJflf-f6¥1}c:ff;fi;J!J&Ii:ljt9J!J&Iif!iUI¥J-1’-1fti
IA. :;fH lt ·=f<it ‘E if~ IA OGG I¥11Jtt£ +PI l;J.1I.!tfilffJT7Jffii!W Oracle 1¥1 redo 1 og,
129Jitff~~~~tl: ~
xta<Jlftrt!r%PX:ga;fi;J$iJliJIimttfli?ta<Jff. *:ili::t!ii~:£-fl-ffi3zoM-ttm oGG ~~
Oracle .
Datasource for initial load:
change synchronization:
transaction log or
third-party capture module
Network
(Optional)
TARGET DB
CHANGE SYNCHRON’· U MySQL ~9J
Jiff~~UfilJH!la<JfilftjcjJa<J:>fffi’ff, l;).}kijj:fr’-Atl:ffiiTIQs–;r J.f.-·rr :r::j-,.’ r7
‘"-’’-•. / /;;t. PJ .::r: ’lT 11m ‘J’.L !L:.
**X>tLOOM**-fl-ffirOGG~~~;fi;J.GIiii1’-M7M.FPOO!ii~~w
7F:;fH:lttI¥JWC.‘i’.7J;Jt, tE OGG -ftffltt9J±~~ lki;J. r:lttlkX1tf:
1.
Manager :itt: -fi1R i!f ~rJffii!Wil! §;fjiljlij . 7t1ClkBfrr. ±1tfflJI[. tlli!fitilm‘E:lttlm
!J&iJiS :ff: ~ fB]’
:ht;{ff
2.
Extrtwt H: iiffdlt$1tlfl4, IEJH’f-lllt
3. Trails Xfl’: lilll’tfl’-ll!t.tfUl:ii!JUXfl’
4. Data pgq, U: iifft£1tM.$M. Jl’f Extract Uii!Jii!J. 14Ha, ;Jtdatii!JIItt.llm. !D:::t"lli Data PUIIIJI,
Extract UHllltiiiJUil.lllJJl!l Trail j;f1’, t,.§;litData Pump i!JJRii!J Trail j;ft:, :fm.!!ti!Jl7 Data Pump, Extract i!
fi1l.tRM,
I!Jl Data Pump H
ii’JIE:tHd:AIfiM.RfiijjltttPI!tllfxJi, Extract UH~~.d:.
5. Collector U: •u•ftlii!JU~. :Ji’l;A.IIII Trail X11'1Jr
6. ReplicatH: .JfR Trail Xft:IJriaitfiiJJteit, tJP!J!ZfiiJ DML ilfii:JI’t£13Miiii1Jt
2~
2.1~·
OGGJ!&<fl;
OGG HOM E
IP~:Ilt
2.2 1419
OGG 12.2.0.2.2 For Oracle
Oracle 11.2.0.4
/ home/oracle/ogg
17X.1 X.84.124
ems
OGG 12.2.0.2.2 For MySQL
MySQL 5.7.21
/opt/ogg
17X.1 X.84.121
ems
d·‘fJII;\JI#ltWI.tllfiiJ-;81, ftfl.tfjf~~lltfU7 -till! sqlines fiiJ7HII
,R,. Xff sqlinee IJI:.fEMySQL ftl·!RMOO~Ni!fiM•· l?J.JII:Mit&~~
BOOlt·
t,: .. , OOG.tf Oracle 3f8U:ySQL M:ar:::t"xff DDL “liiJ:tt;, I!!I~~JIJU$-lt
.&ta:::t"2fwa-a•·
2.3 UiBJ
–~·~~~~
iJIJ, ~1$ii’J redo log, fiiJ~~~JHOOGIA.—·~*·fiiJW.,OOGHJURIU.!mtJt, M~ftfUJIIU1”!Eii’J 13
U,OOG~‘f–ff··–.~l?J.OOGI!tfffi!Jl:Ji’
7 8**UIItl:*, OOG M~U"’ JH..Hii!JJIIJPJl?J.~jj’UfiiJ Reference jjHifVif
M-$~Uii!JIt·
使用 OGG 实现 Oracle 到 MySQL 数据平滑迁移 2.3.1 源端OGG 配置 (1)Oracle 数据库配置 针对Oracle 数据库,OGG 需要数据库开启归档模式及增加辅助补充日志、强制记录日志等来保障OGG 可 抓取到完整的日志信息 查看当前环境是否满足要求,输出结果如下图所示: SQL> SELECT NAME,LOG_MODE,OPEN_MODE,PLATFORM_NAME,FORCE_LOGGING,SUPPLEMENTAL_LOG _DATA_MIN FROM V$DATABASE; 如果条件不满足则实行该部分,
开启归档(开启归档需要重启数据库)
SQL> shutdown immediate; SQL> startup mount; SQL> alter database archivelog; SQL> alter database open; SQL> archive log list;
开启附加日志和强制日志
SQL> alter database add supplemental log data; SQL> alter database force logging; SQL> alter system switch logfile;
启用OGG 支持
SQL> show parameter enable_goldengate_replication SQL> alter system set enable_goldengate_replication=true;
再次查看当前环境是否满足要求
SQL>
SELECT
NAME,LOG_MODE,OPEN_MODE,PLATFORM_NAME,FORCE_LOGGING,SUPPLEMENTAL_LOG_DATA_MIN
FROM V$DATABASE;
(2)Oracle 数据库OGG 用户创建
OGG 需要有一个用户有权限对数据库的相关对象做操作,以下为涉及的权限,该示例将创建一个用户名和
密码均为ogg 的Oracle 数据库用户并授予以下权限

第4 章 技术分享
查看当前数据库已存在的表空间,可使用已有表空间或新建单独的表空间
SQL> SELECT TABLESPACE_NAME, CONTENTS FROM DBA_TABLESPACES;
查看当前表空间文件数据目录
SQL> SELECT NAME FROM V$DATAFILE;
创建一个新的OGG 用户的表空间
SQL> CREATE TABLESPACE OGG_DATA DATAFILE ‘/u01/app/oracle/oradata/cms/ogg_data01.dbf’ SIZE 2G;
创建ogg 用户且指定对应表空间并授权
SQL> CREATE USER ogg IDENTIFIED BY ogg DEFAULT TABLESPACE OGG_DATA; SQL> grant connect,resource,unlimited tablespace to ogg; SQL> grant create session,alter session to ogg; SQL> grant execute on utl_file to ogg; SQL> grant select any dictionary, select any table to ogg; SQL> grant alter any table to ogg; SQL> grant flashback any table to ogg; SQL> grant select any transaction to ogg; SQL> grant sysdba to ogg; SQL> grant execute on dbms_streams_adm to ogg; SQL> grant execute on dbms_flashback to ogg; SQL> exec dbms_goldengate_auth.grant_admin_privilege(‘OGG’); (3)源端OGG 管理进程(MGR)配置
切换至ogg 软件目录并执行ggsci 进入命令行终端
shell> cd $OGG_HOME shell> ggsci
编辑/创建mgr 配置文件
ggsci> edit params mgr PORT 7809 DYNAMICPORTLIST 8000-8050 – AUTOSTART extract – AUTORESTART extract,retries 4,waitminutes 4 STARTUPVALIDATIONDELAY 5 ACCESSRULE, PROG , IPADDR 17X.1X., ALLOW ACCESSRULE, PROG SERVER, ALLOW PURGEOLDEXTRACTS /home/oracle/ogg/dirdat/*, USECHECKPOINTS,MINKEEPFILES 3
启动并查看mgr 状态
ggsci> start mgr ggsci> info all ggsci> view report mgr
使用 OGG 实现 Oracle 到 MySQL 数据平滑迁移 (4)源端OGG 表级补全日志(trandata)配置 表级补全日志需要在最小补全日志打开的情况下才起作用,之前只在数据库级开启了最小补全日志(alter database add supplemental log data;),redolog 记录的信息还不够全面,必须再使用add trandata 开启表级的补全日志以获得必要的信息。
使用ogg 用户登录appdb 实例(TNS 配置)对cms 所有表增加表级补全日志
shell> cd $OGG_HOME shell> ggsci ggsci> dblogin userid ogg@appdb,password ogg ggsci> add trandata cms.* (5)源端OGG 抽取进程(extract)配置 Extract 进程运行在数据库源端,负责从源端数据表或日志中捕获数据。Extract 进程利用其内在的 checkpoint 机制,周期性地检查并记录其读写的位置,通常是写入到本地的trail 文件。这种机制是为 了保证如果Extract 进程终止或者操作系统宕机,我们重启Extract 进程后,GoldenGate 能够恢复到以 前的状态,从上一个断点处继续往下运行,而不会有任何数据损失。
切换至ogg 软件目录并执行ggsci 进入命令行终端
shell> cd $OGG_HOME shell> ggsci
创建一个新的增量抽取进程,从redo 日志中抽取数据
ggsci> add extract e_cms,tranlog,begin now
创建一个抽取进程抽取的数据保存路径并与新建的抽取进程进行关联
ggsci> add exttrail /home/oracle/ogg/dirdat/ms,extract e_cms,megabytes 1024
创建抽取进程配置文件
ggsci> edit params e_cms extract e_cms setenv (NLS_LANG = “AMERICAN_AMERICA.AL32UTF8”) setenv (ORACLE_HOME = “/data/oracle/11.2/db_1”) setenv (ORCLE_SID = “cms”) userid ogg@appdb,password ogg discardfile /home/oracle/ogg/dirrpt/e_cms.dsc,append,megabytes 1024 exttrail /home/oracle/ogg/dirdat/ms statoptions reportfetch reportcount every 1 minutes,rate warnlongtrans 1H,checkinterval 5m table cms.*;
启动源端抽取进程
ggsci> start e_cms ggsci> info all ggsci> view report e_cms
第4 章 技术分享 (6)源端OGG 传输进程(pump)配置 pump 进程运行在数据库源端,其作用非常简单。如果源端的Extract 抽取进程使用了本地trail 文件, 那么pump 进程就会把trail 文件以数据块的形式通过TCP/IP 协议发送到目标端,Pump 进程本质上是 Extract 进程的一种特殊形式,如果不使用trail 文件,那么Extract 进程在抽取完数据后,直接投递到 目标端。 补充:pump 进程启动时需要与目标端的mgr 进程进行连接,所以需要优先将目标端的mgr 提前配置好, 否则会报错连接被拒绝,无法传输抽取的日志文件到目标端对应目录下
切换至ogg 软件目录并执行ggsci 进入命令行终端
shell> cd $OGG_HOME shell> ggsci
增加一个传输进程与抽取进程抽取的文件进行关联
shell> add extract p_cms,exttrailsource /home/oracle/ogg/dirdat/ms
增加配置将抽取进程抽取的文件数据传输到远程对应目录下
注意rmttrail 参数指定的是目标端的存放目录,需要目标端存在该目录路径
shell> add rmttrail /opt/ogg/dirdat/ms,extract p_cms
创建传输进程配置文件
shell>edit params p_cms extract p_cms setenv (NLS_LANG = “AMERICAN_AMERICA.AL32UTF8”) setenv (ORACLE_HOME = “/data/oracle/11.2/db_1”) setenv (ORCLE_SID = “cms”) userid ogg@appdb,password ogg RMTHOST 17X.1X.84.121,MGRPORT 7809 RMTTRAIL /opt/ogg/dirdat/ms discardfile /home/oracle/ogg/dirrpt/p_cms.dsc,append,megabytes 1024 table cms.*;
启动源端抽取进程
启动前确保目标端mgr 进程已开启
ggsci> start p_cms ggsci> info all ggsci> view report p_cms (7)源端OGG 异构mapping 文件(defgen)生成 该文件记录了源库需要复制的表的表结构定义信息,在源库生成该文件后需要拷贝到目标库的dirdef 目 录,当目标库的replica 进程将传输过来的数据apply 到目标库时需要读写该文件,同构的数据库不需 要进行该操作。
创建mapping 文件配置
shell> cd $OGG_HOME shell> vim ./dirprm/mapping_cms.prm
使用 OGG 实现 Oracle 到 MySQL 数据平滑迁移 defsfile ./dirdef/cms.def,purge userid ogg@appdb,password ogg table cms.*;
基于配置生成cms 库的mapping 文件
默认生成的文件保存在$OGG_HOME 目录的dirdef 目录下
shell> ./defgen paramfile ./dirprm/mapping_cms.prm
将该文件拷贝至目标端对应的目录
shell> scp ./dirdef/cms.def root@17X.1X.84.121:/opt/ogg/dirdef 至此源端环境配置完成 2.3.2 目标端OGG 配置 (1)目标端MySQL 数据库配置 确认MySQL 端表结构已经存在 MySQL 数据库OGG 用户创建 mysql> create user ‘ogg’@’%’ identified by ‘ogg’; mysql> grant all on . to ‘ogg’@’%’;
提前创建好ogg 存放checkpoint 表的数据库
mysql> create database ogg; (2)目标端OGG 管理进程(MGR)配置 目标端的MGR 进程和源端配置一样,可直接将源端配置方式在目标端重复执行一次即可,该部分不在赘 述 (3)目标端OGG 检查点日志表(checkpoint)配置 checkpoint 表用来保障一个事务执行完成后,在MySQL 数据库从有一张表记录当前的日志回放点,与 MySQL 复制记录binlog 的GTID 或position 点类似。
切换至ogg 软件目录并执行ggsci 进入命令行终端
shell> cd $OGG_HOME shell> ggsci ggsci> edit param ./GLOBALS checkpointtable ogg.ggs_checkpoint ggsci> dblogin sourcedb ogg@17X.1X.84.121:3306 userid ogg ggsci> add checkpointtable ogg.ggs_checkpoint (4)目标端OGG 回放线程(replicat)配置 Replicat 进程运行在目标端,是数据投递的最后一站,负责读取目标端Trail 文件中的内容,并将解析 其解析为DML 语句,然后应用到目标数据库中。
第4 章 技术分享
切换至ogg 软件目录并执行ggsci 进入命令行终端
shell> cd $OGG_HOME shell> ggsci
添加一个回放线程并与源端pump 进程传输过来的trail 文件关联,并使用checkpoint 表确保数据
不丢失 ggsci> add replicat r_cms,exttrail /opt/ogg/dirdat/ms,checkpointtable ogg.ggs_checkpoint
增加/编辑回放进程配置文件
ggsci> edit params r_cms replicat r_cms targetdb cms@17X.1X.84.121:3306,userid ogg,password ogg sourcedefs /opt/ogg/dirdef/cms.def discardfile /opt/ogg/dirrpt/r_cms.dsc,append,megabytes 1024 HANDLECOLLISIONS MAP cms.,target cms.; 注意:replicat 进程只需配置完成,无需启动,待全量抽取完成后再启动。 至此源端环境配置完成。 待全量数据抽取完毕后启动目标端回放进程即可完成数据准实时同步。 2.3.3 全量同步配置 全量数据同步为一次性操作,当OGG 软件部署完成及增量抽取进程配置并启动后,可配置1 个特殊的 extract 进程从表中抽取数据,将抽取的数据保存到目标端生成文件,目标端同时启动一个单次运行的 replicat 回放进程将数据解析并回放至目标数据库中。 (1)源端OGG 全量抽取进程(extract)配置
切换至ogg 软件目录并执行ggsci 进入命令行终端
shell> cd $OGG_HOME shell> ggsci
增加/编辑全量抽取进程配置文件
其中RMTFILE 指定抽取的数据直接传送到远端对应目录下
注意:RMTFILE 参数指定的文件只支持2 位字符,如果超过replicat 则无法识别
ggsci> edit params ei_cms SOURCEISTABLE SETENV (NLS_LANG = “AMERICAN_AMERICA.AL32UTF8”) SETENV (ORACLE_SID=cms) SETENV (ORACLE_HOME=/data/oracle/11.2/db_1) USERID ogg@appdb,PASSWORD ogg RMTHOST 17X.1X.84.121,MGRPORT 7809 RMTFILE /opt/ogg/dirdat/ms,maxfiles 100,megabytes 1024,purge TABLE cms.*;
使用 OGG 实现 Oracle 到 MySQL 数据平滑迁移
启动并查看抽取进程正常
shell> nohup ./extract paramfile ./dirprm/ei_cms.prm reportfile ./dirrpt/ei_cms.rpt &
查看日志是否正常进行全量抽取
shell> tail -f ./dirrpt/ei_cms.rpt (2)目标端OGG 全量回放进程(replicat)配置
切换至ogg 软件目录并执行ggsci 进入命令行终端
shell> cd $OGG_HOME shell> ggsci ggsci> edit params ri_cms SPECIALRUN END RUNTIME TARGETDB cms@17X.1X.84.121:3306,USERID ogg,PASSWORD ogg EXTFILE /opt/ogg/dirdat/ms DISCARDFILE ./dirrpt/ri_cms.dsc,purge MAP cms.,TARGET cms.;
启动并查看回放进程正常
shell> nohup ./replicat paramfile ./dirprm/ri_cms.prm reportfile ./dirrpt/ri_cms.rpt &
查看日志是否正常进行全量回放
shell> tail -f ./dirrpt/ri_cms.rpt 3 数据校验 数据校验是数据迁移过程中必不可少的环节,本章节提供给几个数据校验的思路共大家参数,校验方式 可以由以下几个角度去实现:
- 通过OGG 日志查看全量、增量过程中discards 记录是否为0 来判断是否丢失数据;
- 通过对源端、目标端的表执行count 判断数据量是否一致;
- 编写类似于pt-table-checksum 校验原理的程序,实现行级别一致性校验,这种方式优缺点特别明 显,优点是能够完全准确对数据内容进行校验,缺点是需要遍历每一行数据,校验成本较高;
- 相对折中的数据校验方式是通过业务角度,提前编写好数十个返回结果较快的SQL,从业务角度 抽样校验。 4 迁移问题处理 本章节将讲述迁移过程中碰到的一些问题及相应的解决方式。 4.1 MySQL 限制
第4 章 技术分享 在Oracle 到MySQL 的表结构迁移过程中主要碰到以下两个限制:
- Oracle 端的表结构因为最初设计不严谨,存在大量的列使用varchar(4000)数据类型,导致迁移到 MySQL 后超出行限制,表结构无法创建。由于MySQL 本身数据结构的限制,一个16K 的数据页最少要 存储两行数据,因此单行数据不能超过65,535 bytes,因此针对这种情况有两种解决方式: □ 根据实际存储数据的长度,对超长的varchar 列进行收缩; □ 对于无法收缩的列转换数据类型为text,但这在使用过程中可能导致一些性能问题;
- 与第一点类似,在Innodb 存储引擎中,索引前缀长度限制是767 bytes,若使用DYNAMIC、 COMPRESSED 行格式且开启innodblargeprefix 的场景下,这个限制是3072 bytes,即使用utf8mb4 字符集时,最多只能对varchar(768)的列创建索引;
- 使用ogg 全量初始化同步时,若存在外键约束,批量导入时由于各表的插入顺序不唯一,可能子表 先插入数据而主表还未插入,导致报错子表依赖的记录不存在,因此建议数据迁移阶段禁用主外键 约束,待迁移结束后再打开。 mysql>set global foreign_key_checks=off; 4.2 全量与增量衔接 HANDLECOLLISIONS 参数是实现OGG 全量数据与增量数据衔接的关键,其实现原理是在全量抽取前先开启 增量抽取进程,抓去全量应用期间产生的redo log,当全量应用完成后,开启增量回放进程,应用全量 期间的增量数据。使用该参数后增量回放DML 语句时主要有以下场景及处理逻辑: □ 目标端不存在delete 语句的记录,忽略该问题并不记录到discardfile □ 目标端丢失update 记录 更新的是主键值,update 转换成insert 更新的键值是非主键,忽略该问题并不记录到discardfile □ 目标端重复insert 已存在的主键值,这将被replicat 进程转换为UPDATE 现有主键值的行 4.3 OGG 版本选择 在OGG 版本选择上我们也根据用户的场景多次更换了OGG 版本,最初因为客户的Oracle 数据库版本为 11.2.0.4,因此我们在选择OGG 版本时优先选择使用了11 版本,但是使用过程中发现,每次数据抽取生 成的trail 文件达到2G 左右时,OGG 报错连接中断,查看RMTFILE 参数详细说明了解到trail 文件默认 限制为2G,后来我们替换OGG 版本为12.3,使用MAXFILES 参数控制生成多个指定大小的trail 文件, 回放时Replicat 进程也能自动轮转读取Trail 文件,最终解决该问题。但是如果不幸Oracle 环境使用 了Linux 5 版本的系统,那么你的OGG 需要再降一个小版本,最高只能使用OGG 12.2。 4.4 无主键表处理 在迁移过程中还碰到一个比较难搞的问题就是当前Oracle 端存在大量表没有主键。在MySQL 中的表没有 主键这几乎是不被允许的,因为很容易导致性能问题和主从延迟。同时在OGG 迁移过程中表没有主键也 会产生一些隐患,比如对于没有主键的表,OGG 默认是将这个一行数据中所有的列拼凑起来作为唯一键, 但实际还是可能存在重复数据导致数据同步异常,Oracle 官方对此也提供了一个解决方案,通过对无主 键表添加GUID 列来作为行唯一标示,具体操作方式可以搜索MOS 文档ID 1271578.1 进行查看。 4.5 OGG 安全规则 报错信息
使用 OGG 实现 Oracle 到 MySQL 数据平滑迁移 2019-03-08 06:15:22 ERROR OGG-01201 Error reported by MGR : Access denied. 错误信息含义源端报错表示为该抽取进程需要和目标端的mgr 进程通讯,但是被拒绝,具体操作 为:源端的extract 进程需要与目标端mgr 进行沟通,远程将目标的replicat 进行启动,由于安全 性现在而被拒绝连接。 报错原因 在Oracle OGG 11 版本后,增加了新特性安全性要求,如果需要远程启动目标端的replicat 进程, 需要在mgr 节点增加访问控制参数允许远程调用 解决办法 在源端和目标端的mgr 节点上分别增加访问控制规则并重启
表示该mgr 节点允许(ALLOW)10.186 网段(IPADDR)的所有类型程序(PROG *)进行连接访问
ACCESSRULE, PROG , IPADDR 10.186..*, ALLOW 4.6 数据抽取方式 □ 报错信息 2019-03-15 14:49:04 ERROR OGG-01192 Trying to use RMTTASK on data types which may be written as LOB chunks (Table: ‘UNIONPAYCMS.CMS_OT_CONTENT_RTF’). □ 报错原因 根据官方文档说明,当前直接通过Oracle 数据库抽取数据写到MySQL 这种initial-load 方式,不 支持LOBs 数据类型,而表 UNIONPAYCMS.CMSOTCONTENT_RTF 则包含了CLOB 字段,无法进行传输,并 且该方式不支持超过4k 的字段数据类型 □ 解决方法 将抽取进程中的RMTTASK 改为RMTFILE 参数官方建议将数据先抽取成文件,再基于文件数据解析进 行初始化导入 5 OGG 参考资料 阅读OGG 官方文档,查看support.oracle.com 上Oracle 官方给予的问题解决方案是学习OGG 非常有效 的方式,作者这里也打包了一些文档供大家下载查看,其中个人感觉比较重要并且查看比较多的文档包 括: How to Replicate Data Between Oracle and MySQL Database? (文档 ID 1605674.1).pdf 这个 文档的作用相当于OGG 快速开始,能够帮助用户快速的配置并跑通OGG 全量、增量数据同步的流程,非 常适合初学者上手 Administering Oracle GoldenGate for Windows and UNIX.pdf Administering 文档 内容较多,其中详细介绍了各种OGG 的使用配置场景,大家可以根据自己需要选择重点章节浏览 Reference for Oracle GoldenGate for Windows and UNIX.pdf Reference 文档算是我查看最多的一个 文档,其中包含了OGG 所有参数的描述、语法及使用示例,非常的详细,这也是前面未对参数进行展开 讲解的原因。 文档资料百度网盘下载链接: https://pan.baidu.com/s/13fwguorcVrboeBH8DYTpZA 密码:e8rh