数据复制

一个可能出错的事物与一个不可能出错的事物之间的主要区别是,当一个不可能出错的事物出错了,通常也就意味着不可修复

——Douglas Adams,《基本无害》(1992)

复制主要指通过互联网络在多台机器上保存相同的数据副本。通过数据复制方案,通常系统大袋以下目的:

  • 使数据在地理位置上更接近用户,从而降低访问延迟
  • 当部分组件出现故障,系统依然可以继续工作,从而提高可用性
  • 扩展至多台机器以同时提供数据访问服务,从而提高吞吐量

主节点与从节点

主从复制的工作原理如下:

  1. 指定某一个副本为主副本(主节点)。当客户写数据库时,必须将写请求首先发送给主副本,主副本首先将新数据写入本地存储
  2. 其他副本则全部称为从副本(从节点)。主副本把新数据写入本地存储后,然后将数据更改作为复制的日志或更改流发送给所有从副本。每个副本获得更高日志之后,将其应用到本地,且严格保持与主副本想用的写入顺序
  3. 客户端从数据库中读数据时,可以在主副本或者从副本上执行查询。再次强调,只有主副本才能接受写请求;从客户端角度看,从副本都是只读的

常用的关系型数据库都内置支持主从复制,一些非关系型数据库如MongoDB,RethinkDB,Espresso也支持主从复制。另外主从复制技术也不仅限于数据库,还广泛用于分布式消息队列如Kafka和RabbitMQ,以及一些网络文件系统和复制块设备(DRBD)

同步复制与异步复制

同步复制:主节点需要等待直到从节点确认完成了写入,然后才会向用户报告完成

异步复制:主节点给从节点发送复制消息后,不用等待从节点完成写入确认,就可以向主节点报告完成

同步复制的优点是,一旦向用户确认,从节点可以明确保证完成了与主节点的更新同步,数据已经处于最新版本。万一主节点发生故障,总是可以在从节点继续访问最新数据。缺点则是,如果同步的从节点语法完成确认(例如从节点发生崩溃,或者网络故障),写入就不能视为成功。主节点会阻塞其后所有的写操作,知道同步副本确认完成

时间中,如果数据库开启了同步复制,通常意味着其中某一个从节点是同步的,而其他节点则是异步模式。万一同步的从节点变得不可用或性能下降,则将另一个异步的从节点提升为同步模式。这样可以保证至少有两个节点(即主节点和一个同步从节点)拥有最新的数据副本。这种配置有时被称为半同步。

主从复制还经常会被配置为全异步模式。此时如果主节点发生失败且不可恢复,则所有尚未复制到从节点的写请求都会丢失。这意味着即使向客户端确认了写操作,却无法保证数据的持久化。全异步配置优点在于,不过从节点上的数据多么滞后,主节点总是可以继续响应写请求,系统吞吐性更好。

配置新的从节点

配置新的从节点的步骤:

  1. 在某个时间点对主节点的数据副本产生一个一致性快照,这样避免长时间锁定整个数据库。目前打多少数据库都支持此功能,快照也是系统备份所必须的。而在某种情况下,可鞥需要第三方工具,如MySQL的innobackupex。
  2. 将此快照拷贝到新的从节点
  3. 从节点链接到主节点并请求快照点滞后所发生的数据更改日志。因为在第一步创建快照时,快照与系统复制日志的某个确定位置相关联,这个位置信息在不同的系统有不同的称呼,如PostgreSQL将其称为“log sequence number”(日志序列号),而MySQL将其成为“binlog coordinnates”
  4. 获得日志滞后从节点来应用这些快照点滞后所有数据变更,这个过程称之为追赶。接下来,它可以继续处理主节点上新的数据变化。

处理节点失效

从节点失效:追赶式恢复

从节点的本地磁盘上都保存了副本收到的数据变更日志。如果从节点发生崩溃,然后顺利重启,或者主从节点之间发生暂时中断(闪断),则恢复比价容易,根据副本的复制日志,从节点可以知道在发生故障之前所处理的最后一笔事务,然后链接到主节点,并请求自那比事务滞后终端期间内所有数据变更。在收到这些数据变更日志滞后,将其应用到本地来追赶主节点。之后和正常情况一样持续接受来自主节点数据流的变化

