redis面试题总结

本文详细梳理了Redis面试中常见的问题,包括Redis的基本概念、使用场景、为何快速、数据类型与结构、持久化策略(RDB与AOF)、过期键策略、内存优化、事务机制、主从复制的原理与心跳检测、哨兵系统的功能和选举机制,以及Redis在分布式锁和一致性方面的应用。内容涵盖了Redis的核心知识点,适合面试复习。
摘要由CSDN通过智能技术生成

redis面试题总结

常规问题

什么是redis,为什么要使用它

什么是redis?
Redis是Remote Dictionary Server(远程数据服务)的简称,Redis的的是完全开源免费的,遵守BSD协议,是一个高性能的键值存储系统。是当前最热门的NoSql数据库之一,也被人们称为数据结构服务器,常用于缓存、订阅发布、高数队列等场景,是基于内存的,可持久化

为什么要使用它?
因为它基于内存,读写性能优秀(官方验证:写操作11 万次万次/秒;读操作8.1万次/秒);可使用RDB与AOF实现持久化;数据类型丰富(基本类型String、List、Hash、Set、ZSet;特殊类型:bitmap、hyperloglog、geo);操作原子性(支持多个操作全并后原子操作);丰富特性(发布订阅、通知等);分布式(Redis cluster);发布订阅

redis一般有哪些使用场景

  • 缓存

String类型
eg:热点数据缓存(例如报表、棒子承认祖先是种花家),对象缓存、全页缓存

  • 数据共享分布式

String 类型
eg:分布式session

  • 分布式锁

String 类型setnx方法

  • 全局ID

String 类型(int类型,incrby命令,利用原子性)
eg:incrby tableid 1000 分库分表的场景,一次性拿一段

  • 计数器

String 类型(int类型,incr命令)
eg:文章的阅读量、微博点赞数、允许一定的延迟,先写入Redis再定时同步到数据库

  • 限流

String 类型(int类型,incr命令)
eg:使用者ip和其他消息为key,没访问一次加一,达到次数返回false

  • 购物车

String 或hash类型。所有String可以做的hash都可以做
eg:key:用户id;field:商品id和简介;value:商品数量

  • 用户消息时间线timeline

list类型,双向链表,直接作为timeline就好了。插入有序

  • 消息队列

list类型
eg:lpush命令+brpop命令=Message Queue(消息队列)

  • 抽奖

Set类型
eg:Redis SPOP 命令用于从集合 key 中删除并返回一个或多个随机元素

  • 点赞、签到、打卡

Set类型

  • 商品标签

set类型

  • 商品筛选

set类型

  • 用户关注、推荐模型

set类型
follow 关注 fans 粉丝
相互关注:

  • sadd 1:follow 2
  • sadd 2:fans 1
  • sadd 1:fans 2
  • sadd 2:follow 1

我关注的人也关注了他(取交集):

  • sinter 1:follow 2:fans

可能认识的人:

  • 用户1可能认识的人(差集):sdiff 2:follow 1:follow
  • 用户2可能认识的人:sdiff 1:follow 2:follow
  • 排行榜

zset类型
id 为6001 的新闻点击数加1:zincrby hotNews:20220405 1 news6001
获取今天点击最多的15条:zrevrange hotNews:20220405 0 15 withscores

redis为什么快

Redis完全基于内存,大部分都是简单的存取操作,大量的时间花费在IO上
Redis因自身极其优越的性能和读取速度而被广泛使用
Redis绝大部分操作时间复杂度为O (1),所以速度十分快

数据类型和数据结构

redis有哪些数据类型

五种基本类型:

  • String
    – 数字,int类型
    – 字符长度小于39位,embstr
    – 字符长度大于39位,raw
    – embstr和raw都是由SDS动态字符串构成,embstr分配内存时,redisobject与sds划分在同一块内存;raw分配内存时,redisobject与sds各划分一块内存
  • List
    – ziplist:链表所有字符串长度小于64个字节,元素数量小于512个
    – 双向链表:ziplist之外其它情况
  • Hash
    – ziplist:链表所有字符串长度小于64个字节,元素数量小于512个
    – hash table:ziplist之外其它情况
  • Set
    – inset:整数,元素数量小于512个
    – hash table:inset之外其它情况
  • ZSet
    – ziplist:元素长度小于64个字节,元素数量小于128个
    – 跳表:ziplist之外其它情况

