PostgreSql-学习05-逻辑解码

目录

一、环境

二、什么是逻辑解码

三、SQL接口控制逻辑解码

1、配置参数wal_level为logical

2、重启PG

3、验证参数wal_level

4、验证参数max_replication_slots

5、超级用户连接数据库

6、创建逻辑复制槽

7、查看逻辑复制槽

8、查看逻辑复制槽是否接收到数据变化

9、建表

10、查看逻辑复制槽是否接收到数据变化

11、查看逻辑复制槽是否接收到数据变化

12、插入多条数据

13、查看逻辑复制槽是否接收到数据变化

14、插入数据

15、逻辑复制槽预读数据变化

16、options参数改变格式

17、销毁逻辑复制槽

四、解码准备好的事务的SQL接口

1、设置参数max_prepared_transactions

2、重启PG

3、验证参数max_prepared_transactions

4、逻辑解码提交的准备事务

5、逻辑解码回滚的准备事务

五、逻辑解码插入多列多行

六、逻辑解码更新多行

七、逻辑解码删除多行

八、逻辑解码COPY

九、总结


一、环境

名称描述
PGPostgreSQL 16.3
CPUIntel(R) Core(TM) i5-1035G1 CPU @ 1.00GHz
OSCentOS Linux release 7.9.2009 (Core)

二、什么是逻辑解码

逻辑解码是一种将对数据库表的所有持久更改抽取到一种清晰、易于理解的格式的处理,这种技术允许在不了解数据库内部状态的详细知识的前提下解释该格式。

在PostgreSQL中,逻辑解码通过解码预写式日志的内容来实现,预写式日志描述了存储层面上的更改,而逻辑解码则会把更改解码成一种应用相关的形式,例如一个元组流或者 SQL 语句流。

三、SQL接口控制逻辑解码

1、配置参数wal_level为logical

[postgres@czg2 ~]$ cat /opt/Postgresql/PG_16_3/Data/postgresql.conf |grep wal_level
wal_level = logical                     # minimal, replica, or logical

2、重启PG

[root@czg2 ~]# service PG_16_3 restart
Restarting PostgreSQL: ok

3、验证参数wal_level

postgres=# \x
Expanded display is on.

postgres=# SELECT * FROM PG_CATALOG.PG_SETTINGS WHERE NAME = 'wal_level';
-[ RECORD 1 ]---+--------------------------------------------------
name            | wal_level
setting         | logical
unit            | 
category        | Write-Ahead Log / Settings
short_desc      | Sets the level of information written to the WAL.
extra_desc      | 
context         | postmaster
vartype         | enum
source          | configuration file
min_val         | 
max_val         | 
enumvals        | {minimal,replica,logical}
boot_val        | replica
reset_val       | logical
sourcefile      | /opt/Postgresql/PG_16_3/Data/postgresql.conf
sourceline      | 211
pending_restart | f

4、验证参数max_replication_slots

max_replication_slots必须至少被设置为 1。

czg=# SELECT * FROM PG_CATALOG.PG_SETTINGS WHERE NAME = 'max_replication_slots';
-[ RECORD 1 ]---+---------------------------------------------------------------------
name            | max_replication_slots
setting         | 10
unit            | 
category        | Replication / Sending Servers
short_desc      | Sets the maximum number of simultaneously defined replication slots.
extra_desc      | 
context         | postmaster
vartype         | integer
source          | default
min_val         | 0
max_val         | 262143
enumvals        | 
boot_val        | 10
reset_val       | 10
sourcefile      | 
sourceline      | 
pending_restart | f

5、超级用户连接数据库

czg=# \c
You are now connected to database "czg" as user "postgres".

6、创建逻辑复制槽

czg=# SELECT * FROM PG_CREATE_LOGICAL_REPLICATION_SLOT('czg_slot', 'test_decoding', false, true);
-[ RECORD 1 ]---------
slot_name | czg_slot
lsn       | 0/5985F468
名称描述
slot_name逻辑(解码)复制插槽名
plugin输出插件
temporary当设置为true时,指定插槽不应永久存储到磁盘,仅供当前会话使用。临时插槽也会在任何错误发生时释放。
twophase当设置为true时,指定为此插槽启用已准备事务的解码。