主节点失效:节点切换

节点切换过程:选择某个从节点将其提升为主节点;客户端也需要更新,这样之后写请求会发送给新的主节点,然后其他从节点要加收来自新的主节点上的变更数据,这一过程称为切换

自动切换步骤:

  1. 确认主节点失效。大多数系统都采用基于超时的机制:节点间频繁地互相发送心跳存活消息,如果发现某一个节点在一段比较长的时间(30s)内没有响应,即认为该借点发生失效
  2. 选举新的主节点。可以功过选举的方式(超过多数的几点达成共识)来选举新的主节点,或者由之前选定的某控制节点来指定新的主节点。候选节点最好与原主节点的数据差异最小,这样可以最小化数据丢失的风险。
  3. 重新配置系统使新主节点生效。客户端现在需要将写请求发送给新的主节点。如果原主节点之后重新上线,可能仍然自认为是主节点,而没有意识到其他节点已经达成共识迫使其下台。这是要确保原主节点降级为从节点,并认可新的主节点

可能存在的问题:

  • 如果使用了一部复制,且失效之前,新的主节点并未收到原主节点的所有数据;在选举之后,原主节点很快又重新上线并加入到集群,接下来新的主节点很可能会收到冲突的写请求,这是因为原主节点未意识到角色变化,还会尝试同步其他从节点,但其中一个现在已经接管称为现任主节点。常见的解决方案是,原主节点上未完成的复制的写请求就此放弃,但这可能会违背数据更新持久化的承诺
  • 如果数据库之外有其他系统依赖于数据库的内容并在一起谢荣使用,丢弃数据的方案就特别危险。例如在GItHub的一个事故中,某个数据并非完全同步的MySQL从节点被提升为主节点,数据库使用了自增计数器将主键分配给新创建的行,但是新的主节点计数器落后于原主节点,它重新使用了一杯原主节点分配出去的某些主键,而恰好这些主机嗯一杯外部Redis所引用,结果出现MySQL和Redis之间的不一致,最后导致了某些私有数据被错误的泄露给其他用户
  • 在某些故障情况下,可能会发生两个节点同时都自认为是主节点。这种情况被称为脑裂,它非常危险:两个主节点都可能接受请求,并且没有很好解决冲突的方法,最会数据可能会丢失或者破坏。最为一种安全应急方案,有些系统会采取措施来强制关闭其中一个节点。然而,如果设计或者实现考虑步骤,可能会出现两个节点都被关闭的情况
  • 如何设置合适的超时来检测主节点失效呢?主节点失效后,超时时间设置的越长也意味着总体恢复时间就越长。但如果超时设置太短,可能会导致很多不必要的切换。

复制日志的实现

基于语句的复制

主节点记录所执行的每个写请求并将该操作语句作为日志发送给从节点。这意味着每个insert,update或delete语句都会转发给从节点,并且每个从节点都会分析并执行这些SQL语句

不适用场景:

  • 任何调用非确定性函数的语句,如NOW()获取当前时间,或RAND()获取一个随机数等,可能会在不同的副本上产生不同的值
  • 如果语句中使用了自增列,或者依赖于数据库的现有数据,则所有副本必须按照完全相同的书序执行,否则可能会带来不同的结果。进而,如果有多个同事并发执行的事务时,会有很大的限制
  • 有副作用的语句(触发器,存储过程,用户定义的函数等),可能会在每个副本上产生不同的副作用

基于预写日志(WAL)传输

不论采用日志结构存储引擎或者Btree结构,都会讲数据库写入的字节序列记入日志。因此可以使用完全相同的日志在另一个节点上构建副本:除了将日志写入磁盘之外,主节点还可以通过网络将其发送给从节点;从节点收到日志进行处理,简历和主节点内容完全相同的数据副本。

这种方式的主要缺点是日志描述的数据结果非常底层:一个WAL包含了哪些磁盘块的哪些字节发生改变,诸如此类的细节。这是的复制方案和存储引擎紧密耦合。如果数据库的存储格式从一个版本改为另一个版本,那么系统无法支持主从节点上运行不同版本的软件

如果复制协议允许从节点的软件版本比主节点更新,则可以实现数据库软件的不停机升级:首先升级从节点,然后执行主节点切换,使升级后的从节点称为新的主节点。相反,复制协议如果要求版本严格一致(WAL传输),那么势必以停机为代价