三种特殊类型

  • bitmap
    – 即位图数据结构,都是操作二进制位来进行记录,只有0 和 1 两个状态
  • hyperloglog
    – 这个结构可以非常省内存的去统计各种计数,比如注册 IP 数、每日访问 IP 数、页面实时UV、在线用户数,共同好友数等
  • geo
    – 这个功能可以推算地理位置的信息: 两地之间的距离, 方圆几里的人

谈谈redis的对象机制(redisObject)

redis数据类型有哪些底层数据结构

简单字符串(SDS)、双端链表、字典、压缩列表(zip list)、跳跃表、整数集合

为什么要设计sds?

获取字符串长度的复杂度降低
避免内存溢出
空间预分配
惰性删除

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

512M

为什么会设计Stream Stream用在什么样场景

消息ID的设计是否考虑了时间回拨的问题

持久化和内存

Redis 的持久化机制是什么?各自的优缺点?一般怎么用?

RDB快照

  • 优点
    – RDB文件小,非常适合定时备份,用于灾难恢复
    – Redis加载RDB文件的速度比AOF快很多,因为RDB文件中直接存储的时内存数据,而AOF文件中存储的是一条条命令,需要重演命令
  • 缺点
    – RDB无法做到实时持久化,若在两次bgsave间宕机,则会丢失区间(分钟级)的增量数据,不适用于实时性要求较高的场景
    – RDB的cow机制中,fork子进程属于重量级操作,并且会阻塞redis主进程
    – 存在老版本的Redis不兼容新版本RDB格式文件的问题
  • 操作
    – 手动触发:save与bgsave命令
    – 自动触发:
    ---- 1. redis.conf中配置save m n,即在m秒内有n次修改时,自动触发bgsave生成rdb文件
    ---- 2. 主从复制时,从节点要从主节点进行全量复制时也会触发bgsave操作,生成当时的快照发送到从节点
    ---- 3. 执行debug reload命令重新加载redis时也会触发bgsave操作
    ---- 4. 默认情况下执行shutdown命令时,如果没有开启aof持久化,那么也会触发bgsave操作

AOF日志

  • 优点
    – AOF只是追加写日志文件,对服务器性能影响较小,速度比RDB要快,消耗的内存较少
  • 缺点
    – AOF方式生成的日志文件太大,需要不断AOF重写,进行瘦身。即使经过AOF重写瘦身, 由于文件是文本文件,文件体积较大(相比于RDB的二进制文件)。
    – AOF重演命令式的恢复数据,速度显然比RDB要慢
  • 操作
    – 手动触发:bgrewriteaof命令
    – 自动触发:根据auto-aof-rewrite-min-size和auto-aof-rewrite-percentage参数确定自动触发时机

Redis 过期键的删除策略有哪些

  • 定时删除:在设置键的过期时间的同时,创建一个定时器,达到过期时间,执行键的删除操作
  • 惰性删除:不主动删除过期键,从键空间中获取键时,都检查取得的键是否过期,过期则删除;没过期则返回
  • 定期删除:每隔一段时间对数据库进行一次检查,删除里面的过期键。删除多少过期键、检查多少个数据库,由算法决定

Redis 内存淘汰算法有哪些

LRU 淘汰算法和 LFU 淘汰算法

Redis的内存用完了会发生什么?

如果达到设置的上限,Redis的写命令会返回错误信息(但是读命令还可以正常返回。)或者你可以配置内存淘汰机制,当Redis达到内存上限时会冲刷掉旧的内容

Redis如何做内存优化?

Redis key 的过期时间和永久有效分别怎么设置?

EXPIRE 和 PERSIST 命令

Redis 中的管道有什么用?

