Redis(13)----浅谈Redis的主从复制

1,前言

读《Redis设计与实现》所作笔记

Redis中,用户可以通过SLAVEOF命令或者设置slaveof选项,让一个服务器去复制另一个服务器,我们称呼被复制的服务器为主服务器,而对主服务器进行复制的服务器则被称为从服务器

假设有两个服务器

  • 服务器A,地址为127.0.0.1:6379
  • 服务器B,地址为127.0.0.1:12345
  • 在服务器A发送以下命令:SLAVEOF 127.0.0.1:12345

那么服务器A就会成为服务器B的从服务器,服务器B也就是服务器A的主服务器

进行复制的主从服务器双方的数据库将保存相同的数据,概念上将这种现象称作:”数据库状态一致“,或者简称”一致“

主从复制功能以Redis2.8版本为分界线,2.8版本以前的旧版复制功能在处理断线后重新连接的从服务器存在低效的问题;而Redis2.8之后的新版复制功能则是通过部分重同步来解决旧版复制功能的低效问题

2,旧版复制功能的实现

Redis的复制功能分为:

  1. 同步(sync):同步操作用于将从服务器的数据库状态更新至主服务器当前所处的数据库状态
  2. 命令传播(command propagate):命令传播操作用于在主服务器的数据库状态被修改,导致主从服务器的数据库状态不一致时,让主从服务器的数据库重新回到一致状态

2.1,同步

当客户端向从服务器发送了SLAVEOF命令,要求从服务器复制主服务器时,从服务器首先需要执行同步操作,也即是:将从服务器的数据库状态更新至主服务器当前所处的数据库状态

从服务器通过向主服务器发送SYNC命令完成同步,以下是SYNC命令的执行步骤:

  1. 从服务器向主服务器发送SYNC命令
  2. 收到SYNC命令的主服务器执行BGSAVE命令,在后台生成一个RDB文件,并使用一个缓冲区记录从现在开始执行的所有写命令
  3. 当主服务器的BGSAVE命令执行完毕时,主服务器会将BGSAVE命令生成的RDB文件发送给服务器,从服务器接收并载入这个RDB文件,将自己的数据库状态更新至主服务器执行BGSAVE命令时的数据库状态
  4. 主服务器将记录在缓冲区里面的所有写命令发送给从服务器,从服务器执行这些写命令,将自己的数据库状态更新至主服务器数据库当前所处的状态

在这里插入图片描述

下表展示了一个主从服务器进行同步的例子:

时间主服务器从服务器
T0服务器启动服务器启动
T1执行SET k1 v1
T2执行SET k2 v2
T3执行SET k3 v3
T4向主服务器发送SYNC命令
T5接收到从服务器发送来的SYNC命令,执行BGSAVE命令,创建包含键k1、k2、k3RDB文件,并使用缓冲区记录接下来执行的所有写命令
T6执行SET k4 v4,并将这个命令记录到缓冲区里面
T7执行SET k5 v5,并将这个命令记录到缓冲区里面
T8BGSAVE命令执行完毕,向从服务器发送RDB文件
T9接收并载入主服务器发来的RDB文件,获得k1、k2、k3三个键
T10向从服务器发送缓冲区中保存的写命令SET k4 v4SET k5 v5
T11接收并执行主服务器发来的两个SET命令,得到k4k5两个键
T12同步完成,现在主从服务器两者的数据库都包含了键k1、k2、k3、k4k5同步完成,现在主从服务器两者的数据库都包含了键k1、k2、k3、k4k5

2.2,命令传播

在同步操作执行完毕之后,主从服务器两者的数据库将达到一致状态,但这种一致并不是一成不变的。当主服务器执行写命令之后,主从服务器之间的数据库状态有可能不再一致,这时候就需要主服务器将刚刚执行的写命令传递给从服务器,让从服务器执行这条写命令,从而使得主从服务器数据库状态重新达到一致
在这里插入图片描述

在这里插入图片描述

2.3,缺陷