基于行的逻辑日志复制

逻辑日志:将复制和存储引擎采用不同的日志格式,这样复制与存储逻辑剥离。这种复制日志称为逻辑日志,以区分屋里存储引擎的数据表示

逻辑日志的内容:

  • 对于行插入,日志要包含所有相关列的新值。
  • 对于行删除,日志要有足够的额信息俩唯一标识已删除的行,通常是靠主键,但如果表上没有定义主键,就需要记录所有列的旧值
  • 对于行更新,日志包含足够的信息来唯一标识更新的行,以及所有列的新值(至少包含所有已更新列的新值)

如果一条事务设计多行的修改,则会产生多个这样的日志记录,并在后面跟着一条记录,指出该事务已经提交。

MySQL的二进制日志binlog(当配置为基于行复制时)使用该方式

基于触发器的复制

如果有时候需要更高的灵活性,比如指向复制数据的一部分,或者想从一种数据库到另一种数据库,或者需要定制管理冲突解决逻辑,则需要将复制控制交给应用程序层。

这个可以借助许多关系数据库都支持的功能:触发器和存储过程

基于触发器的复制通常比其他复制方式开销更改,也比数据库内置复制更容易出错,或者暴露一些限制

复制滞后问题

主从复制要求所有写请求都经过主节点,从节点只接受读请求。创建多个从节点,将读请求分发给这些从节点,从而减轻主节点负载并允许读取请求就近满足

由于并非所有的写入都反映在从副本上,如果同时对主节点和从节点发起相同的查询,可能会得到不同的结果。这种不一致知识一个暂时的状态,如果停止写数据库,经过一段时间后,从节点最终会赶上并与主节点保持一致。这种效应也被称为最终一致性

总的来说,副本落后的程度理论上并没有上限。正常情况下,主节点和从节点完成些操作之间的时间延迟(复制滞后)可能不足1秒。这样的滞后在实践中通常不会导致太大的影响。但是如果系统已接近设计上限,或者网络存在问题,则滞后可能轻松增加几秒甚至几分钟不等

读自己的写

对于异步复制存在这样一个问题,用户在写入不就即查看数据,则新数据可能尚未到达从节点。对用户来讲,似乎刚刚提交的数据丢失了

这时候我们需要"写后读一致性",也称为读写一致性。该机制保障如果用户重新加载页面,他们总能看到自己最近提交的更新。

读写一致性的实现方案:

  • 如果用户访问可能会被修改的内容,从主节点读取;否则,在从节点读取。这背后就要求有一些方法在实际执行查询之前,就已经内容是否可能会被修改
  • 如果应用的大部分内容都可能被所有用户修改,那么上述方法将不太有效,它会导致大部分内容都必须经由主节点,这就上市了读操作的扩展性。此时需要其他方案来判断是否从主节点读取。例如,跟踪最近更新的时间,如果更新后一分钟之内,则总是在主节点读取;并监控从节点的复制滞后程度,避免从哪些滞后时间超过一分钟的从节点读取
  • 客户端还可以记住最近更新时的时间戳,并附带在读请求中,据此信息,系统可以确保对该用户提供读服务时都应该至少包含了该时间戳的更新。如果不够新,要么交由另一个副本来处理,要不等待直到副本接收到最近的更新。时间戳可以是逻辑时间戳(用来指示写入顺序的日志序列号)或实际系统时钟(时钟同步是又一个关键点)
  • 如果副本分布在多数据中心(例如考虑与用户的地理接近,以及高可用性),情况会更复杂些。必须先把请求路由到主节点所有的数据中心(该数据中心可能离用户很远)

如果同一用户可能会从多个设备访问数据,需要考虑一些其他问题

  • 记住用户上次更新时间戳的方法实现起来会比较困难,因为在一台设备上运行的代码完全无法知道在其他设备上发生了什么。此时,元数据必须做到全局共享
  • 如果副本分布在多数据中心,无法保证来自不同设备的连接经过路由滞后都到达同一个数据中心。

单调读

假定用户从不同副本进行了多次读取,用户刷新一个网页,读请求可能被随机路由到某个从节点,如果执行了两次完全相同的查询,从节点滞后时间不同,可能会看到不一样的结果,这时候需要单调读一致性

