Redis常见面试题整理



Redis面试题及答案


1、什么是 Redis?

Redis的的是完全开源免费的,遵守BSD协议,是一个高性能基于内存的键值(key-value)数据库。是当前最热门的的的NoSql数据库之一,也被人们称为数据结构服务器。


2、Redis 的数据类型?

Redis是一个key-value兼职对数据库。它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。

3、使用 Redis 有哪些好处?

  1. 速度快,因为数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1)。
  2. 支持丰富数据类型,支持string,list,set,sorted set,hash。
  3. 支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行。
  4. 丰富的特性:可用于缓存,消息,按key设置过期时间,过期后将会自动删除。
  • 为什么选择redis

由于内存的读写速度远快于硬盘,因此Redis的的的在性能上对比其他基于硬盘存储的数据库有非常明显的优势。

项目中使用Redis的的的,主要是从两个角度去考虑:性能、并发。

  • 性能
    在碰到需要执行耗时特别久,且结果不频繁变动的SQL,就特别适合将运行结果放入缓存,这样,后面的请求就去缓存中读取,请求使得能够迅速响应。
  • 并发
    在大并发的情况下,所有的请求直接访问数据库,数据库会出现连接异常。这个时候,就需要使用的的Redis的做一个缓冲操作,让请求先访问到的Redis的的,而不是直接访问数据库。

4、Redis 相比 Memcached 有哪些优势?

  1. memcached所有的值均是简单的字符串,redis作为其替代者,支持更为丰富的数据类型。
  2. redis的速度比memcached快很多。
  3. redis可以持久化其数据。

5、Memcache 与 Redis 的区别都有哪些?

(1)存储方式

Memecache把数据全部存在内存之中,断电后会挂掉,数据不能超过内存大小。

Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用。支持数据的备份,即master-slave模式的数据备份。

(2)数据支持类型

Memcache对数据类型支持相对简单。
Redis有较复杂的数据类型。

(3)使用底层模型不同

它们之间底层实现方式 以及与客户端之间通信的应用协议不一样。

Redis直接自己构建了VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求。

(4)value大小

redis最大可以达到1GB,而memcache只有1MB。

6、Redis 是单进程单线程的吗?

  • redis是单线程的,是纯内存数据库,一般都是简单的存取操作,线程占用的时间很多,时间的花费主要集中在IO上,所以读取速度快。
  • 再说一下IO,Redis使用的是非阻塞IO,IO多路复用,使用了单线程来轮询描述符,将数据库的开、关、读、写都转换成了事件,减少了线程切换时上下文的切换和竞争。
  • Redis采用了单线程的模型,保证了每个操作的原子性,也减少了线程的上下文切换和竞争。采用网络IO多路复用技术来保证在多连接的时候,保证系统的高吞吐量。多路-指的是多个socket连接,复用-指的是复用一个线程。多路复用主要有三种技术:select,poll,epoll。epoll是最新的也是目前最好的多路复用技术。
  • 另外,数据结构也帮了不少忙,Redis全程使用hash结构,读取速度快,还有一些特殊的数据结构,对数据存储进行了优化,如压缩表,对短数据进行压缩存储,再如,跳表,使用有序的数据结构加快读取的速度。
  • 还有一点,Redis采用自己实现的事件分离器,效率比较高,内部采用非阻塞的执行方式,吞吐能力比较大。

7、一个字符串类型的值能存储最大容量是多少?

Redis数据类型容量 / 个数
String一个String类型的value最大可以存储512M
List / Set /Hash /Sorted Set元素个数最多为2^32-1个,也就是4294967295个


8、Redis 的持久化机制是什么?各自的优缺点?

  • Redis 持久化的两种方式

RDB:RDB 持久化机制,是对 redis 中的数据执行周期性的持久化。

