《Oracle 高级复制技术介绍及应用》-HA技术-应用场景-实战演练

Oracle 高级复制技术介绍及应用》

引言:Oracle 高级复制技术是Oracle最早提出的HA容灾解决方案,起源于Oracle 8i系统,至今在11G官方文档上依然可以找到高级复制说明文档。这个技术因为其古老我想大多数80后童靴只闻其声~未闻其形。在生产环境下可能更没有使用过。由于高级复制技术在当下已经属于非主流渐渐的退出了其历史舞台,逐步被Data Guard、Golden Gate、Streams等新技术所代替,导致其没落的原因是由于其本身机制问题,后面我们会介绍其原理和应用场景。虽说这种技术非常的原始,但可能还有一些老系统会继续使用着。一种技术我们需要知道其优点、应用场景、局限性、风险性,这样才能得心应手,有针对性的去使用,例如 原始的汇编语言现在几乎没有人在使用了,但由于其接近硬件的特点,在某些嵌入式系统中效率是最快的。所以说非主流技术我们也应该去了解一下,好的架构师就是需要具备在N种不同技术簇中,选择出最适合的。始终问自己:能不能压缩成本,能不能解决问题,能不能更易用。

《心得体会》

系统不同,架构不同,发现问题解决问题

好架构是系统演进出来的

保持简洁

易于扩展,监控,容错

高级复制技术应用架构

clip_image002

高级复制技术类型

(1)基于多主节点的复制(使用内部触发器)Master to Master

(2)基于物化视图的复制

(3)混合架构复制(既有多主节点又有物化视图,如上图所示)

说明高级复制和流复制在机制和应用场景上的异同点

刚才我们看到了高级复制的架构图,下面我们再来看一下流复制的架构图

基于redo流复制

clip_image004

基于归档流复制

clip_image006

实时流复制

clip_image008

流复制原理

原理:通过capture(捕获),propagation(传播),apply(应用)三个进程,将数据复制到目标数据库。三个步骤中间通过queue(oracle的advanced queue)进行连接,通过一系列的rule定义需要复制的数据,既可以是单点复制,也可以是多点复制。内部原理为capture进程从redo log中发掘dml和ddl操作,然后生成独特的LCR(Logical Change Record)数据单元并存于advanced queue中,propagation进程再将LCR queue传输到目标数据库,apply进程将接收的queue进行解析,并应用LCR中的DML和DDL操作,完成数据的复制。capture既可以在源库本地捕获,也可以在目标库捕获(downstream),如果是本地捕获,capture直接从redo log中读取信息;如果是downstream捕获,capture从redo log或archive log中捕获信息,中间的日志传输机制跟data guard一样。

从两张图中我们可知这两种技术的实现原理是不同的

高级复制原理

clip_image010

原理:当发生DML操作的时候,高级复制使用“内部触发器”捕获变化动作,并将这些操作封装在Remote Procedure Calls(RPCs就是一个存储过程)里,并借助Deferred Transaction Queue把RPCs推送到目标数据库,目标数据库利用“内部触发器”执行推送过来的RPCs(存储过程代码)从而实现了数据的复制。

高级复制优缺点汇总-免费

(1)需要稳定的网络环境,当网络因故障中断时,会出现丢包现象,从而丢失数据。

(2)适用于数据量小的场景,由于使用的是内部触发器原理,会消耗大量系统资源,当数据量很大的时候,会处理不过来。

(3)传输的速度较高,因为高级复制是基于事务触发的,当操作完之后马上触发->传递->应用

(4)高级复制不支持DDL语句复制,如果想执行DDL操作,先需要suspend复制组(承担高级复制的单位叫复制组),此时复制表只能查询不可执行任何DML语句,执行想要的DDL语句,然后重新generate复制支持,最后将复制组恢复到应用状态。当你不小心直接执行了DDL语句后会破坏掉复制环境,这时需将该表移出复制环境->删除->重新创建或从其他节点复制过来。

(5)基于分布式事务,当事务一开始复制就开始了,当事务出现问题复制也会出现问题,当事务回滚复制也会回滚。由于与事务紧密相关,可能会出现阻塞、争用、锁定现象,对用户操作影响较大。