单调性是一个比强一致弱,但比最终一致性强。当读取数据时,单调读保证,如果某个用户一次进行多次读取,则他绝不会看到回滚现象,即在读取新值之后又发生读旧值的情况

实现单调读的一种方式是,确保每个用户总是从固定的同一副本执行读取(不同的用户可以从不同的副本中读取)。例如基于用户ID的哈希的方法

前缀一致读

前缀一致读:对于一系列按照某个顺序发生的写请求,那么读取这些内容时也会按照当时写入的顺序。

这是分区数据库中出现的一个特殊问题,如果数据库总是以相同的顺序写入,则读取总是看到一致的序列,不会发生这种反常。然而,许多分布式数据库中,不同的分区独立运行,因此不存在全局写入顺序。这就导致当用户从数据库中读数据时,可能会看到数据库的某部分旧值和另一部分新值。

一个解决方案是确保任何具有因果顺序关系的写入都叫个一个分区来完成,但该方案真实实现效率会大打折扣。

多主节点复制

主从复制存在一个明显得缺点:系统只有一个主节点,而所有写入都必须经由主节点。如果由于某种原因,例如与主节点的网络中断而导致主节点无法连接,主从复制方案就会影响所有的写入操作

对主从复制模型记性扩展,则可以配置多个主节点,每个主节点都可以接受下写操作,后面的复制的流程类似:处理写的每个主节点都必须将该数据更改转发到所有其他节点,这就是多主节点复制。此时每个主节点还同时扮演其他主节点的从节点

使用场景

多数据中心

为了容忍整个数据中心级别故障或者更接近,可以把数据库的副本横跨多个数据中心,有了多主节点复制模型,则可以在每个数据中心都配置主节点。在每个数据中心内,采用常规的主从复制方案;而数据中心之间,有各个数据中心的主节点来负责同其他数据中心的主节点进行数据的交换、更新

多数据中心环境下,部署单主节点的主从复制方案与多主复制方案之间的差异:

性能:

对于主从复制,每个写请求都必须经由广域网传送至主节点所在的数据中心。这回大大增加写入延迟,并基本偏离了采用多数据中心的初衷(即就近访问)。而在多个主节点模型中,每个写操作都可以在本地数据中心快速响应,然后采用与复制方式将变化同步到其他数据中心。因此对上层应用游戏哦屏蔽了数据中心之间的网络延迟,使得终端用户所体验到的性能更好

容忍数据中心失效

对于主从复制,如果主节点所在的数据中心发生故障,必须切换至另一个数据中心,将其中的一个从节点被提升为主节点。在多主节点模型中,每个数据中心则可以独立于其他数据中心继续运行,发生故障的数据中心在恢复之后更新到最新状态

容忍网络问题

数据中心之间的通信通常经由广域网,它往往不如数据中心内的本地网络可靠。对于主从复制模型,由于写请求是同步操作,对数据中心之间的网络性能和稳定性更加依赖。多主节点模型则通常采用异步同步复制,可以更好地容忍此类完题

有些数据库已内嵌支持了多主复制,但有些则借助外部工具来实现,例如MySQL的Tungsten Replicator,PostgreSQL的 BDR,Oracle的GoldenGate。

多主复制的问题:不同的数据中心可能会同时修改相同的数据,因而必须解决潜在的写冲突。

离线客户端操作

另一种多主复制比较适合的场景是,应用在于网络断开后还需要继续工作

这种情况下,每个设备都有一个充当主节点的本地数据库(用来接受写请求),然后再所有设备之间采用异步方式同步这些多主节点上的副本,同步滞后可能是几小时或者几天,具体时间取决于设备何时可以再次联网

协作编辑

实时写作编辑应用程序允许多个用户同时编辑文档。当用户编辑文档,所做的更改回立即应用到本地副本,然后异步复制到服务器以及编辑同一文档的其他用户

如果要确保不会发生编辑冲突,做应用程序必须先将文档锁定,然后才能对其进行编辑。为了加快写作效率,可编辑粒度要非常小

处理写冲突

同步与异步冲突检测