Redis中,从服务器对主服务器的复制可以分为以下两种情况:

  • 初次复制:从服务器以前没有复制过任何主服务器,或者从服务器当前要复制的主服务器和上一次复制的主服务器不同【没有复制过主服务器或者换主服务器复制了
  • 断线后重复制,处于命令传播阶段的主从服务器因为网络原因中断复制,但从服务器通过自动重连重新连接上了主服务器,并继续复制主服务器【连的好好的,突然断开连接,然后重新连接,导致主从服务器数据库状态有可能不一致

对于初次复制,旧版的复制功能能很好的完成任务,但对于断线后重复制来说,旧版复制功能虽然能让主从服务器重新回到一致状态,但效率很低

具体情况以下的示例:

时间主服务器从服务器
T0主从服务器完成同步主从服务器完成同步
T1执行并传播SET k1 v1执行主服务器传来的SET k1 v1
T2执行并传播SET k2 v2执行主服务器传来的SET k2 v2
………………
T10085执行并传播SET k10085 v10085执行主服务器传来的SET k10085 v10085
T10086执行并传播SET k10086 v10086执行主服务器传来的SET k10086 v10086
T10087主从服务器连接断开主从服务器连接断开
T10088执行并传播SET k10087 v10087断线中,尝试重新连接主服务器
T10089执行并传播SET k10088 v10088断线中,尝试重新连接主服务器
T10090执行并传播SET k10089 v10089断线中,尝试重新连接主服务器
T10091主从服务器重新连接主从服务器重新连接
T10092向主服务器发送SYNC命令
T10093接收到服务器发来的SYNC命令,执行BGSAVE命令,创建包含键k1至键10089RDB文件,并使用缓冲区记录接下来执行的所有写命令
T10094BGSAVE命令执行完毕,向从服务器发送RDB文件
T10095接收并载入主服务器发来的RDB文件,获得键k1k10089
T10096因为在BGSAVE命令执行期间,主服务器都没有执行任何写命令,所以跳过发送缓冲区包含的写命令这一步
T10097主从服务器再次完成同步主从服务器再次完成同步

在时间T10091,从服务器重新连接到了主服务器,因为这时主从服务器状态已经不一致了,所以从服务器将向主服务器发送SYNC命令,而主服务器将包含键k1至键k10089的RDB文件发送给从服务器,从服务器通过接收和载入这个RDB文件来将自己的数据库更新至主服务器数据库当前所处的状态

虽然SYNC命令能让主从服务器重新回到一致状态,但仔细研究这个断线重复制过程,可以发现传送RDB文件这一步并非必须的:

  • 主从服务器之间的数据库状态不同是在于键k10087至键k10089,而键k1至键k10086这些键是一样的,从服务器只需要添加键k10087至键k10089即可到达主从服务器数据库状态一致
  • 可惜的是,旧版复制功能并没有根据上面的情况提出解决办法,而是让主服务器生成并向从服务器发送包含键k1至键k10089的RDB文件,但实际上RDB文件中包含的键k1至键k10086对于从服务器来说都是不必要的

上述的情况可能有一点理想化:而在主从服务器断线期间,主服务器执行的写命令可能会有成百上千之多,不仅仅是两三个命令,并且断线期间执行的写命令所产生的数据量要比数据库中的数据量少得多。在这种情况下,为了让从服务器补足一小部分缺失的数据,却要让主从服务器重新执行一次SYNC命令,重新载入RDB文件,这种做法无疑是非常低效的

SYNC命令是一个非常耗费资源的操作

每次执行SYNC命令,主从服务器都需要执行以下动作:

  1. 主服务器需要执行BGSAVE命令来生成RDB文件,这个生成操作会耗费主服务器大量的CPU、内存和磁盘I/O资源
  2. 主服务器需要将自己生成的RDB文件发送给从服务器,这个发送操作会耗费主从服务器大量的网络资源(带宽和流量),并对主服务器响应命令请求的时间产生影响
  3. 接收到RDB文件的从服务器需要载入服务器发来的RDB文件,并且在载入期间,从服务器会因为阻塞而没办法处理命令请求

因为SYNC命令是一个耗费资源的操作,所以Redis必须慎之又慎的使用SYNC命令,只有在真正有需要的时候才执行

3,新版复制功能

为了解决旧版复制功能在断线重复制情况下的低效问题,Redis从2.8开始,使用PSYNC命令代替SYNC命令来执行复制时的同步操作