AOF:AOF 机制对每条写入命令作为日志,以 append-only 的模式写入一个日志文件中,在 redis 重启的时候,可以通过重新按顺序执行 AOF 日志中的写入指令来重新构建整个数据集。

  • RDB优缺点
  1. RDB 会生成多个数据文件,每个数据文件都代表了某一个时刻中 redis 的完整数据,这种多个数据文件的方式,非常适合做冷备,可以将这种完整的数据文件发送到一些远程的安全存储上去,以预定好的备份策略来定期备份 redis 中的数据。

  2. RDB 对 redis 对外提供的读写服务,影响非常小,可以让 redis 保持高性能,因为 redis 主进程只需要 fork 一个子进程,让子进程执行磁盘 IO 操作来进行 RDB 持久化即可。

  3. 相对于 AOF 持久化机制来说,直接基于 RDB 数据文件来重启和恢复 redis 进程,更加快速。

  4. 如果想要在 redis 故障时,尽可能少的丢失数据,那么 RDB 没有 AOF 好。一般来说,RDB 数据快照文件,都是每隔 5 分钟,或者更长时间生成一次,这个时候就得接受一旦 redis 进程宕机,那么会丢失最后一次的 5 分钟的数据。

  5. RDB 每次在 fork 子进程来执行 RDB 快照数据文件生成的时候,如果数据文件特别大,可能会导致对客户端提供的服务暂停数毫秒,或者甚至数秒。

  • AOF优缺点
  1. AOF 可以更好的保护数据不丢失,一般 AOF 会每隔 1 秒,通过一个后台线程执行一次fsync操作,最多丢失 1 秒钟的数据。
  2. AOF 日志文件以 append-only 模式写入,所以没有任何磁盘寻址的开销,写入性能非常高,而且文件不容易破损,即使文件尾部破损,也很容易修复。
  3. AOF 日志文件即使过大的时候,出现后台重写操作,也不会影响客户端的读写。因为在 rewrite log 的时候,会对其中的指令进行压缩,创建出一份需要恢复数据的最小日志出来。在创建新日志文件的时候,老的日志文件还是照常写入。当新的 merge 后的日志文件 ready 的时候,再交换新老日志文件即可。
  4. AOF 日志文件的命令通过非常可读的方式进行记录,这个特性非常适合做灾难性的误删除的紧急恢复。比如某人不小心用 flushall 命令清空了所有数据,只要这个时候后台 rewrite 还没有发生,那么就可以立即拷贝 AOF 文件,将最后一条 flushall 命令给删了,然后再将该 AOF 文件放回去,就可以通过恢复机制,自动恢复所有数据。
  5. 对于同一份数据来说,AOF 日志文件通常比 RDB 数据快照文件更大。
  6. AOF 开启后,支持的写 QPS 会比 RDB 支持的写 QPS 低,因为 AOF 一般会配置成每秒 fsync 一次日志文件,当然,每秒一次 fsync,性能也还是很高的。(如果实时写入,那么 QPS 会大降,redis 性能会大大降低)
  7. 以前 AOF 发生过 bug,就是通过 AOF 记录的日志,进行数据恢复的时候,没有恢复一模一样的数据出来。所以说,类似 AOF 这种较为复杂的基于命令日志 / merge / 回放的方式,比基于 RDB 每次持久化一份完整的数据快照文件的方式,更加脆弱一些,容易有 bug。不过 AOF 就是为了避免 rewrite 过程导致的 bug,因此每次 rewrite 并不是基于旧的指令日志进行 merge 的,而是基于当时内存中的数据进行指令的重新构建,这样健壮性会好很多。
  • RDB 和 AOF 到底该如何选择
  1. 如果对数据完整性不敏感可以使用 RDB,因为这种方式可能会导致你丢失很多数据;
  2. 不要仅仅使用 AOF,因为那样有两个问题:第一,你通过 AOF 做冷备,没有 RDB 做冷备来的恢复速度更快;第二,RDB 每次简单粗暴生成数据快照,更加健壮,可以避免 AOF 这种复杂的备份和恢复机制的 bug;
  3. redis 支持同时开启开启两种持久化方式,存储设备足够的情况下我们可以综合使用 AOF 和 RDB 两种持久化机制,用 AOF 来保证数据不丢失,作为数据恢复的第一选择; 用 RDB 来做不同程度的冷备,在 AOF 文件都丢失或损坏不可用的时候,还可以使用 RDB 来进行快速的数据恢复。

9、Redis 常见性能问题和解决方案

  1. Master写内存快照,save命令调度rdbSave函数,会阻塞主线程的工作,当快照比较大时对性能影响是非常大的,会间断性暂停服务,所以Master最好不要写内存快照。
  2. Master AOF持久化,如果不重写AOF文件,这个持久化方式对性能的影响是最小的,但是AOF文件会不断增大,AOF文件过大会影响Master重启的恢复速度。Master最好不要做任何持久化工作,包括内存快照和AOF日志文件,特别是不要启用内存快照做持久化,如果数据比较关键,某个Slave开启AOF备份数据,策略为每秒同步一次。
  3. Master调用BGREWRITEAOF重写AOF文件,AOF在重写的时候会占大量的CPU和内存资源,导致服务load过高,出现短暂服务暂停现象。
  4. Redis主从复制的性能问题,为了主从复制的速度和连接的稳定性,Slave和Master最好在同一个局域网内。