理论上,可以做到同步冲突检测,即等待写请求完成ui所有副本的同步,然后再通知用户写入成功。但是,这样做将会失去多主节点的主要有时:允许每个主节点独立接受写请求。

如果确实想要同步方式冲突检测,或许应该考虑采用单主节点的主从复制模型

避免冲突

处理冲突最理想的策略是避免发生冲突,即如果应用层可以保证对特定记录的写请求总是通过同一个主节点,这样就不会发生写冲突

例如用户需要更新自己的数据,那么我们确保特定用户的更新请求总是路由到特定的数据中心,这样可以避免冲突

但是,有时可能需要改变实现指定的主节点,例如由于该数据中心发生故障或者用户已经漫游到另一个位置,因而更靠近新数据中心。此时冲突避免方式不再有效

收敛于一致状态

多主复制可能会出现ABA问题,因此,数据库必须以一种收敛趋同的方式来解决冲突,这也意味着当所有更改最终被复制、同步滞后,所有副本的最终值是相同

实现收敛的冲突解决有以下可能的方式

  • 给每个写入分配唯一的ID,例如时间戳,挑选最高ID的写入作为胜利者,并将其他写入丢弃。这种方式很容易造成数据丢失
  • 为每个副本分宜一个唯一ID,并指定规则,例如序号高的副本写入始终优先于序号低的副本。这种方法也可能导致数据丢失
  • 以某种中方式将这些值合并在一起。例如,按字母顺序排序,然后拼接在一起
  • 利用好预定好的格式来记录和保留冲突相关的所有信息,然后依靠应用层逻辑,时候解决冲突

自定义冲突解决逻辑

解决冲突最合适的方式可能还是依靠应用层,所以打多少多主节点复制模型都有工具来让用户编写应用代码来解决冲突

在写入时执行:只要数据库系统在复制变更日志时检测到冲突,就会调用应用层的冲突处理程序。例如,Bucardo支持编写一段Perl代码。则个处理程序通常不能提示用户,而只能在后台运行,这样速度快

在读取时执行:当检测到冲突时,所有冲突写入值都会暂时保持下来。下一次读取数据时,会将数据的多个版本读返回给应用层,应用层可能会提示用户或自动解决冲突,并将最后结果返回到数据库,CouchDB采用这样的处理方式

拓扑结构

复制的拓扑结构描述了写请求从一个节点的传播到其他节点的通信路径。如果有俩呢主节点,则只存在一个合理的拓扑结构,如果存在两个以上的主节点,则会有多个可能的同步拓扑结构

最常见的拓扑结构是全部-至-全部,每个主节点将其写入同步到其他所有主节点。

环形拓扑结构:默认情况下MySQL只支持环形拓扑结构,其中的每个节点接受来自前序节点的写入,并将这些写入转发给后序节点

星型结构:一个指定根节点将写入转发给所有其他节点。星型拓扑还可以推广到树状结构

在环形和星型拓扑中,写请求需要通过过个节点才能到达所有的副本,即中间节点需要转发从其他节点收到的数据变更。为防止无限循环,每个节点需要赋予一个唯一的标识符,在复制日志中每个写请求都标记了已通过的节点标识符

环形和星型拓扑的问题是,如果某一个节点发生了故障,在修复之前,会影响其他节点之间复制日志的转发。可以采用重新配置拓扑结构的方法暂时排除掉故障节点。对于连接更密集的拓扑(例如全部到全部),消息可以沿着不同的路径传播,避免了单点故障,因而有更好的容错性

全链接拓扑也存在一些自身的问题。主要是存在某些网络连路比其他链路更快的情况,从而导致复制日志之间的覆盖。

为了使得日志消息正确有序,可以使用一种称为版本向量的技术。

无主节点复制

无主节点复制:放弃主节点,允许任何副本直接接受来自客户端的写请求。

节点失效时写入数据库

无主节点配置不存在切换主节点操作,用户将写请求并行发送到三个副本,有两个可用副本接受写请求,而不可用的副本无法处理写请求。如果三个副本中有两个成功确认写操作,用户收到两个确认的回复后,即可认为写入成功

当失效节点重新上线后,客户端重新开始读取里面的内容。由于失效期间发生的任何在该节点上都尚未同步,因此读取可能会得到过期的数据