PSYNC命令具有完整重同步和部分重同步两种模式:

  • 完整重同步:完整重同步的执行步骤和SYNC命令的执行步骤基本一样,都是通过主服务器创建RDB文件,并及时推送主服务器保存在缓冲区中的写命令来进行同步
  • 部分重同步:部分重同步则用于处理断线后重复制的情况,当从服务器断线后重新连接主服务器。如果条件允许,主服务器可以将断线期间执行的命令发送给从服务器,从服务器只需要接受并执行这些写命令,这样就可以将数据库更新至主服务器当前所处的状态

看以下的示例:

时间主服务器从服务器
T0主从服务器完成同步主从服务器完成同步
T1执行并传播SET k1 v1执行主服务器传来的SET k1 v1
T2执行并传播SET k2 v2执行主服务器传来的SET k2 v2
………………
T10085执行并传播SET k10085 v10085执行主服务器传来的SET k10085 v10085
T10086执行并传播SET k10086 v10086执行主服务器传来的SET k10086 v10086
T10087主从服务器连接断开主从服务器连接断开
T10088执行并传播SET k10087 v10087断线中,尝试重新连接主服务器
T10089执行并传播SET k10088 v10088断线中,尝试重新连接主服务器
T10090执行并传播SET k10089 v10089断线中,尝试重新连接主服务器
T10091主从服务器重新连接主从服务器重新连接
T10092向主服务器发送PSYNC命令
T10093向从服务器返回+CONTINUE回复,表示执行部分重同步
T10094接收+CONTINUE回复,准备执行部分重同步
T10095向从服务器发送SET k10087 v10087、SET k10088 v10088、SET k10089 v10089
T10096接受并执行主服务器传来的三个SET命令
T10097主从服务器再次完成同步主从服务器再次完成同步

相比于SYNC命令,PSYNC命令的执行只需要将从服务器缺少的命令发给从服务器执行即可。

那么接下来的问题就是,主服务器如何确定从服务器缺少哪些命令?

部分重同步的实现