7、查看逻辑复制槽

czg=# SELECT * FROM PG_REPLICATION_SLOTS WHERE SLOT_NAME = 'czg_slot';
-[ RECORD 1 ]-------+--------------
slot_name           | czg_slot
plugin              | test_decoding
slot_type           | logical
datoid              | 16387
database            | czg
temporary           | f
active              | f
active_pid          | 
xmin                | 
catalog_xmin        | 1072
restart_lsn         | 0/5985F430
confirmed_flush_lsn | 0/5985F468
wal_status          | reserved
safe_wal_size       | 
two_phase           | t
conflicting         | f

8、查看逻辑复制槽是否接收到数据变化

czg=# SELECT * FROM PG_LOGICAL_SLOT_GET_CHANGES('czg_slot', NULL, NULL);
(0 rows)
名称描述
slot_name逻辑(解码)复制插槽名。
upto_lsn如果upto_lsn为非 NULL,解码将只包括那些在指定 LSN 之前提交的事务。
upto_nchanges如果upto_nchanges为非 NULL,解码将在其产生的行数超过指定值后停止。
options官方手册中没有解释。

如果upto_lsn和upto_nchanges为 NULL,逻辑解码将一直继续到 WAL 的末尾。 
不过要注意,被返回的实际行数可能更大,因为对这个限制的检查只会在增加了解码每个新的提交事务产生的行之后进行。

因为我们现在还没有做数据改变,所以没有查到数据。

9、建表

czg=# CREATE TABLE TEST (A INT);
CREATE TABLE

10、查看逻辑复制槽是否接收到数据变化

czg=# SELECT * FROM PG_LOGICAL_SLOT_GET_CHANGES('czg_slot', NULL, NULL);
-[ RECORD 1 ]-----
lsn  | 0/5985F4B8
xid  | 1072
data | BEGIN 1072
-[ RECORD 2 ]-----
lsn  | 0/59873558
xid  | 1072
data | COMMIT 1072

DDL 不会被复制,因此只会看到事务。

11、查看逻辑复制槽是否接收到数据变化

czg=# SELECT * FROM PG_LOGICAL_SLOT_GET_CHANGES('czg_slot', NULL, NULL);
(0 rows)

读取更改后,它们将被使用,并且不会在后续调用中发出。

因为我们上面设置的读取到WAL的结尾。

12、插入多条数据

czg=# BEGIN;
BEGIN
czg=*# INSERT INTO TEST VALUES(1);
INSERT 0 1
czg=*# INSERT INTO TEST VALUES(2);
INSERT 0 1
czg=*# COMMIT;
COMMIT

13、查看逻辑复制槽是否接收到数据变化

czg=# SELECT * FROM PG_LOGICAL_SLOT_GET_CHANGES('czg_slot', NULL, NULL);
-[ RECORD 1 ]---------------------------------
lsn  | 0/59873678
xid  | 1073
data | BEGIN 1073
-[ RECORD 2 ]---------------------------------
lsn  | 0/59873678
xid  | 1073
data | table public.test: INSERT: a[integer]:1
-[ RECORD 3 ]---------------------------------
lsn  | 0/598736F0
xid  | 1073
data | table public.test: INSERT: a[integer]:2
-[ RECORD 4 ]---------------------------------
lsn  | 0/59873760
xid  | 1073
data | COMMIT 1073

捕获DML正常。

14、插入数据

czg=# INSERT INTO TEST VALUES(3);
INSERT 0 1

15、逻辑复制槽预读数据变化

czg=# SELECT * FROM PG_LOGICAL_SLOT_PEEK_CHANGES('czg_slot', NULL, NULL);
-[ RECORD 1 ]---------------------------------
lsn  | 0/59873880
xid  | 1074
data | BEGIN 1074
-[ RECORD 2 ]---------------------------------
lsn  | 0/59873880
xid  | 1074
data | table public.test: INSERT: a[integer]:3
-[ RECORD 3 ]---------------------------------
lsn  | 0/59873978
xid  | 1074
data | COMMIT 1074