一次请求/响应服务器能实现处理新的请求即使旧的请求还未被响应,这样就可以将多个命令发送到服务器,而不用等待回复,最后在一个步骤中读取该答复

事务

什么是redis事务

redis事务就是一次性、顺序性、排他性的执行一个队列中的一系列命令
Redis 事务的本质是一组命令的集合。事务支持一次执行多个命令,一个事务中所有命令都会被序列化。在事务执行过程,会按照顺序串行化执行队列中的命令,其他客户端提交的命令请求不会插入到事务执行命令序列中

Redis事务相关命令

  • MULTI :开启事务,redis会将后续的命令逐个放入队列中,然后使用EXEC命令来原子化执行这个命令系列
  • EXEC:执行事务中的所有操作命令
  • DISCARD:取消事务,放弃执行事务块中的所有命令
  • WATCH:监视一个或多个key,如果事务在执行前,这个key(或多个key)被其他命令修改,则事务被中断,不会执行事务中的任何命令
  • UNWATCH:取消WATCH对所有key的监视

Redis事务的三个阶段

开始事务. 命令入队. 执行事务

watch是如何监视实现的呢

Redis 事务 watch命令

这里redis CAS(Click And Set)参考多线程CAS(Compare And Swap),watch监控键值对,执行这个事务的线程将共享资源保存副本到当前进程,在exec命令时,比较副本中旧值与共享资源是否相同,不同就回滚

为什么 Redis 不支持回滚

在Redis中,命令只会因为错误的语法而失败,或者是命令用在了错误类型的键上面;
也就是说,从实用的角度说,失败的命令是由编译错误造成的,而这些错误应该在开发过程中被发现,而不应该出现在生产环境中。
因为不需要对回滚进行支持,所以Redis的内部可以保持简单且快速
使用watch命令开启回滚

redis 对 ACID的支持性理解

  • 原子性atomicity
    – 运行期不允许回滚,redis官方认为:Redis的事务是原子性的:所有的命令,要么全部执行,要么全部不执行。而不是完全成功
  • 一致性consistency
    – 无论是在命令执行错误或 Redis 发生故障的情况下,Redis 事务机制对一致性属性都是有保证的
  • 隔离性Isolation
    – redis单线程,严格遵守隔离性就,没有关系型数据库的隔离级别,也不会出现幻读等
  • 持久化Durability
    – redis数据库是基于内存的,使用RDB与AOF持久化策略,事务持久化都得不到保障
    – redis使用RDB,在下次生成RDB文件前,编写事务命令,发送宕机,这些命令丢失
    – redis使用AOF,即是使用写回策略always、everysec、no,也都会由命令丢失

Redis事务其他实现

  • 基于Lua脚本,Redis可以保证脚本内的命令一次性、按顺序地执行,其同时也不提供事务运行错误的回滚,执行过程中如果部分命令运行错误,剩下的命令还是会继续运行完
  • 基于中间标记变量,通过另外的标记变量来标识事务是否执行完成,读取数据时先读取该标记变量判断是否事务执行完成。但这样会需要额外写代码实现,比较繁琐

主从复制

主从复制作用?

  • 数据冗余:对数据进行热备份,是数据持久化外一种数据冗余方式
  • 故障转移:主节点宕机,从节点变为主节点提供服务
  • 负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写Redis数据时应用连接主节点,读Redis数据时应用连接从节点),分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量
  • 高可用基石:除了上述作用以外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制是Redis高可用的基础

心跳检测作用?