当出现死锁现象时,我们可以broken掉对应的job作业

如果还不行,可以alter system kill session终止死锁会话

如果再不行,就需要利用v$lock查出类型为’RQ’的分布式死锁,再利用sid从v$process、v$session、v$bgprocess等视图中查出系统后台进程,从操作系统层面杀掉后台进程。

在重新建立job和相关复制环境

(6)Master to Master提供节点冗余,负载均衡的复制方式

所有节点关系是对等的,数据是对等的,当其中一个Master不可用时,可以直接切换到其他Master

负载均衡,例如有三个Master相互复制,用户1&2&3可以分别连接这三个Master主节点,实现负载均衡

常用于容灾场景

(7)支持同步和异步复制

同步:可以实时推送,操作后立马触发->复制->应用,因为复制的表上就建立了trigger和package。

异步:可以设置定时job(后台进程控制),定时、批量的复制数据,使用Deferred Transaction Queue实现。

(8)事务和依赖

高级复制自动处理事务的依赖关系,如果B事务访问A事务,并且A事务有更新,称作B事务对A事务有依赖

高级复制自动解决数据的约束关系

高级复制根据分布式事务方式,解决事务的一致性问题,比GG、Streams对事务的控制力更强,对用户操作参与更多,我们应该尽量把用户操作和数据复制分离开来,不要混淆在一起,提高系统的稳定性、健壮性。

(9)基于物化视图的高级复制

对网络质量要求不高,由于非实时传递,因此可以中断

定时、批量的复制数据,直接利用解析物化视图日志的方式复制数据

原理图

clip_image012

流复制优缺点汇总-免费

(1)也需要稳定的网络环境,如果网络中断,流复制就会失败

(2)Oracle自己的后台进程使用log miner工具从redo log或archive log中挖掘DML和DDL操作,对系统资源影响较小,streams是基于日志的分析,高级复制是基于触发器原理

(3)传输的速度会慢一些,因为日志挖掘本身就慢,再转换成SQL语句,应用SQL语句。我自己测试3秒钟延迟

(4)流复制支持DDL语句复制,采用DDL LCRs方式来记录DDL操作的改变

(5)流复制按照SCN号顺序捕获->传递->应用,只有redo数据写入log中才被挖掘,效率没有高级复制快

(6)用户操作和数据复制分离开来,数据复制过程中不会对用户操作有影响,增强了系统的稳定性、易用性

(7)流复制和高级复制都使用了高级队列来传输信息

(8)流复制也是Oracle HA技术之一,常用于容灾场景

演示针对某个表使用高级复制进行数据同步(Master to Master 内部触发器机制)

版本info

OS Version:Oracle Linux Release 6 Update 4 for x86_64 (64 Bit)

Oracle Version:Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Production

linux.x64_11gR2_database_1of2.zip + linux.x64_11gR2_database_2of2.zip

Virtualbox Version:4.2.16

作者 info

User:leonarding 刘盛

Date:2013.09.12

Blog:www.leonarding.com

Site:China-beijing

总体架构图

高级复制  

wKioL1LNE3KQ_7HaAAB6D8nj0fY310.jpg

1.准备工作

检查初始化参数,source和destination节点都需要检查,区分一下名字,好理解哪个是源端,哪个是目标端

Source库

SYS@leo1> select * from global_name;

GLOBAL_NAME

--------------------------------------------------------------------------------

LEO1

Destination库

SYS@leo2> select * from global_name;

GLOBAL_NAME

--------------------------------------------------------------------------------

LEO2

确认global_name是否为ture,高级复制要求global_names=true

SYS@leo1> show parameter global_name

NAME TYPE VALUE

------------------------------------ ----------- ------------------------------

global_names boolean FALSE

SYS@leo1> alter system set global_names=true;

System altered.

SYS@leo1> show parameter global_name

NAME TYPE VALUE

------------------------------------ ----------- ------------------------------

global_names boolean TRUE

确认job_queue_processes>0,oracle11g 默认是1000

alter system set job_queue_processes=1000; 可以通过这条语句修改

SYS@leo1> show parameter job_queue_processes