PG_LOGICAL_SLOT_PEEK_CHANGES函数和PG_LOGICAL_SLOT_GET_CHANGES函数的参数和返回内容一样,只是预读数据变化,可以多次读。

16、options参数改变格式

czg=# SELECT * FROM PG_LOGICAL_SLOT_PEEK_CHANGES('czg_slot', NULL, NULL, 'include-timestamp', 'on');
-[ RECORD 1 ]----------------------------------------
lsn  | 0/59873880
xid  | 1074
data | BEGIN 1074
-[ RECORD 2 ]----------------------------------------
lsn  | 0/59873880
xid  | 1074
data | table public.test: INSERT: a[integer]:3
-[ RECORD 3 ]----------------------------------------
lsn  | 0/59873978
xid  | 1074
data | COMMIT 1074 (at 2024-08-29 17:25:07.957535+08)

17、销毁逻辑复制槽

czg=# SELECT PG_DROP_REPLICATION_SLOT('czg_slot');
-[ RECORD 1 ]------------+-
pg_drop_replication_slot |

四、解码准备好的事务的SQL接口

三中的1-6都需要执行。

1、设置参数max_prepared_transactions

[postgres@czg2 ~]$ cat /opt/Postgresql/PG_16_3/Data/postgresql.conf |grep max_prepared_transactions
max_prepared_transactions = 2           # zero disables the feature
# Caution: it is not advisable to set max_prepared_transactions nonzero unless

max_prepared_transactions至少为1。

2、重启PG

[root@czg2 ~]# service PG_16_3 restart
Restarting PostgreSQL: ok

3、验证参数max_prepared_transactions

czg=# SELECT * FROM PG_CATALOG.PG_SETTINGS WHERE NAME = 'max_prepared_transactions';
-[ RECORD 1 ]---+-----------------------------------------------------------------
name            | max_prepared_transactions
setting         | 2
unit            | 
category        | Resource Usage / Memory
short_desc      | Sets the maximum number of simultaneously prepared transactions.
extra_desc      | 
context         | postmaster
vartype         | integer
source          | configuration file
min_val         | 0
max_val         | 262143
enumvals        | 
boot_val        | 0
reset_val       | 2
sourcefile      | /opt/Postgresql/PG_16_3/Data/postgresql.conf
sourceline      | 137
pending_restart | f

4、逻辑解码提交的准备事务

czg=# \x
Expanded display is on.
czg=# BEGIN;
BEGIN
czg=*# INSERT INTO TEST VALUES(11);
INSERT 0 1
czg=*# PREPARE TRANSACTION 'TEST_PREPARED1';
PREPARE TRANSACTION
czg=# SELECT * FROM PG_LOGICAL_SLOT_PEEK_CHANGES('czg_slot', NULL, NULL);
-[ RECORD 1 ]-----------------------------------------
lsn  | 0/59875048
xid  | 1085
data | BEGIN 1085
-[ RECORD 2 ]-----------------------------------------
lsn  | 0/59875048
xid  | 1085
data | table public.test: INSERT: a[integer]:11
-[ RECORD 3 ]-----------------------------------------
lsn  | 0/59875340
xid  | 1085
data | PREPARE TRANSACTION 'TEST_PREPARED1', txid 1085

czg=# COMMIT PREPARED 'TEST_PREPARED1';
COMMIT PREPARED
czg=# SELECT * FROM PG_LOGICAL_SLOT_GET_CHANGES('czg_slot', NULL, NULL);
-[ RECORD 1 ]-----------------------------------------
lsn  | 0/59875048
xid  | 1085
data | BEGIN 1085
-[ RECORD 2 ]-----------------------------------------
lsn  | 0/59875048
xid  | 1085
data | table public.test: INSERT: a[integer]:11
-[ RECORD 3 ]-----------------------------------------
lsn  | 0/59875340
xid  | 1085
data | PREPARE TRANSACTION 'TEST_PREPARED1', txid 1085
-[ RECORD 4 ]-----------------------------------------
lsn  | 0/598753C0
xid  | 1085
data | COMMIT PREPARED 'TEST_PREPARED1', txid 1085