部分重同步功能由以下三个部分构成:

  • 主服务器的复制偏移量(replication offset)和从服务器的复制偏移量
  • 主服务器的复制积压缓冲区(replication backlog
  • 服务器的运行IDrun ID

3.1,复制偏移量

执行复制的双方都会维护一个复制偏移量

  • 主服务器每次向从服务器传播N个字节的数据时,就将自己的复制偏移量的值加上N
  • 从服务器每次收到主服务器传播来的N个字节的数据,就将自己的复制偏移量的值加上N

假如有这么几部服务器:

在这里插入图片描述

如果这时主服务器向三个从服务器传播长度为33字节的数据,那么主服务器的复制偏移量将更新为10119,而三个从服务器接收到主服务器传播的数据之后,也会将复制偏移量更新为10119:

在这里插入图片描述

假设从服务器A断线重新连接到主服务器之后,从服务器将向主服务器发送PSYNC命令,报告从服务器A当前的复制偏移量为10086,那么主服务器对从服务器执行完整重同步还是部分重同步?如果是部分重同步,又该怎么补偿给从服务器确实的数据?答案与复制积压缓冲区有关

3.2,复制积压缓冲区

复制积压缓冲区是由主服务器维护的一个固定长度先进先出队列,默认大小为1MB

固定长度先进先出队列的入队和出队规则跟普通的先进先出队列一样,不同的是:普通先进先出队列随着元素的增加和减少会动态调整长度,而固定长度先进先出队列的长度是固定的,当入队元素的数量大于队列长度时,最先入队的元素会被弹出,而新元素会被放入队列

例如一个长度为3的固定长度先进先出队列,现在要将‘h’、’e’、‘l’、‘l’、‘o’五个字符放进队列中

  1. 先放入‘h’、’e’、‘l’,此时队列满了
  2. 此时再放入‘l’,会将‘h’弹出队列,此时队列会变成:‘e’、’l’、‘l’
  3. 接着放入‘o’

复制积压缓冲区大小的确定

【值得一提的是:如果复制积压缓冲区的大小设置不合理,那么PSYNC的复制重同步模式就不能正常发挥作用,因此正确估算和设置复制积压缓冲区的大小非常重要。复制积压缓冲区的最小大小的确定可以根据公式 (从服务器断线后重新连接上主服务器所需的平均时间)* (主服务器平均每秒产生的写命令数据量(协议格式的写命令的长度总和))

当主服务器进行命令传播时,它不仅会将写命令发送给所有从服务器,还会将写命令入队到复制积压缓冲区中:

在这里插入图片描述

因此主服务器的复制积压缓冲区里面会保存着一部分最近传播的写命令,并且复制积压缓冲区会为队列中的每个字节记录相应的复制偏移量,如:

在这里插入图片描述

当从服务器重新连上主服务器,从服务器会通过PSYNC命令将自己的复制偏移量offset发送给主服务器,主服务器会根据这个复制偏移量决定对从服务器执行完整重同步还是部分重同步:

  • 如果offset偏移量之后的数据(也即是偏移量offset+1开始的数据)仍然存在于复制积压缓冲区,那么主服务器将对从服务器执行部分重同步操作
  • 相反,如果offset偏移量之后的数据已经不存在于复制积压缓冲区,那么主服务器将对从服务器执行完整重同步操作

结合之前的例子,新版复制功能处理断线重连的例子:

  1. 当从服务器A断线之后,它立即重新连接主服务器,并向主服务器发送PSYNC命令,报告自己的复制偏移量为10086
  2. 主服务器接收到从服务器发来的PSYNC命令以及偏移量10086,主服务器将检查偏移量10086之后的数据是否存在于复制积压缓冲区里面,结果发现这些数据仍然存在,那么主服务器会向从服务器发送+CONTINUE回复,表示数据同步将以部分重同步来进行
  3. 接着主服务器会将复制积压缓冲区10086偏移量之后的所有数据都发送给从服务器
  4. 从服务器只需要接收这33字节的缺失数据,就可以与主服务器数据库状态重新保持一致!

在这里插入图片描述

3.3,服务器运行ID

除了复制偏移量和复制积压缓冲区之外,部分重同步还需要用到服务器运行IDrun ID

  • 每个Redis服务器,不论主服务器还是从服务器都有一个自己的运行ID
  • 运行ID在服务器启动时自动生成,由40个随机的十六进制字符组成

当从服务器对主服务器进行初次复制时,主服务器会将自己的运行ID传送给从服务器,而从服务器则会将这个运行ID保存起来。当从服务器断线并重新连上一个主服务器时,从服务器将向当前连接的主服务器发送之前保存的运行ID

  • 如果发送的运行ID和当前主服务器的运行ID相同,那么代表从服务器断线之前连接的正是这个主服务器,主服务器将会尝试执行部分重同步操作
  • 如果不相同,那么代表从服务器断线前连接的不是当前的主服务器,主服务器将会对从服务器进行完整重同步操作

4,PSYNC命令的实现

PSYNC命令的调用方式有两种:

  1. 如果从服务器以前没有复制过任何主服务器,或者之前执行过SLAVEOF no one命令,那么从服务器在开始一次新的复制时将向主服务器发送PSYNC ? -1命令,主动请求主服务器进行完整重同步
  2. 相反,如果从服务器已经复制过某个主服务器,那么从服务器在开始一次新的复制时将向主服务器发送PSYNC <runid> <offset>命令,其中runid是上一次复制的主服务器的运行ID,而offset则是从服务器当前的复制偏移量,接收到这个命令的主服务器会通过这两个参数判断应该对服务器进行哪种同步操作

根据情况,接收到PSYNC的主服务器会向从服务器返回下面三种回复之一:

  • 如果主服务器返回+FULLREYSNC <runid> <offset>回复,表示主服务器将与从服务器执行完整重同步操作
    • runid:主服务器的运行ID,从服务器会将这个ID保存起来,在下次发送PSYNC命令时使用
    • offset:主服务器当前的复制偏移量,从服务器会将这个值作为自己的初始化偏移量
  • 如果主服务器返回+CONTINUE回复,那么表示主服务器将与从服务器执行部分重同步操作,从服务器只要等着主服务器将自己缺少的那部分数据发送过来就可以了
  • 如果主服务器返回-ERR回复,表示主服务器的版本低于Redis2.8,它识别不了PSYNC命令,从服务器将向主服务器发送SYNC命令,并与主服务器执行完整重同步操作

相关的示意图如下:

在这里插入图片描述

以下是一个完整的复制-》网络中断-重复制例子:

  1. 首先有主服务器A:127.0.0.1:6379和从服务器:127.0.0.1:12345
  2. 客户端向从服务器发送命令SLAVEOF 127.0.0.1 6379,假设从服务器是第一次执行复制操作,那么从服务器将向主服务器发送PSYNC ?-1命令,请求主服务器执行完整重同步操作
  3. 主服务器收到完整重同步请求后,在后台执行BGSAVE命令,并向从服务器返回+FULLRESYNC 主服务器运行ID 10086,其中的10086是主服务器复制偏移量
  4. 假设完整重同步成功执行,并且主从服务器在一段时间内保持数据库状态一致。但是在复制到偏移量为20000的时候,主从服务器之间的网络连接中断,一段时间后,从服务器重新连接至主服务器,并再次对主服务器进行复制
  5. 因为之前对主服务器进行过复制,所以从服务器将向主服务器发送命令PSYNC 主服务器运行ID 20000,请求进行部分重同步
  6. 主服务器接收到从服务器的PSYNC命令之后,首先对比从服务器传来的运行ID,并和自身的运行ID进行比较。结果显示相同,于是主服务器继续读取从服务器传来的偏移量20000,检查偏移量为20000之后的数据是否存在于复制积压缓冲区中,结果发现仍然存在
  7. 确认运行ID相同并且数据存在之后,主服务器就会向服务器返回+CONTINUE回复,表示将于从服务器执行部分重同步操作,之后主服务器将从服务器需要的所有数据(偏移量之后的数据)发送给从服务器,主从服务器数据库状态再次回到一致

5,复制的实现

以下是Redis2.8及以上版本的复制功能的详细实现步骤:

  1. 设置主服务器的地址和端口
  2. 建立套接字连接
  3. 发送PING命令
  4. 身份验证
  5. 发送端口信息
  6. 同步
  7. 命令传播

5.1,设置主服务器的地址和端口

当客户端向从服务器执行以下命令:

127.0.0.1:12345> SLAVEOF 127.0.0.1 6379
OK

从服务器首先要做的就是将客户端给定的主服务器IP地址和端口保存到服务器状态的masterhost属性和masterport属性里面:

struct redisServer{
    
    //...
    
    //主服务器地址
    char *masterhost;
    
    //主服务器端口
    int masterport;
    
    //...
};

执行完命令之后,从服务器的服务状态:

在这里插入图片描述

5.2,建立套接字连接

执行SLAVEOF命令后,从服务器将根据命令设置的地址和端口,创建连向主服务器的套接字连接。如果从服务器创建的套接字能成功连接到主服务器,那么从服务器将为这个套接字关联一个专门用于处理复制工作的文件事件处理器,这个处理器将负责执行后续的复制工作。比如接收RDB文件、接收主服务器传播过来的写命令等。

主服务器在接收从服务器的套接字连接之后,将为该套接字创建相应的客户端状态,并将从服务器看作是一个连接到主服务器的客户端来对待。【这时从服务器将具有服务器与客户端两个身份】

因为复制工作接下来的几个步骤都会以从服务器向主服务器发送命令请求的形式来进行,所以理解“从服务器是主服务器的客户端”这点非常重要

5.3,发送PING命令

当从服务器成为主服务器的客户端之后,第一件事就是向主服务器发送一个PING命令

在这里插入图片描述

此处的PING命令有两个作用:

  • 虽然主从服务器成功建立起套接字连接,但双方并未使用该套接字进行过任何通信,通过发送PING命令可以检查套接字读写状态是否正常
  • 通过发送PING命令查看主服务器是否能正常处理命令请求

从服务器在发送PING命令之后将遇到以下三种情况的其中一种:

  1. 主服务器向从服务器发送了命令回复,但由于主从服务器之间的网络连接不稳定,不能继续执行复制工作的后续步骤。当出现这种情况,从服务器断开并重新创建连向主服务器的套接字
  2. 如果主服务器向从服务器返回一个错误,那么表示主服务器暂时没办法处理服务器的命令请求,不能继续执行复制工作的后续步骤。当出现这种情况,从服务器断开并重新创建连向主服务器的套接字
  3. 如果从服务器读到PONG回复,表示主从服务器之间网络连接正常并且主服务器能正常处理从服务器的命令请求。这种情况下,从服务器可以继续执行复制工作的下个步骤

5.4,身份验证

从服务器接收到主服务器的PONG回复,下一步就是决定是否进行身份验证:

  • 如果从服务器设置了masterauth选项,那么进行身份验证
  • 如果从服务器没有设置masterauth选项,那么不进行身份验证

在需要进行身份验证的情况下,从服务器将向主服务器发送一条AUTH命令,命令的参数为服务器,masterauth选项的值。

假设从服务器的masterauth的值为10086,那么从服务器将向主服务器发送AUTH 10086

从服务器在身份验证阶段可能遇到的情况:

  • 主服务器没有设置requirepass选项,从服务器也没有设置masterauth选项,那么无事发生,复制工作继续进行
  • 主服务器设置了requirepass选项,从服务器没有设置masterauth选项,那么主服务器将返回一个NOAUTH错误;如果主服务器没有设置requirepass选项,从服务器设置了masterauth选项,那么主服务器将返回一个no password is set错误
  • 如果从服务器通过AUTH命令发送的密码和主服务器requirepass选项的值一致,那么主服务器将继续执行从服务器发送的命令,复制工作继续进行;如果不相同,那么主服务器将返回一个invalid password错误

所有的错误都会让从服务器停止当前复制工作,并从创建套接字开始重新执行复制,直到身份验证通过,或者从服务器放弃执行复制为止

在这里插入图片描述

5.5,发送端口信息

在身份验证步骤之后,从服务器将执行命令REPLCONF listening-port <port-number>向主服务器发送从服务器的监听端口号

主服务器接收到这个命令之后,将端口号记录在从服务器所对应的客户端状态的slave_listening_port属性中

5.6,同步

在这一步,从服务器将向主服务器发送PSYNC命令,执行同步操作,有关PSYNC的信息已经在上面讲述过了,所以不赘述

5.7,命令传播

当完成同步之后,主从服务器就会进入命令传播阶段,主服务器只要一直将自己执行的写命令发送给从服务器,而从服务器只需执行这些命令即可,就能保证主从服务器数据库状态一致

6,心跳检测

命令传播阶段,从服务器默认会以每秒一次的频率,向主服务器发送命令REPLCONF ACK <replication_offset>,其中replication_offset是从服务器当前的复制偏移量。

REPLCONF ACK命令对于主从服务器有三个作用:

  1. 检测主从服务器的网络连接状态
    1. 主从服务器之间通过发送和接收REPLCONF ACK命令来检查两者之间的网络连接是否正常:如果主服务器超过一秒钟没接收到从服务器发来的REPLCONF ACK命令,那么主服务器就知道主从服务器之间的连接出现问题了
  2. 辅助实现min-slaves选项
    1. Redismin-slaves-to-writemin-slaves-max-lag选项可以防止主服务器在不安全的情况下执行命令
    2. 假设min-slaves-to-wirte的值为3,min-slaves-max-lag的值为10。那么在从服务器的数量少于3个,或者三个从服务器的延迟(lag)值都大于或等于10秒时,主服务器会拒绝执行写命令
  3. 检测命令丢失
    1. 如果因为网络故障,导致主服务器发送给从服务器的写命令没有被从服务器接收,那么当从服务器向主服务器发送REPLCONF ACK命令时,带上从服务器的复制偏移量,主服务器接受命令会与自己的复制偏移量进行比较,就能得知是否发生了命令丢失。然后主服务器就会根据服务器提交的复制偏移量,将复制积压缓冲区中找到从服务器缺失的数据发送给从服务器

【值得一提的是:REPLCONF ACKRedis2.8版本新增的,Redis2.8以前的版本,即便发生了命令丢失,主从服务器都不会注意到,主服务器更不会向从服务器补发缺失的数据,所以为了保证复制时的数据一致性,最好使用Redis2.8及以上版本】

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值