NAME TYPE VALUE

------------------------------------ ----------- ------------------------------

job_queue_processes integer 1000

SYS@leo1> show parameter db_domain

NAME TYPE VALUE

------------------------------------ ----------- ------------------------------

db_domain string

高级复制技术要求global_name必须带域名,因此leo1后需要加后缀,什么后缀都可以

源端

SYS@leo1> alter database rename global_name to leo1.com;

Database altered.

SYS@leo1> select * from global_name;

GLOBAL_NAME

--------------------------------------------------------------------------------

LEO1.COM

目标端

SYS@leo2> alter database rename global_name to leo2.com;

Database altered.

SYS@leo2> select * from global_name;

GLOBAL_NAME

--------------------------------------------------------------------------------

LEO2.COM

2.配置tnsnames.ora文件

根据实际情况修改ip地址和service_name服务名,port一般都使用默认值1521

SYS@leo1> show parameter service_names

NAME TYPE VALUE

------------------------------------ ----------- ------------------------------

service_names string leo1

[oracle@leonarding2 admin]$ vim tnsnames.ora

leo1 =

(DESCRIPTION =

(ADDRESS_LIST =

(ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.1.102)(PORT = 1521))

)

(CONNECT_DATA =

(SERVER = DEDICATED)

(SERVICE_NAME = leo1)

)

)

leo2 =

(DESCRIPTION =

(ADDRESS_LIST =

(ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.1.103)(PORT = 1521))

)

(CONNECT_DATA =

(SERVER = DEDICATED)

(SERVICE_NAME = leo2)

)

)

启动监听并tnsping测试连接字符串的连通性,两个节点都要测试成功

[oracle@leonarding2 admin]$ lsnrctl status

LSNRCTL for Linux: Version 11.2.0.1.0 - Production on 12-SEP-2013 19:24:08

Copyright (c) 1991, 2009, Oracle. All rights reserved.

Connecting to (DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=source)(PORT=1521)))

TNS-12541: TNS:no listener

TNS-12560: TNS:protocol adapter error

TNS-00511: No listener

Linux Error: 111: Connection refused

[oracle@leonarding2 admin]$ lsnrctl start

LSNRCTL for Linux: Version 11.2.0.1.0 - Production on 12-SEP-2013 19:25:46

Copyright (c) 1991, 2009, Oracle. All rights reserved.

TNS-01106: Listener using listener name LISTENER has already been started

[oracle@leonarding2 admin]$ lsnrctl status

LSNRCTL for Linux: Version 11.2.0.1.0 - Production on 12-SEP-2013 19:25:50

Copyright (c) 1991, 2009, Oracle. All rights reserved.

Connecting to (DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=source)(PORT=1521)))

STATUS of the LISTENER

------------------------

Alias LISTENER

Version TNSLSNR for Linux: Version 11.2.0.1.0 - Production

Start Date 12-SEP-2013 19:24:11

Uptime 0 days 0 hr. 1 min. 38 sec

Trace Level off

Security ON: Local OS Authentication

SNMP OFF

Listener Parameter File /u01/app/oracle/product/11.2.0/db_1/network/admin/listener.ora

Listener Log File /u01/app/oracle/diag/tnslsnr/leonarding2/listener/alert/log.xml

Listening Endpoints Summary...

(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=source)(PORT=1521)))

Services Summary...

Service "leo1" has 1 instance(s).

Instance "leo1", status READY, has 1 handler(s) for this service...

Service "leo1XDB" has 1 instance(s).

Instance "leo1", status READY, has 1 handler(s) for this service...

The command completed successfully

[oracle@leonarding2 admin]$ tnsping leo1

TNS Ping Utility for Linux: Version 11.2.0.1.0 - Production on 12-SEP-2013 19:26:00

Copyright (c) 1997, 2009, Oracle. All rights reserved.

Used parameter files:

Used TNSNAMES adapter to resolve the alias

Attempting to contact (DESCRIPTION = (ADDRESS_LIST = (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.1.102)(PORT = 1521))) (CONNECT_DATA = (SERVER = DEDICATED) (SERVICE_NAME = leo1)))