czg=# SELECT * FROM TEST WHERE A = 11;
-[ RECORD 1 ]
a | 11

5、逻辑解码回滚的准备事务

czg=# BEGIN;
BEGIN
czg=*# INSERT INTO TEST VALUES(12);
INSERT 0 1
czg=*# PREPARE TRANSACTION 'TEST_PREPARED1';
PREPARE TRANSACTION
czg=# SELECT * FROM PG_LOGICAL_SLOT_PEEK_CHANGES('czg_slot', NULL, NULL);
-[ RECORD 1 ]-----------------------------------------
lsn  | 0/59875880
xid  | 1087
data | BEGIN 1087
-[ RECORD 2 ]-----------------------------------------
lsn  | 0/59875880
xid  | 1087
data | table public.test: INSERT: a[integer]:12
-[ RECORD 3 ]-----------------------------------------
lsn  | 0/598759C0
xid  | 1087
data | PREPARE TRANSACTION 'TEST_PREPARED1', txid 1087

czg=# ROLLBACK PREPARED 'TEST_PREPARED1';
ROLLBACK PREPARED
czg=# SELECT * FROM PG_LOGICAL_SLOT_GET_CHANGES('czg_slot', NULL, NULL);
-[ RECORD 1 ]-----------------------------------------
lsn  | 0/59875880
xid  | 1087
data | BEGIN 1087
-[ RECORD 2 ]-----------------------------------------
lsn  | 0/598759C0
xid  | 1087
data | PREPARE TRANSACTION 'TEST_PREPARED1', txid 1087
-[ RECORD 3 ]-----------------------------------------
lsn  | 0/59875A40
xid  | 1087
data | ROLLBACK PREPARED 'TEST_PREPARED1', txid 1087

czg=# SELECT * FROM TEST WHERE A = 12;
(0 rows)

回滚准备事务开始之前看到的逻辑解码和之后看到的逻辑解码,少了INSERT语句。

五、逻辑解码插入多列多行

czg=# INSERT INTO TESTTAB(B,C) SELECT 99.1,'SUN_CZG';
INSERT 0 1
czg=# SELECT * FROM PG_LOGICAL_SLOT_GET_CHANGES('czg_slot', NULL, NULL);
-[ RECORD 1 ]------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
lsn  | 0/5987D7F0
xid  | 1093
data | BEGIN 1093
-[ RECORD 2 ]------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
lsn  | 0/5987D7F0
xid  | 1093
data | table public.testtab: INSERT: a[integer]:2098670 b[double precision]:99.1 c[character varying]:'SUN_CZG' d[text]:null e[bytea]:null f[bytea]:null g[date]:null h[timestamp without time zone]:null i[numeric]:null
-[ RECORD 3 ]------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
lsn  | 0/5987D8B8
xid  | 1093
data | COMMIT 1093

看一下解析出的数据,这个输出插件test_decoding编写的已经很好了,但可以稍微压缩一下数据,一个是方便解析,二是减少数据包的大小,减少网络压力。

六、逻辑解码更新多行

czg=# UPDATE TEST SET A = 2 WHERE A < 2;
UPDATE 3
czg=# SELECT * FROM PG_LOGICAL_SLOT_GET_CHANGES('czg_slot', NULL, NULL);
-[ RECORD 1 ]---------------------------------
lsn  | 0/5987D9D8
xid  | 1094
data | BEGIN 1094
-[ RECORD 2 ]---------------------------------
lsn  | 0/5987D9D8
xid  | 1094
data | table public.test: UPDATE: a[integer]:2
-[ RECORD 3 ]---------------------------------
lsn  | 0/5987EA80
xid  | 1094
data | table public.test: UPDATE: a[integer]:2
-[ RECORD 4 ]---------------------------------
lsn  | 0/5987EAC8
xid  | 1094
data | table public.test: UPDATE: a[integer]:2
-[ RECORD 5 ]---------------------------------
lsn  | 0/5987EB40
xid  | 1094
data | COMMIT 1094