在命令传播阶段,从服务器会以每秒一次的频率,向主服务器发送命令:REPLCONF ACK <replication_offset>,其中replication_offset是从服务器的复制偏移量。心跳检测主要有三个作用:

  • 检测主服务器的网络连接状态
    – 如果超过一秒钟没有收到从服务器发来的REPLCONF ACK命令,那么主服务器就知道主从服务器之间连接出现了问题。通过向主服务器发送INFO replconf命令,可以观测出最后一次向主服务器发送REPLCONF ACK命令过了多少秒
  • 辅助实现min-slaves选项
    – Redis的 min-slaves-to-write 和 min-slaves-max-lag 两个选项可以防止主服务器在不安全的情况下执行写命令
  • 检测命令丢失
    – 如果因为网络故障,导致主服务器给从服务器传播的命令半路丢失时,那么当从服务器向主服务器发送 REPLCONF ACK 命令时,主服务器将发觉从服务器当前的复制偏移量少于自己的复制偏移量,然后主服务器根据从服务器提交的复制偏移量,在复制积压缓冲区里面找到从服务器缺少的数据,并将这些数据重新发送给从服务器

全量复制的三个阶段?

  • 主从库间建立连接、协商同步的过程
    – 从库第一次发送psync(runid=?, offset=-1)命令给主库,主库采用代表第一次复制为全量复制的FULLRESYNC(runid=主库实例id,offset=主库复制进度)响应命令给从库
  • 主库将所有数据同步给从库
    – 主库使用bgsave命令,fork子线程生成此刻内存数据的内存快照通过网络发送给从库,并将此后主进程接受的写操作写入replication buffer,从库调用replicaof命令读取rdb文件,同步数据
  • 主库会把第二阶段执行过程中新收到的写命令,再发送给从库
    – 当主库完成 RDB 文件发送后,就会把此时 replication buffer 中的修改操作发给从库,从库再重新执行这些操作

为什么会设计增量复制?

如果主从库在命令传播时出现了网络闪断,那么,从库就会和主库重新进行一次全量复制,开销非常大。从 Redis 2.8 开始,网络断了之后,主从库会采用增量复制的方式继续同步

增量复制的流程? 如果在网络断开期间,repl_backlog_size环形缓冲区写满之后,从库是会丢失掉那部分被覆盖掉的数据,还是直接进行全量复制呢?

  • 从库断开之后,主库写操作会存入主从差异数据而设计的环形缓冲区repl_backlog_buffer,从而避免全量复制带来的性能开销。
  • 主从库间建立连接、协商同步,从库发送psync(runid=主库实例id,offset=复制进度)命令给主库,主库将offet做比较,从环形缓冲区repl_backing_buffer取出从库缺少的数据,交由replication buffer 发送给从库
  • 一个从库如果和主库断连时间过长,造成它在主库repl_backlog_buffer的slave_repl_offset位置上的数据已经被覆盖掉了,此时从库和主库间将进行全量复制
  • 每个从库会记录自己的slave_repl_offset,每个从库的复制进度也不一定相同。在和主库重连进行恢复时,从库会通过psync命令把自己记录的slave_repl_offset发给主库,主库会根据从库各自的复制进度,来决定这个从库可以进行增量复制,还是全量复制

为什么不持久化的主服务器自动重启非常危险呢?

主服务器不开启持久化,当宕机后,redis会自动重启,造成主服务器数据丢失,成为空集合,这时从库发送请求复制主库数据,导致从库数据都清空,即是配置Redis Sentinel,也会因为重启时间短,无法检测到这次失败,那么上面说的这种失败的情况就发生了,造成数据隔空消失
在重要数据,不能使用持久化的情况下,关闭redis自动重启功能

为什么主从全量复制使用RDB而不使用AOF?

  • RDB只会在需要定时备份和主从全量复制数据时才会触发生成一次快照;AOF做全量复制,需要开启AOF功能,选择刷盘策略,如果选择不当会导致redis性能下降,在数据丢失不敏感的场景下,不需要开启AOF功能
  • RDB是二进制内存快照文件(文件体积远小于内存数据体积),对主库网络带宽占用少,redis根据RDB协议解析读取RDB文件便可恢复数据;AOF是日志文件,记录每次写操作,写操作频繁会导致文件体积大,恢复数据需要重演写操作,即是重写瘦身,文件体积仍然大

为什么还有无磁盘复制模式?

Redis 默认是磁盘复制,但是如果使用比较低速的磁盘,这种操作会给主服务器带来较大的压力。Redis从2.8.18版本开始尝试支持无磁盘的复制。使用这种设置时,子进程直接将RDB通过网络发送给从服务器,不使用磁盘作为中间存储