10、Redis 过期键的删除策略

(1)过期删除策略

redis数据库键的过期时间都保存在过期字典中,根据系统时间和存活时间判断是否过期。

(2)redis有三种不同的删除策略

  1. 定时删除:实现方式,创建定时器。
  2. 惰性删除:每次获取键时,检查是否过期。
  3. 定期删除:每隔一段时间,对数据库进行一次检查,删除过期键,由算法决定删除多少过期键和检查多少数据库。

(3)优缺点

  1. 定时删除,对内存友好,但是对cpu很不友好。
  2. 惰性删除,对cpu友好,对内存很不友好
  3. 定期删除,是两种折中,但是,如果删除太频繁,将退化为定时删除,如果删除次数太少,将退化为惰性删除。

(4)算法

  1. 惰性删除

策略由expireIfNeeded函数实现,所有读写命令在执行之前都会调用该函数进行检查。
expireIfNeeded

  1. 定时删除

删除策略由activeExpireCycle算法决定关键点,遍历数据库,如果时间到,则终止;遍历数据库接着上次的进度,直到所有数据库遍历完,再接着遍历;遍历库时候,随机取出一定数量的随机键。

(5)RDB和AOF时过期键的处理

  1. RDB生成时,过期键会被过滤。RDB载入时,以主服务器运行,则过滤过期键。以从服务器运行,则忽略过期键,不做处理。

  2. AOF生成时,如果过期键还没被删除,则忽略,当被删除以后,会追加记录一条DEL命令。AOF载入时,过期键会被过滤。

11、Redis 的回收策略(淘汰策略)

Redis可以看作是一个内存数据库,可以通过Maxmemory指令配置Redis的数据集使用指定量的内存。设置maxmemory为0,则表示无限制(这是64位系统的默认行为,而32位系统使用3GB内存极限)。当内存使用达到maxmemory极限时,需要使用某种淘汰算法来决定清理掉哪些数据,以保证新数据的存入。

  • 常用的淘汰算法

FIFO:First In First Out,先进先出。判断被存储的时间,离目前最远的数据优先被淘汰。
LRU:Least Recently Used,最近最少使用。判断最近被使用的时间,目前最远的数据优先被淘汰。
LFU:Least Frequently Used,最不经常使用。在一段时间内,数据被使用次数最少的,优先被淘汰。

#达到内存上限后的处理策略,即过期淘汰策略,共有6种
# volatile-lru 只对设置了过期时间的key进行LRU(最近少使用)算法,默认此策略
# allkeys-lru 对所有的key进行LRU算法
# volatile-random 对即将过期的key随机删除
# allkeys-random 对所有key随机删除
# volatile-ttl 删除即将过期的
# noeviction 永不过期,抛出错误


12、为什么 Redis 需要把所有数据放到内存中?

Redis为了达到最快的读写速度,将数据都读到内存中,并通过异步的方式将数据写入磁盘。所以redis具有快速和数据持久化的特征。

如果不将数据放到内存中,磁盘I/O速度会严重影响redis性能。如果设置了最大使用内存,则数据量达到内存限制值之后,将不能继续插入新值,可启用淘汰策略。

13、Redis 的同步机制

  • 旧版复制功能的实现(Redis2.8版本之前)

Redis的复制功能分为同步(sync )和命令传播( command propagate )两个操作:

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

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

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

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

在同步操作执行完毕之后,主从服务器两者的数据库将达到一致状态,但这种一致并不是一层不变的,每当主服务器执行客服端发送的写命令时,主服务器的数据库就有可能会被修改,并导致主从服务器状态不再一致。

为了让主从服务器再次回到一致状态,主服务器需要对从服务器执行命令传播操作:主服务器会将自己执行的写命令,也即是造成主从服务器不一致的那条写命令,发送给从服务器执,行,当从服务器执行了相同的写命令之后,主从服务器将再次回到一致状态。

  • 旧版复制功能的缺陷

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

  1. 初次复制:从服务器以前没有复制过任何主服务器,或者从服务器当前要复制的主服务器和上一次复制的主服务器不同。
  2. 断线后重复制:处于命令传播阶段的主从服务器因为网络原因而中断了复制,但从服务器通过自动重连接重新连上了主服务器,并继续复制主服务器。

对于初次复制来说,旧版复制功能能够很好地完成任务,但对于断线后重复制来说,旧版复制功能虽然也能让主从服务器重新回到一致状态,但效率却非常低,从服务器需要让主服务器将所有执行的写命令的RDB文件,从新发送给从服务器。但主从服务器断开的时间可能很短,主服务器在断线期间执行的写命令可能很少,而执行少量写命令所产生的数据量通常比整个数据库的数据量要少得多,在这种情况下,为了让从服务器补足一小部分缺失的数据,却要让主从服务器重新执行一次SYNC命令,这种做法无疑是非常低效的。

  • 新版复制功能的实现

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