解析出的结果,我们不能断定是哪行数据变成为2,这个会影响CDC的实现。

七、逻辑解码删除多行

czg=# DELETE FROM TEST WHERE A <= 2;
DELETE 6
czg=# SELECT * FROM PG_LOGICAL_SLOT_GET_CHANGES('czg_slot', NULL, NULL);
-[ RECORD 1 ]------------------------------------
lsn  | 0/5987EC60
xid  | 1095
data | BEGIN 1095
-[ RECORD 2 ]------------------------------------
lsn  | 0/5987EC60
xid  | 1095
data | table public.test: DELETE: (no-tuple-data)
-[ RECORD 3 ]------------------------------------
lsn  | 0/5987FD28
xid  | 1095
data | table public.test: DELETE: (no-tuple-data)
-[ RECORD 4 ]------------------------------------
lsn  | 0/5987FD60
xid  | 1095
data | table public.test: DELETE: (no-tuple-data)
-[ RECORD 5 ]------------------------------------
lsn  | 0/5987FD98
xid  | 1095
data | table public.test: DELETE: (no-tuple-data)
-[ RECORD 6 ]------------------------------------
lsn  | 0/5987FDD0
xid  | 1095
data | table public.test: DELETE: (no-tuple-data)
-[ RECORD 7 ]------------------------------------
lsn  | 0/5987FE08
xid  | 1095
data | table public.test: DELETE: (no-tuple-data)
-[ RECORD 8 ]------------------------------------
lsn  | 0/5987FE70
xid  | 1095
data | COMMIT 1095

解析出的结果,我们不能断定是哪行数据被删除,这个会影响CDC的实现。

八、逻辑解码COPY