为了解决这个问题,当一个客户端从数据库中读取数据时,它不是向一个副本发送请求,而是并行发送到多个副本。客户端可能会得到不同节点的不同响应,包括某些节点的新值和某些节点的旧值。可以采用版本号技术确定哪个值更新

读修复和反熵

复制模型应确保所有数据最终复制到所有的副本。当一个失效节点重新上线后,要如何赶上中间错过那些写请求?

读修复:当客户端并行多个副本时,可以检测到过期的返回值。例如,用户从副本3获得的是版本6,而从副本1和2得到的死版本7.客户端可以判断副本6是一个过期值,然后将新值写入到该副本。这种方法主要适合那些被频繁读取的场景

反熵过程:一些数据存储有后台进程不断查找副本之间的数据差异,将任何缺少的数据从一个副本复制到另一个副本。与基于主节点复制的复制日志不同,此反熵过程并不保证以特定的顺序复制写入,并且会引入明显得哦那个不滞后

读写quorum

当成功的写操作要求三个副本中至少两个完成,这意味着至多有一个副本可能包含旧值。因此,在读取时需要至少向两个副本发起读请求,通过版本号可以确定一定至少一个包含新值。如果第三个副本出现停机或者响应缓慢,则读取仍可以继续并返回最新值

Quorum一致性的局限性

  • 如果采用了sloppy quorum(宽松的quorum),写操作的w节点和读操作的r节点可能完全不同,因此无法保证读写请求一定存在重叠节点
  • 如果两个写操作同时发生,则无法明先后顺序。这种情况下,唯一安全的解决方案是合并并发写入
  • 如果写操作与读操作同时发生,写操作可能仅在一部分副本上完成。此时,读取时返回旧值还是新值存在不确定性
  • 如果某些副本上已经写入成功,而其他一些副本写入失败,且总的成功副本数少于w,那些已成功的副本不会做回滚。这意味着尽管这样的写操作被视为失败,后续的读操作仍可能返回新值
  • 如果具有新值的节点后来发生失效,但恢复数据来自某个旧值,则总的新值副本数会低于w,这打破了之前的判定条件

监控旧值

对于主从复制的系统,数据库通常会导出复制滞后的相关指标,可以将其集成到统一监控模块。原理大致是:由于主节点和从节点上写入都遵从相同的顺序,而每个节点都维护了复制日志执行的当前偏移量。通过对比主节点和从节点当前偏移量,即可衡量从节点落后于主节点的程度

目前针对无主节点的监控比较困难,主要想法是根据参数n,w,r来预测读到旧值的期望百分比(想知道亚马逊内部是怎么做无主节点监控的

宽松的quorum与数据回传

quorum并不总如期待的那样提供高容错能力。一个网络中断可以很容易切断一个客户端到多数数据库节点的链接。尽管这些集群节点是活着的,而其他客户端也确实可以正常链接,但对于断掉连接的客户端来说,情况无疑等价于集群整体失效。这种情况下,很可能无法满足最低的w和r所要求的节点数,因此导致客户端无法满足quorum要求

放松的仲裁:写入和读取仍然需要w和r个成功的响应,但包含了那些并不在先前指定的n个节点。

数据回传:一旦网络问题得到解决,临时节点需要把接收到的写入全部发送到原始主节点上

sloppy quorum 对于提高写入高可用特别有用:只要有任何w个节点可用,数据库就可以接受新的写入 ,然而这意味着,即使满足w+r>n,也不能保证在读取某个键时,一定能读到最新值,因为新值可能被临时写入到n之外的某些节点中尚未回传

多数据中心操作

Cassandra和Voldemort在其默认配置的无主节点模型中都支持跨数据中心操作:副本的数量n是包括所有数据中心的节点总数。配置时,可以指定每个数据中心各有多少副本。每个客户端的写入都会发送到所有副本,但客户端通常只会等待来自本地数据中心内quorum节点数的确认,这样避免了高延迟和跨数据中心可能的网络异常。尽管可以灵活配置,但对远程数据中心的写入由于延迟很高,通常被配置为异步方式

Riak则将客户端与数据库节点之间的通信限制在一个数据中心内,因此n描述的是一个数据总新内的副本数量。集群之间跨数据中心的复制则在后台异步运行

检测并发写

很复杂,暂时没有理解,不记录

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值