PSYNC命令具有完整重同步( full resynchronization)和部分重同步( partial resynchronization)两种模式,

其中完整重同步用于处理初次复制情况:完整重同步的执行步骤和SYNC命令的执行步骤基本一样,它们都是通过让主服务器创建并发送RDB文件,以及向从服务器发送保存在缓冲区里面的写命令来进行同步。

而部分重同步则用于处理断线后重复制情况:当从服务器在断线后重新连接主服务器时,如果条件允许,主服务器可以将主从服务器连接断开期间执行的写命令发送给从服务器,从服务器只要接收并执行这些写命令,就可以将数据库更新至主服务器当前所处的状态。

PSYNC命令的部分重同步模式解决了旧版复制功能在处理断线后重复制时出现的低效情况。

  • 部分重同步的实现

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

  1. 主服务器的复制偏移量( replication offset )和从服务器的复制偏移量。主服务器和从服务器会分别维护一个复制偏移量,断线时根据偏移量判断复制程度。
  2. 主服务器的复制积压缓冲区(replication backlog),是由主服务器维护的一个固定长度(fixed-size )先进先出( FIFO )队列,默认大小为1MB,当主服务器进行命令传播时,它不仅会将写命令发送给所有从服务器 还会将写命令人队到复制积压缓冲区里面 。断线后当服务器重连上时,判断挤压缓冲区是否存在操作缓冲,没有则进行完整重同步操作。
  3. 服务器的运行ID (run ID ),每个服务器存在独立运行ID。

14、Pipeline 有什么好处,为什么要用 pipeline?

pipeline,中文意为管线,意义等同于流水线。 pipeline的概念抽象出来为:将一件需要重复做的事情切割成各个不同的阶段,每一个阶段由独立的单元负责。所有待执行的对象依次进入作业队列,可以复用时间片段节省时间提高效率。
pipeline
redis客户端执行一条命令分4个过程:
发送命令-〉命令排队-〉命令执行-〉返回结果

这个过程称为Round trip time(简称RTT, 往返时间),mget mset有效节约了RTT,但大部分命令(如hgetall,并没有mhgetall)不支持批量操作,需要消耗N次RTT ,这个时候需要pipeline来解决这个问题。

  • 为什么用pipeline

Redis本身是基于Request/Response协议(停等机制)的,正常情况下,客户端发送一个命令,等待Redis返回结果,Redis接收到命令,处理后响应。在这种情况下,如果同时需要执行大量的命令,那就是等待上一条命令应答后再执行,这中间不仅仅多了RTT(Round Time Trip),而且还频繁调用系统IO,发送网络请求。为了提升效率,这时候pipeline出现了,它允许客户端可以一次发送多条命令,而不等待上一条命令执行的结果,这和网络的Nagel算法有点像(TCP_NODELAY选项)。pipeline不仅减少了RTT,同时也减少了IO调用次数(IO调用涉及到用户态到内核态之间的切换)。

  • pipeline的说明

原生批命令(mset, mget)是原子性(原子性概念:一个事务是一个不可分割的最小工作单位,要么都成功要么都失败。原子操作是指你的一个业务逻辑必须是不可拆分的. 处理一件事情要么都成功,要么都失败,原子不可拆分),pipeline是非原子性,需要服务端与客户端共同完成。

使用pipeline组装的命令个数不能太多,不然数据量过大,增加客户端的等待时间,还可能造成网络阻塞,可以将大量命令的拆分多个小的pipeline命令完成。

15、是否使用过 Redis 集群,集群的原理是什么?

参考之前集群文章内容:一起学习Redis集群.

Redis Sentinal(主从复制集群)着眼于高可用,在master宕机时会自动将slave提升为master,继续提供服务。

Redis Cluster着眼于扩展性,在单个redis内存不足时,使用Cluster进行分片存储。

16、Redis 集群方案什么情况下会导致整个集群不可用?

  1. 集群主库半数宕机(根据 failover 原理,fail 掉一个主需要一半上主都投票通过才可以)。
  2. 集群中某一节点的主从全数宕机。


17、Redis 支持的 Java 客户端都有哪些?官方推荐用哪个?

Redisson、Jedis、lettuce等。