czg=# COPY PUBLIC.TESTTAB(B,C,D,E,F,G,H,I)
czg-#     FROM '/home/postgres/PgData.txt'
czg-# WITH(
czg(# 

czg(# FORMAT    CSV,
czg(# 

czg(# DELIMITER '|',
czg(# 

czg(# NULL      '',
czg(# 

czg(# QUOTE     '"',
czg(# 

czg(# ESCAPE    '\');
COPY 10
czg=# SELECT * FROM PG_LOGICAL_SLOT_GET_CHANGES('czg_slot', NULL, NULL);
-[ RECORD 1 ]------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------
lsn  | 0/5987FF90
xid  | 1096
data | BEGIN 1096
-[ RECORD 2 ]------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------
lsn  | 0/5987FFF8
xid  | 1096
data | table public.testtab: INSERT: a[integer]:2098671 b[double precision]:9.1 c[character varying]:'1LXG''ZXJ|"CLX' d[text]:'HAHHAHAH' e[bytea]:'\x41414141
4141' f[bytea]:'\x424242424242424242424242' g[date]:null h[timestamp without time zone]:'2024-08-19 00:00:00' i[numeric]:8.00
-[ RECORD 3 ]------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------
lsn  | 0/5987FFF8
xid  | 1096
data | table public.testtab: INSERT: a[integer]:2098672 b[double precision]:9.1 c[character varying]:'2LXG''ZXJ|"CLX' d[text]:'HAHHAHAH' e[bytea]:'\x41414141
4141' f[bytea]:'\x424242424242424242424242' g[date]:'2024-08-19' h[timestamp without time zone]:'2024-08-19 00:00:00' i[numeric]:8.00
-[ RECORD 4 ]------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------
lsn  | 0/5987FFF8
xid  | 1096
data | table public.testtab: INSERT: a[integer]:2098673 b[double precision]:9.1 c[character varying]:'3LXG''ZXJ|"CLX' d[text]:'HAHHAHAH' e[bytea]:'\x41414141
4141' f[bytea]:'\x424242424242424242424242' g[date]:'2024-08-19' h[timestamp without time zone]:'2024-08-19 00:00:00' i[numeric]:8.00
-[ RECORD 5 ]------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------
lsn  | 0/5987FFF8
xid  | 1096
data | table public.testtab: INSERT: a[integer]:2098674 b[double precision]:9.1 c[character varying]:'4LXG''ZXJ|"CLX' d[text]:'HAHHAHAH' e[bytea]:'\x41414141
4141' f[bytea]:'\x424242424242424242424242' g[date]:'2024-08-19' h[timestamp without time zone]:'2024-08-19 00:00:00' i[numeric]:8.00
-[ RECORD 6 ]------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------
lsn  | 0/5987FFF8
xid  | 1096
data | table public.testtab: INSERT: a[integer]:2098675 b[double precision]:9.1 c[character varying]:'5LXG''ZXJ|"CLX' d[text]:'HAHHAHAH' e[bytea]:'\x41414141
4141' f[bytea]:'\x424242424242424242424242' g[date]:'2024-08-19' h[timestamp without time zone]:'2024-08-19 00:00:00' i[numeric]:8.00
-[ RECORD 7 ]------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------
lsn  | 0/5987FFF8
xid  | 1096
data | table public.testtab: INSERT: a[integer]:2098676 b[double precision]:9.1 c[character varying]:'6LXG''ZXJ|"CLX' d[text]:'HAHHAHAH' e[bytea]:'\x41414141
4141' f[bytea]:'\x424242424242424242424242' g[date]:'2024-08-19' h[timestamp without time zone]:'2024-08-19 00:00:00' i[numeric]:8.00
-[ RECORD 8 ]------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------
lsn  | 0/5987FFF8
xid  | 1096
data | table public.testtab: INSERT: a[integer]:2098677 b[double precision]:9.1 c[character varying]:'7LXG''ZXJ|"CLX' d[text]:'HAHHAHAH' e[bytea]:'\x41414141
4141' f[bytea]:'\x424242424242424242424242' g[date]:'2024-08-19' h[timestamp without time zone]:'2024-08-19 00:00:00' i[numeric]:8.00
-[ RECORD 9 ]------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------
lsn  | 0/5987FFF8
xid  | 1096
data | table public.testtab: INSERT: a[integer]:2098678 b[double precision]:9.1 c[character varying]:'8LXG''ZXJ|"CLX' d[text]:'HAHHAHAH' e[bytea]:'\x41414141
4141' f[bytea]:'\x424242424242424242424242' g[date]:'2024-08-19' h[timestamp without time zone]:'2024-08-19 00:00:00' i[numeric]:8.00
-[ RECORD 10 ]-----------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------
lsn  | 0/59882338
xid  | 1096
data | table public.testtab: INSERT: a[integer]:2098679 b[double precision]:9.1 c[character varying]:'9LXG''ZXJ|"CLX' d[text]:'HAHHAHAH' e[bytea]:'\x41414141
4141' f[bytea]:'\x424242424242424242424242' g[date]:'2024-08-19' h[timestamp without time zone]:'2024-08-19 00:00:00' i[numeric]:8.00
-[ RECORD 11 ]-----------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------
lsn  | 0/59882338
xid  | 1096
data | table public.testtab: INSERT: a[integer]:2098680 b[double precision]:9.1 c[character varying]:'0LXG''ZXJ|"CLX' d[text]:'HAHHAHAH' e[bytea]:'\x41414141
4141' f[bytea]:'\x424242424242424242424242' g[date]:'2024-08-19' h[timestamp without time zone]:'2024-08-19 00:00:00' i[numeric]:8.00
-[ RECORD 12 ]-----------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------
lsn  | 0/59882F08
xid  | 1096
data | COMMIT 1096

九、总结

从上面的测试结果来看,如果想用输出插件test_decoding进行逻辑解码来实现CDC,是有一定的问题的,在删除和更新的时候。PG_LOGICAL_SLOT_PEEK_CHANGES支持OPTION选项,来改变一些输出格式,不知道能不能达到想要的效果,主要的问题还是没有找到OPTION相关的介绍,如果大家有了解的欢迎分享。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值