为什么还会有从库的从库的设计?

主库在全量复制中,耗时最多的是生成 RDB 文件和传输 RDB 文件,当从库数量大,且同时向主库同步数据时,会使主库fork大量子线程生成 RDB 文件,对主库主进程造成阻塞,传输 RDB 文件时,会占用主库大量的网络带宽
使用主-从-从模式,可以通过级联模式分散主库大量生成 RDB 文件和传输 RDB 文件的压力
主库给第一级从库全量复制,然后第一级从库给后一级从库实现全量复制。。。

哨兵机制

Redis哨兵机制是什么?哨兵实现了什么功能呢

Redis sentinel,即Redis哨兵机制,哨兵的核心功能是主节点的自动故障转移

  • 监控:哨兵会不断地检查主节点和从节点是否运作正常
  • 自动故障转移:当主节点不能正常工作时,哨兵会开始自动故障转移操作,它会将失效主节点的其中一个从节点升级为新的主节点,并让其他从节点改为复制新的主节点
  • 配置提供者:客户端在初始化时,通过连接哨兵来获得当前Redis服务的主节点地址
  • 通知:哨兵可以将故障转移的结果发送给客户端

哨兵集群是通过什么方式组建的?

redis的发布订阅机制
哨兵1将本身ip与端口发布到主库的_sentiniel_:hello频道上,其他哨兵通过订阅这个频道,获取哨兵1网络位置,与其建立网络连接

哨兵是如何监控Redis集群的?

哨兵发送INFO命令给主库获取从库列表,哨兵通过信息连接从库列表所有从库

哨兵如何判断主库已经下线了呢?

主观判定:任何一个哨兵都是可以监控探测,并作出Redis节点下线的判断
客观判定:由哨兵集群共同决定Redis节点是否下线
当一个哨兵主观判定主库为下线时,发送is-master-down-by-addr命令给其它哨兵去主观判定,当赞成票数是大于等于哨兵配置文件中的 quorum 配置项, 则可以判定主库客观下线了

哨兵的选举机制是什么样的?

Raft选举算法: 选举的票数大于等于num(sentinels)/2+1时,将成为领导者,如果没有超过,继续选举

Redis 1主4从,5个哨兵,哨兵配置quorum为2,如果3个哨兵故障,当主库宕机时,哨兵能否判断主库“客观下线”?能否自动切换?

主库宕机,正常的两个哨兵主观判定为赞成,票数等于quorun=2,判断主库“客观下线”
但哨兵不能完成主从切换,哨兵选举票数最多为2 < (5)/2 + 1 = 3,不能完成选举

主库判定客观下线了,那么如何从剩余的从库中选择一个新的主库呢?

  • 过滤掉不健康的(下线或断线),没有回复过哨兵ping响应的从节点
  • 选择salve-priority从节点优先级最高(redis.conf)的
  • 选择复制偏移量最大,指复制最完整的从节点

新的主库选择出来后,如何进行故障的转移?

  • 将选举出来的从节点脱离原主节点,replicaof no one,升级为主节点
  • 将其他从节点连接新主节点
  • 通知客户端故障转移结果
  • 将原主节点变为从节点连接新主节点

应用场景

redis 客户端有哪些

Redisson、Jedis、lettuce等等,官方推荐使用Redisson。 Redisson是一个高级的分布式协调Redis客服端,能帮助用户在分布式环境中轻松实现一些Java的对象 (Bloom filter, BitSet, Set, SetMultimap, ScoredSortedSet, SortedSet, Map, ConcurrentMap, List, ListMultimap, Queue, BlockingQueue, Deque, BlockingDeque, Semaphore, Lock, ReadWriteLock, AtomicLong, CountDownLatch, Publish / Subscribe, HyperLogLog)。

Redis如何做大量数据插入?

Redis2.6开始redis-cli支持一种新的被称之为pipe mode的新模式用于执行大量数据插入工作

redis实现分布式锁? 什么是 RedLock?