OK (0 msec)

[oracle@leonarding2 admin]$ tnsping leo2

TNS Ping Utility for Linux: Version 11.2.0.1.0 - Production on 12-SEP-2013 19:29:54

Copyright (c) 1997, 2009, Oracle. All rights reserved.

Used parameter files:

Used TNSNAMES adapter to resolve the alias

Attempting to contact (DESCRIPTION = (ADDRESS_LIST = (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.1.103)(PORT = 1521))) (CONNECT_DATA = (SERVER = DEDICATED) (SERVICE_NAME = leo2)))

OK (10 msec)

3.创建高级复制专属用户repadmin和数据库业务用户ar

每个用户都要有自己独立的表空间,源端和目标端都要创建

create tablespace repadmin datafile '/u01/app/oracle/oradata/leo1/repadmin_01.dbf' size 50M autoextend off;

create tablespace repadmin datafile '/u01/app/oracle/oradata/leo2/repadmin_01.dbf' size 50M autoextend off;

SYS@leo1> create user repadmin identified by oracle default tablespace repadmin;

User created.

SYS@leo1> grant dba to repadmin;

Grant succeeded.

执行存储过程

SYS@leo1> execute dbms_repcat_admin.grant_admin_any_schema('repadmin'); 注册管理者

PL/SQL procedure successfully completed.

SYS@leo1> execute dbms_defer_sys.register_propagator('repadmin'); 注册传播者

PL/SQL procedure successfully completed.

这里都使用了一个repadmin用户,也可以分别建立用户

说明:repadmin用户是高级复制环境的管理员,使用它可以建立复制组(承担高级复制任务的单位),加入复制对象,启动复制组等任务,为了操作方便我们给予了DBA权限。

创建数据库业务用户ar,源端和目标端都需要创建

create tablespace ar datafile '/u01/app/oracle/oradata/leo1/ar_01.dbf' size 50M autoextend off;

create tablespace ar datafile '/u01/app/oracle/oradata/leo2/ar_01.dbf' size 50M autoextend off;

SYS@leo1> create user ar identified by oracle default tablespace ar;

User created.

SYS@leo1> grant connect,resource to ar;

Grant succeeded.

说明:ar用户只是用来创建一些业务表,不负责高级复制的管理,不需要太高的权限

4.创建业务表

源端

SYS@leo1> conn ar/oracle

Connected.

AR@leo1> create table t (x int primary key);

Table created.

目标端

SYS@leo2> conn ar/oracle

Connected.

AR@leo2> create table t (x int primary key);

Table create

说明:源端和目标端表结构必须一致,才能正常复制。

我们的复制思路:从主节点leo1复制到另一个主节点leo2上,针对t表使用高级复制进行数据同步。

5.创建到目标端dblink

使用repadmin用户创建dblink,还有如果你设置了global_name,那么dblink名字必须和global_name一致

源端

AR@leo1> conn repadmin/oracle

Connected.

REPADMIN@leo1> create database link leo2.com connect to repadmin identified by oracle using 'leo2';

Database link created.

已经创建成功

REPADMIN@leo1> select db_link from dba_db_links;

DB_LINK

--------------------------------------------------------------------------------

LEO2.COM

远程测试

REPADMIN@leo1> select * from global_name@leo2.com;

GLOBAL_NAME

--------------------------------------------------------------------------------

LEO2.COM

REPADMIN@leo1> select name from v$database@leo2.com;

NAME

---------

LEO2

本地测试

REPADMIN@leo1> select name from v$database;

NAME

---------

LEO1

说明dblink生效了,当你设置了db_domain参数后global_name=name了。

6.源端创建Master复制组(承担高级复制任务的单位)

Oracle使用dbms_repcat包实现高级复制功能

REPADMIN@leo1> execute dbms_repcat.create_master_repgroup('rep');

PL/SQL procedure successfully completed.

查看复制组

REPADMIN@leo1> select gname,master,status from dba_repgroup where gname='REP';

GNAME M STATUS

-----------------------------------------------------

REP Y QUIESCED

说明:rep复制组已经创建完毕,当前状态quiesced静止的,还没有启动。一个任务由一个复制组实现。