名称介绍优点缺点
Jedis是Redis的Java实现的客户端,其API提供了比较全面的Redis命令的支持。调用是比较底层的API,操作与原生操作基本一致。提供了比较全面的数据类型及Redis操作。使用阻塞的I/O,且其方法调用都是同步的,程序流需要等到sockets处理完I/O才能执行。单线程不是线程安全的。
Lettuce高级Redis客户端,用于线程安全同步,异步和响应使用,支持集群,Sentinel,管道和编码器。多线程,线程安全的,性能更好。缺乏分布式高级特性。
RedissonRedisson实现了分布式和可扩展的Java数据结构,提供很多分布式相关操作服务,例如,分布式锁,分布式集合,可通过Redis支持延迟队列。和Jedis相比,功能较为简单,不支持字符串操作,不支持排序、事务、管道、分区等Redis特性。Redisson的宗旨是促进使用者对Redis的关注分离,从而让使用者能够将精力更集中地放在处理业务逻辑上。功能较简单,致力于分布式相关特性。数据类型支持不足,消耗性能,若非必需要使用分布式特性建议使用lettuce。


18、Jedis 与 Redisson 对比有什么优缺点?

参照17题

19、Redis 如何设置密码及验证密码?

设置密码有两种方式,一是在redis.conf配置文件中设置# requirepass foobared;另一种是用指令config set requirepass psw设置。

验证密码即进入Redis之前需要使用指令auth psw进行验证之后才能进行操作。

20、说说 Redis 哈希槽的概念?

Redis 集群没有使用一致性hash, 而是引入了哈希槽的概念。

Redis Cluster 采用虚拟哈希槽分区,所有的键根据哈希函数映射到 0 ~ 16383 整数槽内,每个key通过CRC16校验后对16384取模来决定放置哪个槽(Slot),每一个节点负责维护一部分槽以及槽所映射的键值数据。

计算公式:slot = CRC16(key) & 16383。

这种结构很容易添加或者删除节点,并且无论是添加删除或者修改某一个节点,都不会造成集群不可用的状态。使用哈希槽的好处就在于可以方便的添加或移除节点。

  1. 当需要增加节点时,只需要把其他节点的某些哈希槽挪到新节点就可以了;
  2. 当需要移除节点时,只需要把移除节点上的哈希槽挪到其他节点就行了。

Redis Cluster

用了哈希槽的概念,而没有用一致性哈希算法,不都是哈希么?这样做的原因是为什么呢?

Redis Cluster是自己做的crc16的简单hash算法,没有用一致性hash。Redis的作者认为它的crc16(key) mod 16384的效果已经不错了,虽然没有一致性hash灵活,但实现很简单,节点增删时处理起来也很方便。


21、为什么RedisCluster会设计成16384个槽呢?


  1. 如果槽位为65536,发送心跳信息的消息头达8k,发送的心跳包过于庞大。

如上所述,在消息头中,最占空间的是 myslots[CLUSTER_SLOTS/8]。 当槽位为65536时,这块的大小是: 65536÷8÷1024=8kb因为每秒钟,redis节点需要发送一定数量的ping消息作为心跳包,如果槽位为65536,这个ping消息的消息头太大了,浪费带宽。

  1. redis的集群主节点数量基本不可能超过1000个。

集群节点越多,心跳包的消息体内携带的数据越多。如果节点过1000个,也会导致网络拥堵。因此redis作者,不建议redis cluster节点数量超过1000个。 那么,对于节点数在1000以内的redis cluster集群,16384个槽位够用了。

  1. 槽位越小,节点少的情况下,压缩率高.

Redis主节点的配置信息中,它所负责的哈希槽是通过一张bitmap的形式来保存的,在传输过程中,会对bitmap进行压缩,但是如果bitmap的填充率slots / N很高的话(N表示节点数),bitmap的压缩率就很低。
例如8个节点,16384÷8÷1024=2kb。

综上所述,作者决定取16384个槽,不多不少,刚刚好!


22、缓存穿透、击穿、雪崩是什么?如何解决。

现象名称情况特点解决方式
缓存穿透巨大并发访问某一个缓存和数据库中都没有的数据,导致数据库服务器压力超负荷或宕机空数据点穿透(1)布隆过滤器;(2)缓存空数据
缓存击穿巨大并发访问某一个缓存刚到期的数据,导致数据库服务器压力超负荷或宕机缓存到期点穿透(1)设置热点数据永不过期;(2)加互斥锁
缓存雪崩巨大并发访问多个缓存刚到期的数据,导致数据库服务器压力超负荷或宕机缓存到期面穿透(1)缓存高可用,增设几台备用集群;(2)限流降级,使用队列或者加锁控制数量;(3)数据预热
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值