RedLock详解

基于 Redis 实现分布式锁的方式名叫 Redlock,此种方式比原先的单节点的方法更安全。它可以保证以下特性:

  • 安全特性:互斥访问,即永远只有一个 client 能拿到锁
  • 避免死锁:最终 client 都可能拿到锁,不会出现死锁的情况,即使原本锁住某资源的 client crash 了或者出现了网络分区
  • 容错性:只要大部分 Redis 节点存活就可以正常提供服务

redis缓存有哪些问题,如何解决?

  • 缓存击穿:缓存中没有但数据库中有的数据(一般是缓存时间到期),大量请求访问mysql
    – 设置热点数据永远不过期
    – 接口限流与熔断,降级。重要的接口一定要做好限流策略,防止用户恶意刷接口,同时要降级准备,当接口中的某些 服务 不可用时候,进行熔断,失败快速返回机制
    – 加互斥锁
  • 缓存穿透:mysql与缓存中都没有,利用不存在的key频繁攻击应用,挂掉DB
    – 接口层增加校验
    – key在mysql和缓存中都没有,将key-null加入缓存,并设置缓存时间
    – 布隆过滤器
  • 缓存雪崩:数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至down机
    – 缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生
    – 如果缓存数据库是分布式部署,将热点数据均匀分布在不同的缓存数据库中
    – 设置热点数据永远不过期
  • 缓存污染:一些不常用的数据在访问后未被清理,留在缓存中,占用内存,缓存空间是有限的,如果缓存空间满了,再往缓存里写数据时就会有额外开销(选择淘汰策略,删除缓存),影响Redis性能
    – 缓存淘汰策略:
    —— 不淘汰
    noeviction (v4.0后默认的)
    —— 对设置了过期时间的数据中进行淘汰
    随机:volatile-random
    ttl:volatile-ttl
    lru:volatile-lru
    lfu:volatile-lfu
    —— 全部数据进行淘汰
    随机:allkeys-random
    lru:allkeys-lru
    lfu:allkeys-lfu
  • 一致性:如果删除了缓存Redis,还没有来得及写库MySQL,另一个线程就来读取,发现缓存为空,则去数据库中读取数据写入缓存,此时缓存中为脏数据;如果先写了库,在删除缓存前,写库的线程宕机了,没有删除掉缓存,则也会出现数据不一致情况

redis和其它数据库一致性问题如何解决?

更新缓存的的Design Pattern有四种:Cache aside, Read through, Write through, Write behind caching

redis性能问题有哪些,如何分析定位解决?

Redis集群

说说Redis哈希槽的概念?为什么是16384个?

Redis集群没有使用一致性hash,而是引入了哈希槽的概念,Redis集群有16384个哈希槽,每个key通过CRC16校验后对16384取模来决定放置哪个槽,集群的每个节点负责一部分hash槽
2^14次方

Redis集群会有写操作丢失吗?为什么?

Redis并不能保证数据的强一致性,这意味这在实际中集群在特定的条件下可能会丢失写操作

关于Java和Redis面试题,你可以参考以下资源: 1. "Java基础教程(入门篇)"这本书中可能包含与Java和Redis相关的基础知识点,例如如何连接和操作Redis以及在Java中使用Redis的常见场景。 2. "java面试大集合"这本书中可能包含Java和Redis面试题,涵盖了Java技术栈以及与Redis相关的问题。你可以浏览这本书中的相关章节以寻找你感兴趣的Java和Redis面试题。 3. "Java基础教程(进阶篇)"这本书可能包含更深入的Java和Redis面试题,例如Java高并发和如何在Java中使用Redis进行缓存。 这些资源可能会给你提供一些有关Java和Redis面试题的参考。你可以根据自己的需求和兴趣选择适合的资源进行学习。希望这些资源能帮助到你。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [redis面试题总结(附答案)](https://blog.csdn.net/guorui_java/article/details/117194603)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [java面试大集合一共485页](https://download.csdn.net/download/wm9028/88268176)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蓝桉未与

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值