7.加入复制对象(我们对谁进行复制就把谁加入进来)

加入master复制的对象

REPADMIN@leo1>execute dbms_repcat.create_master_repobject(sname=>'ar',oname=>'t',type=>'table',use_existing_object=>true,gname=>'rep',copy_rows=>false);

PL/SQL procedure successfully completed.

sname=>'ar' 复制对象的schema

oname=>'t' 复制对象的名字

type=>'table' 复制对象的类型

gname=>'rep' 复制对象加入到哪个复制组

use_existing_object=>true 可以使用已经创建的对象

copy_rows=>false 不拷贝行

REPADMIN@leo1> select sname,oname,gname,status from dba_repobject where gname='REP';

SNAME ONAME GNAME STATUS

------------------------------ ---------------------------------------------------------- ------------------------------

AR T REP VALID

说明:从dba_repobject数据字典中我们可以查询到已经把t表加入到rep复制组了,状态为valid。

8.对复制对象启动复制支持(就是在t表上生成内部触发器、存储过程、基表)

REPADMIN@leo1> execute dbms_repcat.generate_replication_support('ar','t','table');

PL/SQL procedure successfully completed.

REPADMIN@leo1> select sname,oname,gname,status from dba_repobject where gname='REP';

SNAME ONAME GNAME STATUS

------------------------------ ------------------------------ ------------------------------ ----------

AR T REP VALID

AR T$RP REP VALID

AR T$RP REP VALID

说明:把生成的T$RP存储过程也加入到rep复制组中了。

9.添加目标复制节点(你要把t表往哪个目标数据库上送)

源端,使用repadmin用户操作

REPADMIN@leo1> execute

dbms_repcat.add_master_database(gname=>'rep',master=>'leo2.com',use_existing_objects=>true,copy_rows=>false,propagation_mode=>'synchronous');

说明:把leo2.com数据库的dblink添加到rep复制组中,告诉rep复制组目标数据库就是我

gname=>'rep' 复制组名

master=>'leo2.com' 目标数据库dblink,由于global_name=true,dblink名和数据库名必须一致,oracle强制

propagation_mode=>'synchronous' 传送方式“同步”,实时性高

PL/SQL procedure successfully completed.

REPADMIN@leo1> select gname,dblink,masterdef,master from dba_repsites where gname='REP';

GNAME DBLINK MASTERDEF MASTER

------------------------------ ---------------------------------------------------------------------------------

REP LEO1.COM 源库 Y 发送主节点 Y

REP LEO2.COM 目标库 N 接收主节点 Y

目标端也应该可以查出

REPADMIN@leo2> select gname,dblink,masterdef,master from dba_repsites where gname='REP';

GNAME DBLINK MASTERDEF MASTER

------------------------------ -------------------------------------------------------------------------------------------------------------------------------- - -

REP LEO1.COM Y Y

REP LEO2.COM N Y

检查源端和目标端t表内容,应该都没有数据

源端

AR@leo1> select * from t;

no rows selected

目标端

AR@leo2> select * from t;

no rows selected

10.启动复制组rep(就可以实现高级复制了)

REPADMIN@leo1> execute dbms_repcat.resume_master_activity('rep',true);

PL/SQL procedure successfully completed.

说明:到此高级复制配置完毕,可以进行测试了

11.source->复制->destination

源端

AR@leo1> insert into t values(1);

insert into t values(1)

*

ERROR at line 1:

ORA-04067: not executed, stored procedure "AR.T$RP" does not exist 说明目标端t表上没有生成对应的存储过程

ORA-01085: preceding errors in deferred rpc to "AR.T$RP.REP_INSERT"

ORA-02063: preceding 2 lines from LEO2

检查目标端数据库告警日志

ORA-12012: error on auto execute of job 1

ORA-06550: line 1, column 8:

PLS-00352: Unable to access another database 'LEO1.COM' 不能访问源库LEO1.COM

ORA-06550: line 1, column 8:

PLS-00201: identifier 'SYS@LEO1.COM' must be declared 必须声明到源库dblink

ORA-06550: line 1, column 8:

PL/SQL: Statement ignored

ORA-06550: line 1, column 7:

PLS-00352: Unable to access another database 'LEO1.COM'

ORA-06550: line 1, column 7:

PLS-00201: identifier 'SYS@LEO1.COM' must be declared

ORA-06550: line 1, column 7:

PL/SQL: Statement ignored

ORA-06512: at "SYS.DBMS_REPCAT_MAS", line 864

解决方法

目标端创建到源端dblink,因为两端有信息交互,必须相通

REPADMIN@leo2> create database link leo1.com connect to repadmin identified by oracle using 'leo1';

Database link created.

必须重启复制组rep

REPADMIN@leo1> execute dbms_repcat.suspend_master_activity('rep'); 暂停

PL/SQL procedure successfully completed.

REPADMIN@leo1> execute dbms_repcat.resume_master_activity('rep',true); 重启

PL/SQL procedure successfully completed.

直到源端和目标端的t表上都生成内部触发器、存储过程、基表才能正常复制

REPADMIN@leo1> select sname,oname,gname,status from dba_repobject where gname='REP';

SNAME ONAME GNAME STATUS

------------------------------ ------------------------------ ------------------------------ ----------

AR T REP VALID

AR T$RP REP VALID

AR T$RP REP VALID

参考官方文档地址:Database Advanced Replication Management API Reference书中的DBMS_REPCAT部分

目标端,当目标库t表上有这些东西时就可以正常复制了

REPADMIN@leo2> select sname,oname,gname,status from dba_repobject where gname='REP';

SNAME ONAME GNAME STATUS

------------------------------ ------------------------------ ------------------------------ ----------

AR T REP VALID

AR T$RP REP VALID

AR T$RP REP VALID

再次测试

源库,插入两条记录,当提交之后才会复制到目标库

AR@leo1> insert into t values(1);

1 row created.

AR@leo1> select * from t;

X

----------

1

AR@leo1> insert into t values(2);

1 row created.

AR@leo1> select * from t;

X

----------

1

2

AR@leo1> commit;

Commit complete.

目标库

AR@leo2> select * from t;

X

----------

1

2

源端,更新操作,提交之后才会传到目标库

AR@leo1> update t set x=10 where x=1;

1 row updated.

AR@leo1> update t set x=20 where x=2;

1 row updated.

AR@leo1> commit;

Commit complete.

目标端

AR@leo2> select * from t;

X

----------

10

20

DDL操作测试,上面我们说过了高级复制是不支持DDL操作的

源端

AR@leo1> truncate table t;

Table truncated.

AR@leo1> select * from t;

no rows selected

目标端并没有被清空,源端的DDL操作并不会影响到目标端的

AR@leo2> select * from t;

X

----------

10

20

自动解决数据的约束关系,GG、Streams就不会自动解决

源库

AR@leo1> insert into t values(10);

insert into t values(10)

*

ERROR at line 1:

ORA-00001: unique constraint (AR.SYS_C007046) violated 违反唯一约束

ORA-02063: preceding line from LEO2 来自于LEO2

说明:这个约束警告并不是源库t表上的有问题,而是当事务发生后高级复制会立刻自动检查两端的数据约束关系,

当发现目标库t表中有10这个值时,会立刻发出警告,这个警告说明的是目标库上违反了约束关系~全局约束。

AR@leo2> select * from t;

X

----------

10

20

全局约束:这是由分布式事务引起的,如果发现整个分布式环境中有约束冲突的话,就会告警。

我们插入30,就没有问题,感觉比GG、streams速度还要快

AR@leo1> insert into t values(30);

1 row created.

AR@leo1> commit;

Commit complete.

AR@leo2> select * from t;

X

----------

10

20

30

小结:这种基于内部触发器的高级复制是跟分布式事务紧密相连的,每个动作都是用户操作的一部分。这种模式非常适合容灾场景。缺点也是显而易见的,极易导致事务阻塞、争用、锁定,性能消耗较大,现在已经不是主流技术了。


advanced_replication,HA,基于触发器,队列


Leonarding

2013.09.13

北京&autumn

分享技术~成就梦想

Blog:www.leonarding.com

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值