`
wsql
  • 浏览: 11776870 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
文章分类
社区版块
存档分类
最新评论

GoldenGate配置实例:RHEL 4.7下的Oracle 10g RAC到单实例的单向同步

 
阅读更多

下面文章的部分内容来网络,但所有内容都经过自己的测试、整理、补充。

环境

源端是一个RAC
rac1 rac2: RHEL 4u7 + ORACLE 10.2.0.4 + ASM
rac1 外网IP 192.168.47.241 虚拟IP 192.168.47.243
rac2 外网IP 192.168.47.242 虚拟IP 192.168.47.244

目标端是一个单机
ggdb: RHEL 4u7 + ORACLE 10.2.0.4 + ASM
IP: 192.168.47.211

两台主机均已创建数据库,sid分别为oradb dyggdb

配置oradb dyggdb的数据同步

goldengate版本11.1.1.0

时间同步

RAC中,节点间的时间同步很重要,官方文档里面的解释是,GoldenGate通过时间来做一些关键决策。这里我们通过NTP来配置时间同步
rac1配置为NTP服务器

rac1:

修改配置文件。配置一个server指向自己

cat>/etc/ntp.conf<<EOF
restrict default nomodify notrap noquery
restrict 127.0.0.1

server 0.rhel.pool.ntp.org
server 1.rhel.pool.ntp.org
server 2.rhel.pool.ntp.org

server
192.168.47.241

server 127.127.1.0
fudge 127.127.1.0 stratum 10

driftfile /var/lib/ntp/drift
broadcastdelay 0.008
keys /etc/ntp/keys

EOF

启动ntpd服务

service ntpd restart
chkconfig --level 345 ntpd on


rac2:

rac2上的操作与rac1类似,不同的是配置的server指向rac1

cat>/etc/ntp.conf<<EOF
restrict default nomodify notrap noquery
restrict 127.0.0.1

server 0.rhel.pool.ntp.org
server 1.rhel.pool.ntp.org
server 2.rhel.pool.ntp.org

server
192.168.47.241

fudge
192.168.47.241stratum 10

driftfile /var/lib/ntp/ntp.drift
broadcastdelay 0.008
keys /etc/ntp/keys
EOF


service ntpd restart
chkconfig --level 345 ntpd on


需要注意的是,配置完ntp后大概需要5-10分钟的时候,才会开始进行同步。可以通过ntpq -p 命令来查看

[root@rac1 ~]#ntpq -p
remote refid st t when poll reach delay offset jitter
==============================================================================
rac1 .INIT. 16 u - 64 0 0.000 0.000 4000.00
*LOCAL(0) LOCAL(0) 10 l 30 64 377 0.000 0.000 0.001

[root@rac2 ~]#ntpq -p
remote refid st t when poll reach delay offset jitter
==============================================================================
rac1 LOCAL(0) 11 u 9 64 1 0.187 0.072 0.001



源端和目标端之间的时间同步则不是那么重要。可以在目标端手动对时间进行一次同步(目标端的ntpd服务没有启动),并将同步后的系统时间写入bios
[root@ggdb ~]#ntpdate 192.168.47.241
8 Dec 15:40:20 ntpdate[8311]: adjust time server 192.168.47.241 offset 0.003007 sec
[root@ggdb ~]#/sbin/hwclock -w

如果不能成功,可以在ntpdate命令中加上-d参数来排查具体原因

创建系统用户

在源端(包括rac1rac2)和目标端创建用户,用于管理GoldenGate

useradd -g oinstall -G dba goldengate
passwd goldengate


配置资源限制:
官方建议尽可能将资源限制放开,其实我们可以根据具体情况进行配置,但不能设得太小

cat >>/etc/security/limits.conf<<EOF
goldengate soft memlock 3145728
goldengate hard memlock 3145728
goldengate soft nproc 200
goldengate hard nproc 1024
goldengate soft stack -
goldengate hard stack -
goldengate soft fsize -
goldengate hard fsize -
EOF



准备GoldenGate安装环境

源端

RAC环境中,GoldenGate应该安装在一个共享存储中,这样当运行GoldenGate相关进程的节点出现故障时,可以将该目录挂载到另一个节点中继续运行
以下的在rac1上做就可以了

这里有一个分区/dev/sdg1,可以被rac1rac2访问。我们将其格式化ext3文件系统,并挂载到rac1(在正常情况下,我们在rac1上启动extract进程)

[root@rac1 ~]#mkfs.ext3 /dev/sdg1
mke2fs 1.35 (28-Feb-2004)
Filesystem label=
OS type: Linux
Block size=4096 (log=2)
Fragment size=4096 (log=2)
12500992 inodes, 24993115 blocks
1249655 blocks (5.00%) reserved for the super user
First data block=0
Maximum filesystem blocks=4294967296
763 block groups
32768 blocks per group, 32768 fragments per group
16384 inodes per group
Superblock backups stored on blocks:
32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208,
4096000, 7962624, 11239424, 20480000, 23887872

Writing inode tables: done
Creating journal (8192 blocks): done
Writing superblocks and filesystem accounting information: done

This filesystem will be automatically checked every 24 mounts or
180 days, whichever comes first. Use tune2fs -c or -i to override.
[root@rac1 ~]#

挂载到rac1/opt/gg下:
[root@rac1 ~]#mkdir /opt/gg
[root@rac1 ~]#mount /dev/sdg1 /opt/gg

然后在rac1中配置开机自动挂载(这一步不要在rac2上做)
echo "mount /dev/sdg1 /opt/gg" >> /etc/rc.local



目标端

目标端是一个单机,安装到/opt/gg
mkdir /opt/gg


源端和目标端上,赋予goldengate用户/opt/gg目录的使用权限
chown -R goldengate:oinstall /opt/gg
chmod -R 775 /opt/gg



解压goldengate安装文件到安装目录

安装GoldenGate软件很简单,解压即可

源端(rac1)和目标端都做:
goldengate用户登录
[goldengate@rac1 goldengateMedia]$mkdir /opt/gg/goldengate
[goldengate@rac1 goldengateMedia]$cp ggs_Linux_x64_ora10g_64bit_v11_1_1_0_0_078.tar /opt/gg/goldengate
[goldengate@rac1 goldengateMedia]$cd /opt/gg/goldengate
[goldengate@rac1 goldengate]$tar -xvf ggs_Linux_x64_ora10g_64bit_v11_1_1_0_0_078.tar

配置环境变量

源端(rac1rac2)和目标端:
修改goldengate用户的环境变量配置文件(ORACLE_SID按实际情况修改)
cat>>/home/goldengate/.bashrc<<EOF
ORACLE_HOME=/opt/app/oracle/product/10.2.0/db_1
export ORACLE_HOME
ORACLE_SID=oradb1
export ORACLE_SID
GG_HOME=/opt/gg/goldengate
export GG_HOME
PATH=\$ORACLE_HOME/bin:$GG_HOME:\$PATH
export PATH
LD_LIBRARY_PATH=\$ORACLE_HOME/lib:\$GG_HOME:\$LD_LIBRARY_PATH
export LD_LIBRARY_PATH
EOF


应用刚刚修改的环境变量,然后进入GoldenGate安装目录,执行ldd ggsci,确定需要的库文件都能够找到。如果出现共享库文件无法找到,例如libnnz10.so => not found,检查LD_LIBRARY_PATH环境变量的设置
[goldengate@ggdb goldengate]$source ~/.bashrc
[goldengate@ggdb goldengate]$cd $GG_HOME
[goldengate@ggdb goldengate]$ldd ggsci
libdl.so.2 => /lib64/libdl.so.2 (0x00000037a3900000)
libicui18n.so.38 => /opt/gg/goldengate/libicui18n.so.38 (0x0000002a9558c000)
libicuuc.so.38 => /opt/gg/goldengate/libicuuc.so.38 (0x0000002a958ec000) libicudata.so.38 => /opt/gg/goldengate/libicudata.so.38 (0x0000002a95c25000)
libpthread.so.0 => /lib64/tls/libpthread.so.0 (0x00000037a3d00000)
libxerces-c.so.28 => /opt/gg/goldengate/libxerces-c.so.28 (0x0000002a968fc000)
libnnz10.so => /opt/app/oracle/product/10.2.0/db_1/lib/libnnz10.so (0x0000002a96e13000)
libclntsh.so.10.1 => /opt/app/oracle/product/10.2.0/db_1/lib/libclntsh.so.10.1 (0x0000002a972b4000)
libstdc++.so.6 => /usr/lib64/libstdc++.so.6 (0x00000037a5d00000)
libm.so.6 => /lib64/tls/libm.so.6 (0x00000037a3700000)
libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00000037a5b00000)
libc.so.6 => /lib64/tls/libc.so.6 (0x00000037a3400000)
/lib64/ld-linux-x86-64.so.2 (0x00000037a3000000)
libnsl.so.1 => /lib64/libnsl.so.1 (0x00000037a9100000)

数据库方面的准备

在源端数据库中打开归档模式

SQL>archive log list

Database log mode Archive Mode
Automatic archival Enabled
Archive destination +RECOVERY_DG
Oldest online log sequence 120
Next log sequence to archive 121
Current log sequence 121


若处于非归档模式,则改为归档模式:

SQL>
shutdown immediate
Database closed.
Database dismounted.
ORACLE instance shut down.
SQL>
startup mount;
ORACLE instance started.


SQL>
alter database archivelog;

Database altered.

SQL>
alter database open;

Database altered.

SQL>

在源端数据库中打开force logging

SQL>select force_logging from v$database;

FOR
---
NO

SQL>alter database force logging;

Database altered.

SQL> select force_logging from v$database;

FOR
---
YES

在源端数据库中打开supplemental log

SQL>select supplemental_log_data_min from v$database;

SUPPLEME
--------
NO

SQL>alter database add supplemental log data;

Database altered.


切换日志,使更改生效
SQL>
alter system switch logfile;

System altered.

SQL> select supplemental_log_data_min from v$database;

SUPPLEME
--------
YES

在源端数据库中关闭回收站


官方的说明是,由于一个已知的问题,回收站会对DDL触发器产生影响,因此需要关闭。由此可见,我们只需要在源库中关闭回收站即可。

SQL>show parameter recyclebin

NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
recyclebin string on


SQL>
alter system set recyclebin=off;

System altered.

SQL> show parameter recyclebin

NAME TYPE
------------------------------------ --------------------------------
VALUE
------------------------------
recyclebin string
OFF

确保goldengate能够连接到数据库的ASM实例

RAC中所有节点都要配置


在源端TNSNAMES.ORA中配置ASM实例信息
vi $ORACLE_HOME/network/admin/tnsnames.ora

ORADB_ASM =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = rac1-vip)(PORT = 1521))
(ADDRESS = (PROTOCOL = TCP)(HOST = rac2-vip)(PORT = 1521))
(LOAD_BALANCE = yes)
(CONNECT_DATA =
(SERVER = DEDICATED)
(SERVICE_NAME = +ASM)
)
)



在源端LISTENER.ORA中配置ASM实例的相关信息
vi $ORACLE_HOME/network/admin/listener.ora

SID_LIST_LISTENER_RAC1 =
(SID_LIST =
(SID_DESC =
(GLOBAL_DBNAME = oradb)
(ORACLE_HOME = /opt/app/oracle/product/10.2.0/db_1)
(SID_NAME = oradb1)
)
(SID_DESC =
(GLOBAL_DBNAME = +ASM)
(ORACLE_HOME = /opt/app/oracle/product/10.2.0/db_1)
(SID_NAME = +ASM1)
)

)

上面是rac1中的配置,rac2中的SID_LIST_LISTENER_xxx SID_NAME要相应修改


重启监听
lsnrctl reload


通过sqlplus sys/xxx@oradb_asm as sysdba来连接asm实例,能连上则说明配置成功

字符集

1. 目标数据库的字符集必须是源数据库字符集的超集
2. 数据库字符集必须为客户端应用程序字符集的超集

SQL>SHOW PARAMETER NLS_LANGUAGE

NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
nls_language string SIMPLIFIED CHINESE


SQL>
SHOW PARAMETER NLS_TERRITORY

NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
nls_territory string CHINA


SQL>
SELECT name, value$ from SYS.PROPS$ WHERE name = 'NLS_CHARACTERSET';

NAME
------------------------------
VALUE$
--------------------------------------------------------------------------------
NLS_CHARACTERSET
ZHS16GBK


SQL>
SHOW PARAMETER NLS_LENGTH_SEMANTICS

NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
nls_length_semantics string BYTE


SQL>

设置终端的字符集:
root
用户登录,源(rac1rac2)和目标端都做
cat >>/etc/bashrc<<EOF
NLS_LANG="SIMPLIFIED CHINESE_CHINA".ZHS16GBK
export NLS_LANG
EOF


创建goldengate数据库用户

源和目标端都需要

[oracle@gg1 ~]$sqlplus / as sysdba

SQL*Plus: Release 10.2.0.4.0 - Production on Mon Aug 1 22:31:42 2011

Copyright (c) 1982, 2007, Oracle. All Rights Reserved.


Connected to:
Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options

SQL>create tablespace goldengate;

Tablespace created.

SQL>create user goldengate identified by goldengate default tablespace goldengate;

User created.

SQL>grant connect,resource to goldengate;

Grant succeeded.

SQL>grant execute on utl_file to goldengate;

Grant succeeded.

SQL>


抽取进程使用的数据库用户需要额外的权限,我们将这些权限也授予数据库用户goldengate(在源端数据库中执行)

SQL>exec dbms_streams_auth.grant_admin_privilege('GOLDENGATE');

PL/SQL procedure successfully completed.

SQL>grant insert on system.logmnr_restart_ckpt$ to goldengate;

Grant succeeded.

SQL>grant update on sys.streams$_capture_process to goldengate;

Grant succeeded.

SQL>grant become user to goldengate;

Grant succeeded.

SQL>

为了确保GoldenGate正常运行,特别是在目标端,赋予goldengate用户DBA权限:
SQL>grant dba to goldengate;

UNDO的设置

goldengate使用flashback query从源数据库中读取undo表空间中的数据,以重建基于scn或时间点的读一致性。


建议设置如下:
UNDO_MANAGEMENT=AUTO
UNDO_RETENTION=86400
undo
表空间的大小按如下公式估计设置
<undo space> = <UNDO_RETENTION> * <UPS> + <overhead>
<undo space> is the number of undo blocks.
<UNDO_RETENTION> is the value of the UNDO_RETENTION parameter (in seconds).
<UPS> is the number of undo blocks for each second.
<overhead> is the minimal overhead for metadata (transaction tables, etc.).
Use the system view V$UNDOSTAT to estimate <UPS> and <overhead>.


该步骤在源端数据库执行即可



SQL>
show parameter undo


NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
undo_management string AUTO
undo_retention integer 900
undo_tablespace string UNDOTBS1


SQL>
alter system set undo_retention=86400;


System altered.


flashback any table 权限赋给extract用户


SQL>
grant flashback any table to goldengate;


Grant succeeded.


SQL>

安装DDL OBJECTS

在源端,以oracle用户登录sqlplus,执行以下脚本

执行marker_setup


确保goldengate的相关进程关闭状态,任何使用oracle的应用程序都已关闭,且不会有新的会话产生。然后执行下面命令

[goldengate@gg1 ~]$
cd /opt/gg/goldengate/
[goldengate@gg1 goldengate]$
sqlplus / as sysdba

SQL>
@marker_setup

Marker setup script


You will be prompted for the name of a schema for the GoldenGate database objects.
NOTE: The schema must be created prior to running this script.
NOTE: Stop all DDL replication before starting this installation.

Enter GoldenGate schema name:goldengate

……

Script complete.

SQL>

执行ddl_setup


确保所有的会话都已关闭

SQL>
@ddl_setup

GoldenGate DDL Replication setup script


Verifying that current user has privileges to install DDL Replication...
Checking user sessions...

Check complete.

You will be prompted for the name of a schema for the GoldenGate database objects.
NOTE: For an Oracle 10g source, the system recycle bin must be disabled. For Oracle 11g and later, it can be enabled.
NOTE: The schema must be created prior to running this script.
NOTE: Stop all DDL replication before starting this installation.

Enter GoldenGate schema name:goldengate

You will be prompted for the mode of installation.

To install or reinstall DDL replication, enter INITIALSETUP
To upgrade DDL replication, enter NORMAL
Enter mode of installation:INITIALSETUP

Working, please wait ...
Spooling to file ddl_setup_spool.txt

Using GOLDENGATE as a GoldenGate schema name, INITIALSETUP as a mode of installation.

Working, please wait ...

RECYCLEBIN must be empty.
This installation will purge RECYCLEBIN for all users.
To proceed, enter yes. To stop installation, enter no.

Enter yes or no:yes

……

Script complete.
SQL>



执行role_setup


创建一个名为ggs_ggsuser_role的角色,包含了ddl objects需要的权限
SQL>
@role_setup

GGS Role setup script


This script will drop and recreate the role GGS_GGSUSER_ROLE
To use a different role name, quit this script and then edit the params.sql script to change the gg_role parameter to the preferred name. (Do not run the script.)

You will be prompted for the name of a schema for the GoldenGate database objects.
NOTE: The schema must be created prior to running this script.
NOTE: Stop all DDL replication before starting this installation.

Enter GoldenGate schema name:goldengate
Wrote file role_setup_set.txt

PL/SQL procedure successfully completed.

Role setup script complete

Grant this role to each user assigned to the Extract, GGSCI, and Manager processes, by using the following SQL command:

GRANT GGS_GGSUSER_ROLE TO <loggedUser>

where <loggedUser> is the user assigned to the GoldenGate processes.
SQL>



将创建的角色授权给goldengate用户


SQL>
grant ggs_ggsuser_role to goldengate;

Grant succeeded.


SQL>



启用DDL触发器


SQL>
@ddl_enable

Trigger altered.


SQL>



安装可选的性能工具

安装dbms_shared_pool

如果系统中不存在dbms_shared_pool包,则手动执行脚本安装。如下

SQL> select object_name,object_type from dba_objects where object_name='DBMS_SHARED_POOL';


no rows selected

SQL>@?/rdbms/admin/dbmspool

Package created.


Grant succeeded.

View created.

Package body created.

SQL>select object_name,object_type from all_objects where object_name='DBMS_SHARED_POOL';

OBJECT_NAME OBJECT_TYPE

------------------------------ -------------------
DBMS_SHARED_POOL PACKAGE
DBMS_SHARED_POOL PACKAGE BODY

2 rows selected.



ddl_pin


ddl_pin
将触发器用到的plsql包放进内存中

SQL>
@ddl_pin goldengate

PL/SQL procedure successfully completed.

PL/SQL procedure successfully completed.

PL/SQL procedure successfully completed.

SQL>

配置goldengate

创建goldengate工作目录

源端和目标端:

[goldengate@rac1 goldengate]$
cd $GG_HOME
[goldengate@rac1 goldengate]$
./ggsci

GGSCI (rac1) 1>
create subdirs

Creating subdirectories under current directory /opt/gg/goldengate


Parameter files /opt/gg/goldengate/dirprm: created
Report files /opt/gg/goldengate/dirrpt: created
Checkpoint files /opt/gg/goldengate/dirchk: created
……

GGSCI (gg1) 2> exit



创建trail文件存放目录

源和目标端:

[goldengate@rac1 ~]$
mkdir /opt/gg/trails
[goldengate@rac1 ~]$
ls -l /opt/gg | grep trails
drwxr-xr-x 2 goldengate oinstall 4096 Nov 27 14:57 trails

[goldengate@rac1 ~]$



配置MANAGER

源端和目标端:

DYNAMICPORTLIST
中配置了GoldenGate(extractreplicat)进程使用的端口范围
PORT
参数指定MANAGER使用的端口
AUTORESTART
参数使抽取/复制进程失败后自动重启
配置MANAGER的参数,PURGEOLDEXTRACTS参数指定:当根据checkpoint发现已经完成抽取和复制的trail文件将被自动删除,但保留最近10个。
PURGEDDLHISTORY
PURGEMARKERHISTORY分别删除DDL历史表和marker表中的过期数据,以控制它们不会变得过于庞大。

GGSCI (gg1) 1>
edit params mgr

DYNAMICPORTLIST 7840-7914
PORT 5898
PURGEOLDEXTRACTS /opt/gg/trails/w1*, USECHECKPOINTS, MINKEEPFILES 10
PURGEOLDEXTRACTS /opt/gg/trails/w2*, USECHECKPOINTS, MINKEEPFILES 10
AUTORESTART ER *, RETRIES 3, WAITMINUTES 5
PURGEDDLHISTORY MINKEEPDAYS 3, MAXKEEPDAYS 5, FREQUENCYMINUTES 30
PURGEMARKERHISTORY MINKEEPDAYS 3, MAXKEEPDAYS 5, FREQUENCYMINUTES 30

~

~



全局参数设置


源端:

GGSCI (rac1) 2>
edit params ./globals
GGSCHEMA goldengate
~

~
~

目标端:

创建一个checkpoint
replicat
通过这个表来维护trail文件中的read position。这不是个必须的操作,如果没有这个表,则通过一个磁盘文件来维护

GGSCI (ggdb) 2>
dblogin userid goldengate,password goldengate
Successfully logged into database.


GGSCI (ggdb) 3>add checkpointtable goldengate.chkpoint

Successfully created checkpoint table GOLDENGATE.CHKPOINT.


GGSCI (ggdb) 4>edit params ./globals

GGSCHEMA goldengate
CHECKPOINTTABLE goldengate.chkpoint

~

~
~
~
"./globals" 2L, 44C written



配置源端extract

为了避免primary extract受到网络的影响,我们在源端和目标端之间增加一个data pump,这样的话,primary extract负责将数据从源数据中抽取出来,存在本地的trail文件中,然后data pump进程负责将本地trail文件中的数据传输到目标端的trail文件里。这样能提高更高的灵活性和可用性(当源和目标端之间的网络出现故障时,primary extract会继续抽取数据存到本地的trail),图解如下:



我们这里要同步4个用户下的所有表:dycommondatabase20dyulcentermanagedyacdb34dyulcenterm_bak
这里我们将它们分成两部分来测试,两个primary extract,一个负责同步dycommondatabase20dyulcentermanage下的数据,另一个负责同步dyacdb34dyulcenterm_bak

首先添加两个primary extract group,因为这是一个两节点的RAC,我们需要指定threads 2选项

GGSCI (rac1) 4>
add extract w1ext,tranlog,threads 2,begin now

EXTRACT added.

GGSCI (rac1) 5>
info all

Program Status Group Lag Time Since Chkpt

MANAGER STOPPED

EXTRACT STOPPED DYEXT 00:00:00 00:00:07

编辑w1ext的参数文件:

1. EXTTRAIL
参数指定该抽取进程对应的exttrail
2. DISCARDFILE
参数指定一个文件,用来记录不能正常处理的记录,这里使用追加方式,最大为5MB
3. TRANLOGOPTIONS ALTARCHIVELOGDEST
指定源数据库归档所在的路径。如果不确定,使用该SQL*Plus命令:show parameter log_archive_dest_1
4. TRANLOGOPTIONS ALTARCHIVEDLOGFORMAT
指定了归档文件的命名格式,可以通过SQL*Plus命令show parameter log_archive_format 来确定该格式
5.
由于源数据使用了ASM,这里通过tranlogoptions asmuser来提供登录ASM实例的用户名和密码(用户名必须是SYS)
6. DDL INCLUDE MAPPED
表示只捕获MAPPED范围内的DDL操作
7. DDLOPTIONS ADDTRANDATA
:当创建新的表时,自动为其启用追加日志
8. FETCHOPTIONS
MISSINGROW REPORT表示当extract需要获取的行在源库中无法定位时,extract进程继续运行,相关的错误信息会保存在discardfile参数指定的文件中;USESNAPSHOT表示extract使用flashback查询来从undo从获取一些数据,比如无法从redo中直接获取的UDT、嵌套表、XMLtype以及9i中的LOBNOUSELATESTVERSION使得extract当无法从undo中获取数据时,忽略该条件而不是从源表中获取当前值。
9. STATOPTIONS REPORTFETCH
:使用ggsci命令stats时,显示获取的行的统计信息
10. WARNLONGTRANS 1H, CHECKINTERVAL 5M
:当发现超过1个小时的长事务时,会在错误日志中产生一条warning5分钟检测一次

GGSCI (rac1) 6>
edit params w1ext

EXTRACT w1ext
USERID goldengate, PASSWORD goldengate
EXTTRAIL /opt/gg/trails/w1
DISCARDFILE w1extdsc,APPEND,MEGABYTES 5
TRANLOGOPTIONS ALTARCHIVELOGDEST +RECOVERY_DG
TRANLOGOPTIONS ALTARCHIVEDLOGFORMAT %t_%s_%r.dbf
TRANLOGOPTIONS ASMUSER SYS@ORADB_ASM,ASMPASSWORD BBB
DDL INCLUDE MAPPED
DDLOPTIONS ADDTRANDATA
FETCHOPTIONS, USESNAPSHOT, NOUSELATESTVERSION, MISSINGROW REPORT
STATOPTIONS REPORTFETCH
WARNLONGTRANS 1H, CHECKINTERVAL 5M
TABLE dycommondatabase20.*;
TABLE dyulcentermanage.*;



添加该extract对应的trail文件,用来存储抽取的数据。单个文件大小设置为100MB
GGSCI (rac1) 7>
add exttrail /opt/gg/trails/w1,extract w1ext, MEGABYTES 100
EXTTRAIL added.

添加secondary extract group,即data pump

GGSCI (rac1) 8>
ADD EXTRACT w1extdp, EXTTRAILSOURCE /opt/gg/trails/w1, BEGIN now
EXTRACT added.

编辑参数文件
RMTHOST
后面跟目标端的ip或主机名(需在hosts文件中有对应ip解析)manager进程的端口号;RMTTRAIL指定目标端的trail文件所在位置

GGSCI (rac1) 9>
edit params w1extdp

EXTRACT w1extdp
USERID GOLDENGATE, PASSWORD GOLDENGATE
RMTHOST 192.168.47.211, MGRPORT 5898
RMTTRAIL /opt/gg/trails/w1
DISCARDFILE w1extdpdsc,APPEND,MEGABYTES 5
TABLE dycommondatabase20.*;
TABLE dyulcentermanage.*;

~
~
添加该rmttrail

GGSCI (rac1) 10>
add rmttrail /opt/gg/trails/w1,extract w1extdp, megabytes 100
RMTTRAIL added.


类似的,我们添加第二组primary&secondary extract以及exttrail&rmttrail
GGSCI (rac1) 11>
add extract w2ext,tranlog,threads 2,begin now
EXTRACT added.

GGSCI (rac1) 12>
edit params w2ext

EXTRACT w2ext
USERID goldengate, PASSWORD goldengate
EXTTRAIL /opt/gg/trails/w2
DISCARDFILE w2extdsc,APPEND,MEGABYTES 5
TRANLOGOPTIONS ALTARCHIVELOGDEST +RECOVERY_DG
TRANLOGOPTIONS ALTARCHIVEDLOGFORMAT %t_%s_%r.dbf
TRANLOGOPTIONS ASMUSER SYS@ORADB_ASM,ASMPASSWORD BBB
DDL INCLUDE MAPPED
DDLOPTIONS ADDTRANDATA
FETCHOPTIONS, USESNAPSHOT, NOUSELATESTVERSION, MISSINGROW REPORT
STATOPTIONS REPORTFETCH
WARNLONGTRANS 1H, CHECKINTERVAL 5M
TABLE dyacdb34.*;
TABLE dyulcenterm_bak.*;.*;

GGSCI (rac1) 13>
add exttrail /opt/gg/trails/w2,extract w2ext, MEGABYTES 100
EXTTRAIL added.

GGSCI (rac1) 14>
ADD EXTRACT w2extdp, EXTTRAILSOURCE /opt/gg/trails/w2, BEGIN now
EXTRACT added.

GGSCI (rac1) 15>
edit params w2extdp

EXTRACT w2extdp
USERID GOLDENGATE, PASSWORD GOLDENGATE
RMTHOST 192.168.47.211, MGRPORT 5898
RMTTRAIL /opt/gg/trails/w2
DISCARDFILE w1extdpdsc,APPEND,MEGABYTES 5
TABLE dyacdb34.*;
TABLE dyulcenterm_bak.*;

~
~

GGSCI (rac1) 16>
add rmttrail /opt/gg/trails/w2,extract w2extdp, megabytes 100
RMTTRAIL added.

配置目标端replicat


回到目标端,使用goldengate用户登录到ggsci命令行中,对应前面的两组extractdata pump增加两个replicat

GGSCI (ggdb) 6>
add replicat w1rep,exttrail /opt/gg/trails/w1,checkpointtable goldengate.chkpoint
REPLICAT added.


这里的handlecollisions参数在目标端数据初始化并同步之后去掉

编辑参数文件:
1. assumetargetdefs:
由于在这里我们源端和目标端的表结构是完全一致的,因此使用这个参数来使replicat不用去查看相关的定义文件,从而提高效率
2. DDLOPTIONS REPORT
:将ddl的具体信息写入到报告文件中
3. BATCHSQL
:将相似的SQL语句放到一个数组中以加快执行速度。在normal模式下,repliat同一时间只应用一条sql语句。
4. DBOPTIONS DEFERREFCONST
:将完整性约束推迟到replicat事务提交以后再检测
5.
如果数据库版本在10.2.0.511.2.0.2以后,可以使用DBOPTIONS SUPPRESSTRIGGERSreplicat会话中禁用触发器。如果不是,应该在目标端数据库中禁用触发器(触发器产生的DML操作会从源端同步到目标端)
6. DBOPTIONS LOBWRITESIZE
:将要写入目标库的LOB数据缓存在内存中,当达到参数中指定的大小时写入数据,以减少I/O。这个值的范围是2KB1MB,默认为32KB
7. DDLERROR DEFAULT DISCARD RETRYOP MAXRETRIES 5 RETRYDELAY 20
:当出现DDL错误,重试5次,时间间隔为20秒。如果失败,replicat会继续运行,但相关信息会记录在discardfile中。
8. HANDLECOLLISIONS
:当replicat往表中插入一条记录,而该记录已经存在,则进行覆盖;当replicat在表中试图更新或删除一条记录,而该记录不存在,则该操作被丢弃。这个参数一般在initial-data load中使用,在源和目标端的数据同步之后应该将该参数删除
9. MAP TARGET
:源表和目标表之间的映射,可以使用通配符

GGSCI (ggdb) 7>
edit params w1rep

REPLICAT w1rep
ASSUMETARGETDEFS
USERID GOLDENGATE, PASSWORD GOLDENGATE
DISCARDFILE w1repdsc,APPEND,MEGABYTES 5
DDL INCLUDE MAPPED
DDLOPTIONS REPORT
BATCHSQL
DBOPTIONS DEFERREFCONST
DBOPTIONS LOBWRITESIZE 102400
HANDLECOLLISIONS
DDLERROR DEFAULT DISCARD RETRYOP MAXRETRIES 5 RETRYDELAY 20
MAP dycommondatabase20.* , TARGET dycommondatabase20.* ;
MAP dyulcentermanage.* , TARGET dyulcentermanage.* ;

~

~
~

同样地再配置一个replicat进程 w2rep
GGSCI (ggdb) 8>
add replicat w2rep,exttrail /opt/gg/trails/w2,checkpointtable goldengate.chkpoint
REPLICAT added.


GGSCI (ggdb) 9>edit params w2rep

REPLICAT w2rep
ASSUMETARGETDEFS
USERID GOLDENGATE, PASSWORD GOLDENGATE
DISCARDFILE w2repdsc,APPEND,MEGABYTES 5
DDL INCLUDE MAPPED
DDLOPTIONS REPORT
BATCHSQL
DBOPTIONS DEFERREFCONST
DBOPTIONS LOBWRITESIZE 102400
HANDLECOLLISIONS
DDLERROR DEFAULT DISCARD RETRYOP MAXRETRIES 5 RETRYDELAY 20
MAP dyacdb34.* , TARGET dyacdb34.* ;
MAP dyulcenterm_bak.* , TARGET dyulcenterm_bak.* ;

~



--做到这了

ADD TRANDATA

在启动goldengate相关进程之前,使用trandata对源数据库中需要同步的表启用对象级别的追加日志

--在源端
GGSCI (rac1) 17>
dblogin userid goldengate,password goldengate
Successfully logged into database.

--GGSCI (xxx) 2> list tables goldengate.*


GGSCI (rac1) 18>
add trandata dycommondatabase20.*
Logging of supplemental redo data enabled for table DYCOMMONDATABASE20.AUDITCONFIG.


Logging of supplemental redo data enabled for table DYCOMMONDATABASE20.AUDITHISTORY.
……

如果表中没有主键和唯一键,会产生一条警告,例如:
2011-12-08 22:53:11 WARNING OGG-00869 No unique key is defined for table UDPPACKPORTSET. All viable columns will be used to represent the key, but may not guarantee uniqueness. KEYCOLS may be used to define the key.

GGSCI (rac1) 19>
add trandata dyulcentermanage.*
……
GGSCI (rac1) 20>
add trandata dyacdb34.*
……
GGSCI (rac1) 21>
add trandata dyulcenterm_bak.*
……

业务表中没有主键和唯一键,这是个很糟糕的习惯,这里就不批判了...

数据初始化(initial data load)


在进行同步以前,要先对目标端的数据进行初始化
在线迁移有几种方法,这里采取比较靠谱的两种:通过数据库工具(expdp/impdp)和通过文件导入到replicat。我们将这两种方法分别应用在两组抽取/复制对应的4个数据库用户上。w1ext对应的dycommondatabase20用户和dyulcentermanage使用前者,w2ext对应的dyacdb34dyulcenterm_bak使用后者。


通过expdp/impdp来迁移初始数据

流程图如下:

准备工作

1. 禁用DDL的抽取和复制
extractreplicat参数中将ddl去掉。实际上,若能保证在expdp导出数据期间不会有ddl操作,可以忽略此项
2.
replicat中使用HANDLECOLLISIONS参数。每一个目标表都必须由一个主键或唯一键。如果没有,使用KEYCOLS选项来代替,如果不能指定,祈祷不会出现两条完全一样的记录吧...


在源端和目标端打开MANAGER


在源端和目标端执行ggsci命令start mgr,启动manager进程

在源端,打开extract进程
GGSCI (rac1) 35>
start ext w1ext

Sending START request to MANAGER ...

EXTRACT W1EXT starting

GGSCI (rac1) 36>start ext w1extdp

Sending START request to MANAGER ...

EXTRACT W1EXTDP starting


GGSCI (rac1) 37>info all

Program Status Group Lag Time Since Chkpt


MANAGER RUNNING
EXTRACT RUNNING W1EXT 00:00:00 00:00:05
EXTRACT RUNNING W1EXTDP 00:00:00 02:00:51
EXTRACT STOPPED W2EXT 00:00:00 01:54:09
EXTRACT STOPPED W2EXTDP 00:00:00 01:50:00


在源端,执行expdp导出数据

首先需要在源数据中创建一个directory

SQL>
create directory expdpdir as '/bak/expdpdata';

Directory created.


执行导出:
[oracle@rac1 ~]$
expdp system/aaa directory=expdpdir dumpfile=w1data_%U.dmp logfile=w1data.log schemas=dycommondatabase20, dyulcentermanage parallel=4


将初始数据导入到目标数据库

先通过ftpexpdp导出的文件拷贝到目标主机上:


目标端:
[oracle@ggdb ~]$
ftp 192.168.47.241
Connected to 192.168.47.241.
……
ftp>
cd /bak/expdpdata
250 Directory successfully changed.
ftp>
lcd /bak/impdpdata
Local directory now /bak/impdpdata
ftp>
bin
200 Switching to Binary mode.
ftp>
mget w1*

创建directory
SQL>
create directory impdpdir as '/bak/impdpdata';

Directory created.

需要的表空间也要先创建好

数据导入:
impdp system/aaa directory=impdpdir dumpfile=w1data_%U.dmp logfile=w1data_impdp.log schemas=dycommondatabase20, dyulcentermanage parallel=4


由于数据库版本是10.2.0.4,需要在目标库中禁用我们要同步的用户中的触发器
SQL>

begin
for x in (select owner,trigger_name from dba_triggers

where owner in ('DYCOMMONDATABASE20', 'DYULCENTERMANAGE'))
loop
execute immediate 'alter trigger '||x.owner||'.'||x.trigger_name||' disable';
end loop;
end;

/

在目标端,启动复制进程

impdp导入结束之后,启动复制进程

GGSCI (ggdb) 51>
start rep w1rep

Sending START request to MANAGER ...

REPLICAT DYREP starting

通过info replicat dyrep 命令来查看复制的状态

在目标端的数据变化追上initial-load结束的时间点后,关闭handlecollisions参数:先使用SEND REPLICAT命令使之对运行中的replicat生效,然后修改参数文件,使其在下一次启动时生效。

GGSCI (ggdb) 57>
SEND REPLICAT w1rep, NOHANDLECOLLISIONS

Sending NOHANDLECOLLISIONS request to REPLICAT W1REP ...

W1REP No tables found matching GOLDENGATE.* to set NOHANDLECOLLISIONS.

然后修改w1rep参数文件,去掉handlecollisions参数



通过文件到replicat方式来迁移初始数据


流程图如下所示

expdp/impdp的优势之一就是导入很快,而通过文件到replicat的方式,需要一条一条地应用到目标库中,因此导入是最慢的

准备工作

1. 禁用DDL的抽取和复制
2.
确认目标端表是空的
3.
禁用外键及check约束
4.
replicat中使用HANDLECOLLISIONS参数
5.
可以删除大表中的索引以加快导入速度,导入结束后再重建索引


在源端和目标端打开MANAGER


在源端和目标端执行ggsci,打开manager
start mgr

这一步在上面已经做过了,就不再重复

配置初始化extract

在源端,添加一个extract,并配置参数

GGSCI (rac1) 1>
add extract einit,sourceistable
EXTRACT added.


参数说明:
1.SOURCEISTABLE
:将源数据库中指定的表中的所有记录导出到指定的文件中,一般用于initial-data load
2. RMTFILE
:目标文件的路径。设置最大文件数和最大文件大小,否则当文件超过2GB时会出错

GGSCI (rac1) 2>
edit params einit

EXTRACT einit

SOURCEISTABLE
USERID GOLDENGATE, PASSWORD GOLDENGATE
RMTHOST 192.168.47.211, MGRPORT 5898
RMTFILE /opt/gg/trails/initdata,purge,maxfiles 100,megabytes 1024
TABLE dyacdb34.*;
TABLE dyulcenterm_bak.*;



在目标库中创建表空间、用户及表结构


在导入数据以前,需要准备好这个容器
我们使用expdp来导出,不导出表中的数据:

源端:
[oracle@rac1 ~]$
expdp system/aaa directory=expdpdir dumpfile=w2data.dmp logfile=w2data.log schemas= DYACDB34,DYULCENTERM_BAK exclude=table_data,table_statistics

目标端:
[oracle@ggdb ~]$
ftp 192.168.47.241
Connected to 192.168.47.241.

……
ftp>cd /bak/expdpdata
250 Directory successfully changed.

ftp>lcd /bak/impdpdata
Local directory now /bak/impdpdata

ftp>bin
200 Switching to Binary mode.

ftp>mget w2*

导入用户及其表结构:
[oracle@ggdb ~]$
impdp system/aaa directory=impdpdir dumpfile=w2data.dmp logfile=w2data_imp.log schemas=DYACDB34,DYULCENTERM_BAK

禁用约束:
SQL>

begin
for x in (select owner,table_name,constraint_name from dba_constraints where owner in ('DYACDB34', 'DYULCENTERM_BAK') and constraint_type in ('C','R')) loop
execute immediate 'alter table '||x.owner||'.'||x.table_name||
' disable constraint '||x.constraint_name;
end loop;
dbms_output.put_line('ok');
end;
/


禁用触发器:
SQL>

begin
for x in (select owner,trigger_name from dba_triggers

where owner in ('DYACDB34', 'DYULCENTERM_BAK'))
loop
execute immediate 'alter trigger '||x.owner||'.'||x.trigger_name||' disable';
end loop;
end;
/


在源端,打开抽取进程

GGSCI (rac1) 3>start ext w2ext
Sending START request to MANAGER ...

EXTRACT W2EXT starting

GGSCI (rac1) 4>start ext w2extdp
Sending START request to MANAGER ...

EXTRACT W2EXTDP starting

GGSCI (rac1) 5>info all

Program Status Group Lag Time Since Chkpt


MANAGER RUNNING
EXTRACT RUNNING W1EXT 00:00:00 00:00:08
EXTRACT RUNNING W1EXTDP 00:00:00 00:00:05
EXTRACT RUNNING W2EXT 02:58:57 00:00:04
EXTRACT RUNNING W2EXTDP 00:00:00 03:03:40



在源端,启动initial-load extract


[goldengate@rac1 ~]$
cd $GG_HOME
[goldengate@rac1 goldengate]$
./extract paramfile dirprm/einit.prm reportfile /opt/gg/einit.log

通过查看报告文件监控抽取的过程和结果,直到抽取结束



配置初始化replicat

在目标端,添加一个replicat,并配置参数

GGSCI (rac2) 1>
add replicat rinit,specialrun
REPLICAT added.


参数说明:
1. SPECIALRUN
:一次性的导入,不会产生checkpoint。一般用于通过文件到replicat的初始数据的加载,或者批量进行更新同步
2. EXTFILE
:指定数据所在文件的路径,文件名需要输入全称
3. ASSUMETARGETDEFS
:由于源和目标数据需要同步的表的结构一致,指定该参数以提高效率

GGSCI (rac2) 2>
edit params rinit

REPLICAT rinit
SPECIALRUN
USERID GOLDENGATE,PASSWORD GOLDENGATE
EXTFILE /opt/gg/trails/initdata000000
ASSUMETARGETDEFS
MAP DYACDB34.* , TARGET DYACDB34.* ;
MAP DYULCENTERM_BAK.* , TARGET DYULCENTERM_BAK.* ;




在目标端,启动initial-load replicat

[goldengate@ggdb goldengate]$cd $GG_HOME
[goldengate@ggdb goldengate]$
./replicat paramfile dirprm/rinit.prm reportfile /opt/gg/rinit.log

我的测试环境中由于生成了3extfile,需要修改rinit参数文件,将extfile文件名修改,再执行2次。也可以增加两个specialrunreplicat来同时执行。导入结束后注意查看生成的日志,确保没有出现错误



在目标端,启动复制进程

initial-load replication结束之后,启动复制进程

GGSCI (ggdb) 51>
start rep w2rep

Sending START request to MANAGER ...

REPLICAT W2REP starting

通过info replicat w2rep 命令来查看复制的状态

在目标端的数据变化追上initial-load结束的时间点后,关闭handlecollisions参数:先使用SEND REPLICAT命令使之对运行中的replicat生效,然后修改参数文件,使其在下一次启动时生效。

GGSCI (ggdb) 57>
SEND REPLICAT w2rep, NOHANDLECOLLISIONS

Sending NOHANDLECOLLISIONS request to REPLICAT W2REP ...

W2REP No tables found matching GOLDENGATE.* to set NOHANDLECOLLISIONS.

然后修改w2rep参数文件,去掉handlecollisions参数

重新启用目标数据库中导入数据前禁用的约束:

SQL>

begin
for x in (select owner,table_name,constraint_name from dba_constraints where owner in ('DYACDB34', 'DYULCENTERM_BAK') and constraint_type in ('C','R')) loop
execute immediate 'alter table '||x.owner||'.'||x.table_name||
' enable constraint '||x.constraint_name;
end loop;
dbms_output.put_line('ok');
end;
/





可以试着在源数据库中进行一些ddldml操作,在目标端使用ggsci命令stats或在数据库中查看更改是否被应用了。

这里GoldenGate就配置完成了

日常管理和维护

启动关闭使用startstop ggsci命令,查看状态使用infostatus命令,查看统计信息使用stats命令,注意查看discard文件和其他日志文件(比如goldengate安装目录下的ggserror.log和源端user_dump_dest下的ggs_ddl_trace.log)这里不再赘述

故障切换


RAC环境中,最好将GoldenGate相关的二进制文件、参数文件、trail文件等都放在共享存储上。正常情况下,由其中一个节点来执行GoldenGate进程。当这个节点发生故障时,可以将GoldenGate文件所在目录重新挂载到另一个节点中,这样就可以从中断的地方继续工作。

在这个测试中,extract进程运行在rac1中,如果rac1发生了故障,rac2准备接管(相关os用户和目录应该在先前已经建好)

挂载到rac2上:
[root@rac2 ~]#mount /dev/sdg1 /opt/gg
[root@rac2 ~]#ls -l /opt/gg
total 24
drwxr-xr-x 14 goldengate oinstall 4096 Nov 28 01:57 goldengate
drwxrwxr-x 2 goldengate oinstall 16384 Nov 28 01:05 lost+found
drwxr-xr-x 2 goldengate oinstall 4096 Nov 28 01:57 trails

然后切换到goldengate用户,启动managerextract进程

[root@rac2 ~]#su - goldengate
[goldengate@rac2 ~]$cd /opt/gg/goldengate/
[goldengate@rac2 goldengate]$./ggsci

GGSCI (rac2) 1>start mgr

Manager started.

GGSCI (rac2) 2>info all

Program Status Group Lag Time Since Chkpt

MANAGER RUNNING
EXTRACT ABENDED DYEXT 00:00:00 00:01:19
EXTRACT ABENDED DYEXTDP 00:00:00 00:01:18

GGSCI (rac2) 3>start ext *

Sending START request to MANAGER ...
EXTRACT DYEXT starting

Sending START request to MANAGER ...
EXTRACT DYEXTDP starting

GGSCI (rac2) 4>info all

Program Status Group Lag Time Since Chkpt

MANAGER RUNNING
EXTRACT RUNNING DYEXT 00:00:00 00:00:01
EXTRACT ABENDED DYEXTDP 00:00:00 00:14:53

此时primary extract进程开始正常工作,data pump进程处于abended状态。这是正常的,这是因为rac1发生了节点故障,相关事务在rac2上执行回滚,但extract不能捕获到回滚动作,这些事务还存在于extract事务列表中。
默认情况下,GoldenGate会花一定的时间确认该事务(默认为10分钟),然后从列表中清除它们。然后再启动data pump进程,可以正常运行:

GGSCI (rac2) 24>start ext dyextdp

Sending START request to MANAGER ...
EXTRACT DYEXTDP starting


GGSCI (rac2) 25>info all

Program Status Group Lag Time Since Chkpt

MANAGER RUNNING
EXTRACT RUNNING DYEXT 00:00:00 00:00:02
EXTRACT RUNNING DYEXTDP 00:00:00 08:23:02


注意rac2tnsnames.oralistener.ora里关于ASM已经正确配置,否则extract进程会起不来。

一些错误和警告信息

错误:

OGG-00052 Oracle GoldenGate Delivery for Oracle, dyrep.prm: No replication maps specified.
replicat
配置文件中必须指定map , target参数


OGG-00212 Oracle GoldenGate Delivery for Oracle, dyrep.prm: Invalid option for MAP:xxx
replicat
参数中map子句语法不正确(,/等符号前加上空格)

OGG-00259 Oracle GoldenGate Delivery for Oracle, dyrep.prm: Schema name cannot be wildcarded.
replicat
参数的map ,target参数中,schema不能使用通配符

dblogin
登录失败1
dblogin userid goldengate,password goldengate
报错 ERROR: Failed to open data source for user GOLDENGATE
syssystem或其它具有DBA权限的用户可以使用dblogin登录,然后再登录goldengate能成功
.
解决办法:授予goldengate用户select any dictionary权限


dblogin登录失败2:环境变量设置有问题

[goldengate@rac1 goldengate]$ echo $ORACLE_SID

rac1

解决办法:[goldengate@rac1 ~]$ export ORACLE_SID=orcl1


警告:

OGG-00869 Oracle GoldenGate Command Interpreter for Oracle: No unique key is defined for table xxx. All viable columns will be used to represent the key, but may not guarantee uniqueness. KEYCOLS may be used to define the key.
xxx
表没有唯一键,若数据库中不能对表添加主键或唯一约束,可以使用goldengateKEYCOLS参数指定能准确标识行的列。否则该表的所有字段都将被保存到redo中用以标识更改的行。


OGG-01756 Oracle GoldenGate Command Interpreter for Oracle: Cannot register EXTRACT DYEXT with database because of the following SQL error: OCI Error ORA-06550: line 1, column 3128: PL/SQL: ORA-00942: table or view does not exist ORA-06550: line 1, column 3117: PL/SQL: SQL Statement ignored (status = 0). See Extract user privileges in the Oracle GoldenGate for Oracle Installation and Setup Guide. You can manually register this group with the REGISTER EXTRACT command.
处理方法请见数据库方面的准备中的创建goldengate数据库用户后半段

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics