一文看懂Redis

什么是Redis

    Redis是由意大利人Salvatore Sanfilippo(网名:antirez)开发的一款内存高速缓存数据库。Redis全称为:Remote Dictionary Server(远程数据服务),该软件使用C语言编写,Redis是一个key-value存储系统,它支持丰富的数据类型,如:string、list、set、zset(sorted set)、hash。

 

Redis特点

    Redis以内存作为数据存储介质,所以读写数据的效率极高,远远超过数据库。以设置和获取一个256字节字符串为例,它的读取速度可高达110000次/s,写速度高达81000次/s。
    Redis跟memcache不同的是,储存在Redis中的数据是持久化的,断电或重启后,数据也不会丢失。因为Redis的存储分为内存存储、磁盘存储和log文件三部分,重启后,Redis可以从磁盘重新将数据加载到内存中,这些可以通过配置文件对其进行配置,正因为这样,Redis才能实现持久化。

    Redis支持主从模式,可以配置集群,这样更利于支撑起大型的项目,这也是Redis的一大亮点。

 

Redis数据类型、及常用场景

一、字符串
字符串类型是redis最基础的数据结构,首先键是字符串类型,而且其他几种结构都是在字符串类型基础上构建的,所以字符串类型能为其他四种数据结构的学习尊定基础。字符串类型实际上可以是字符串
(简单的字符串、复杂的字符串(xml、json)、数字(整数、浮点数)、二进制(图片、音频、视频)),但最大不能超过512M。

使用场景:

  •     缓存功能:字符串最经典的使用场景,redis最为缓存层,Mysql作为储存层,绝大部分请求数据都是redis中获取,由于redis具有支撑高并发特性,所以缓存通常能起到加速读写和降低 后端压力的作用。
  •     计数器:许多运用都会使用redis作为计数的基础工具,他可以实现快速计数、查询缓存的功能,同时数据可以一步落地到其他的数据源。  Redis是一个很好的计数器,这要感谢INCRBY和其他相似命令。我相信你曾许多次想要给数据库加上新的计数器,用来获取统计或显示新信息,但是最后却由于写入敏感而不得不放弃它们。好了,现在使用Redis就不需要再担心了。有了原子递增(atomic increment),你可以放心的加上各种计数,用GETSET重置,或者是让它们过期。     

        例如这样操作:
        INCR user:<id> EXPIRE
        user:<id> 60

        你可以计算出最近用户在页面间停顿不超过60秒的页面浏览量,当计数达到比如20时,就可以显示出某些条幅提示,或是其它你想显示的东西。如:视频播放数系统就是使用redis作为视频播放数计数的基础组件。

  •     共享session:出于负载均衡的考虑,分布式服务会将用户信息的访问均衡到不同服务器上,用户刷新一次访问可能会需要重新登录,为避免这个问题可以用redis将用户session集中管理,在这种模式下只要保证redis的高可用和扩展性的,每次获取用户更新或查询登录信息都直接从redis中集中获取。
  •     限速:处于安全考虑,每次进行登录时让用户输入手机验证码,为了短信接口不被频繁访问,会限制用户每分钟获取验证码的频率。

二、哈希
在redis中哈希类型是指键本身又是一种键值对结构,如 value={{field1,value1},......{fieldN,valueN}}  

Redis Hash 实现原理: https://www.cnblogs.com/ourroad/p/4891648.html

常用hash算法原理: https://blog.csdn.net/tingting256/article/details/52475422


使用场景:
      哈希结构相对于字符串序列化缓存信息更加直观,并且在更新操作上更加便捷。所以常常用于**用户信息**等管理,但是哈希类型和关系型数据库有所不同,哈希类型是稀疏的,而关系型数据库是完全结构化的,关系型数据库可以做复杂的关系查询,而redis去模拟关系型复杂查询,开发困难,维护成本高。

三、列表
列表类型是用来储存多个有序的字符串,列表中的每个字符串成为元素(element),一个列表最多可以储存2的32次方-1个元素,在redis中,可以队列表两端插入(pubsh)和弹出(pop),还可以获取指定范围的元素列表、获取指定索引下表的元素等,列表是一种比较灵活的数据结构,它可以充当栈和队列的角色,在实际开发中有很多应用场景。
优点:
    1.列表的元素是有序的,这就意味着可以通过索引下标获取某个或某个范围内的元素列表。
    2.列表内的元素是可以重复的。

使用场景:
消息队列: redis的lpush+brpop命令组合即可实现阻塞队列,生产者客户端是用lupsh从列表左侧插入元素,多个消费者客户端使用brpop命令阻塞时的“抢”列表尾部的元素,多个客户端保证了消费的负载均衡和高可用性

四、集合
    集合类型也是用来保存多个字符串的元素,但和列表不同的是集合中不允许有重复的元素,并且集合中的元素是无序的,不能通过索引下标获取元素,redis除了支持集合内的增删改查,同时还支持多个集合取交集、并集、差集,并合理的使用好集合类型,能在实际开发中解决很多实际问题。

使用场景:

 标签(tag):集合类型比较典型的使用场景,如一个用户对娱乐、体育比较感兴趣,另一个可能对新闻感兴趣,这些兴趣就是标签,有了这些数据就可以得到同一标签的人,以及用户的共同爱好的标签,这些数据对于用户体验以及曾强用户粘度比较重要。(用户和标签的关系维护应该放在一个事物内执行,防止部分命令失败造成数据不一致)

    sadd=tagging(标签)
    spop/srandmember=random item(生成随机数,比如抽奖)

    sadd+sinter=social Graph(社交需求)

五、有序集合
有序集合和集合有着必然的联系,他保留了集合不能有重复成员的特性,但不同得是,有序集合中的元素是可以
排序的,但是它和列表的使用索引下标作为排序依据不同的是,它给每个元素设置一个分数,作为排序的依据。
(有序集合中的元素不可以重复,但是csore可以重复,就和一个班里的同学学号不能重复,但考试成绩可以相
同)。

列表、集合、有序集合三者的异同点

使用场景:

    排行榜:有序集合经典使用场景。例如视频网站需要对用户上传的视频做排行榜,榜单维护可能是多方面:按照时间、按照播放量、按照获得的赞数等。

 

例如:很普遍的需求是各种数据库的数据并非存储在内存中,因此在按得分排序以及实时更新这些几乎每秒钟都需要更新的功能上数据库的性能不够理想。
典型的比如那些在线游戏的排行榜,比如一个Facebook的游戏,根据得分你通常想要: 
- 列出前100名高分选手
- 列出某用户当前的全球排名
这些操作对于Redis来说小菜一碟,即使你有几百万个用户,每分钟都会有几百万个新的得分。
模式是这样的,每次获得新得分时,我们用这样的代码: ZADD leaderboard  <score>  <username>  你可能用userID来取代username,这取决于你是怎么设计的。      
得到前100名高分用户很简单:ZREVRANGE leaderboard 0 99。

用户的全球排名也相似,只需要:ZRANK leaderboard <username>。

排行榜的一种常见变体模式
就像Reddit或Hacker News用的那样,新闻按照类似下面的公式根据得分来排序:score = points / time^alpha
因此用户的投票会相应的把新闻挖出来,但时间会按照一定的指数将新闻埋下去。下面是我们的模式,当然算法由你决定。
模式是这样的,开始时先观察那些可能是最新的项目,例如首页上的1000条新闻都是候选者,因此我们先忽视掉其他的,这实现起来很简单。
每次新的新闻贴上来后,我们将ID添加到列表中,使用LPUSH + LTRIM,确保只取出最新的1000条项目。      

有一项后台任务获取这个列表,并且持续的计算这1000条新闻中每条新闻的最终得分。计算结果由ZADD命令按照新的顺序填充生成列表,老新闻则被清除。这里的关键思路是排序工作是由后台任务来完成的。

排序算法原理(跳跃表): https://blog.csdn.net/lz710117239/article/details/78408919

常用使用方法: https://www.cnblogs.com/huangxincheng/p/4979789.html

六、redis 其他功能使用场景
    1. 订阅-发布系统

    Pub/Sub 从字面上理解就是发布(Publish)与订阅(Subscribe),在 Redis 中,你可以设定对某一个 key 值进行消息发布及消息订阅,当一个 key 值上进行了消息发布后,所有订阅它的客户端都会收到相应的消息。这一功能最明显的用法就是用作实时消息系统,比如普通的即时聊天,群聊等功能。
    2. 事务——Transactions

    谁说 NoSQL 都不支持事务,虽然 Redis 的 Transactions 提供的并不是严格的 ACID 的事务(比如一串用 EXEC 提交执行的命令,在执行中服务器宕机,那么会有一部分命令执行了,剩下的没执行),但是这个 Transactions 还是提供了基本的命令打包执行的功能(在服务器不出问题的情况下,可以保证一连串的命令是顺序在一起执行的,中间有会有其它客户端命令插进来执行)。Redis 还提供了一个 Watch 功能,你可以对一个 key 进行 Watch,然后再执行 Transactions,在这过程中,如果这个 Watched 的值进行了修改,那么这个 Transactions 会发现并拒绝执行。

七、更多:https://www.zhihu.com/question/19829601

 

Redis主从复制

前言:
和MySQL主从复制的原因一样,Redis虽然读取写入的速度都特别快,但是也会产生读压力特别大的情况。为了分担读压力,Redis支持主从复制,Redis的主从结构可以采用一主多从或者级联结构,Redis主从复制可以根据是否是全量分为全量同步和增量同步。下图为级联结构。
 

 
1 全量同步
  Redis全量复制一般发生在Slave初始化阶段,这时Slave需要将Master上的所有数据都复制一份。具体步骤如下: 
  1)从服务器连接主服务器,发送SYNC命令; 
  2)主服务器接收到SYNC命名后,开始执行BGSAVE命令生成RDB文件并使用缓冲区记录此后执行的所有写命令; 
  3)主服务器BGSAVE执行完后,向所有从服务器发送快照文件,并在发送期间继续记录被执行的写命令; 
  4)从服务器收到快照文件后丢弃所有旧数据,载入收到的快照; 
  5)主服务器快照发送完毕后开始向从服务器发送缓冲区中的写命令; 
  6)从服务器完成对快照的载入,开始接收命令请求,并执行来自主服务器缓冲区的写命令; 
 


 

完成上面几个步骤后就完成了从服务器数据初始化的所有操作,从服务器此时可以接收来自用户的读请求。

2 增量同步
  Redis增量复制是指Slave初始化后开始正常工作时主服务器发生的写操作同步到从服务器的过程。 
增量复制的过程主要是主服务器每执行一个写命令就会向从服务器发送相同的写命令,从服务器接收并执行收到的写命令。
 
3 Redis主从同步策略
  主从刚刚连接的时候,进行全量同步;全同步结束后,进行增量同步。当然,如果有需要,slave 在任何时候都可以发起全量同步。redis 策略是,无论如何,首先会尝试进行增量同步,如不成功,要求从机进行全量同步。
 
4 注意点

    如果多个Slave断线了,需要重启的时候,因为只要Slave启动,就会发送sync请求和主机全量同步,当多个同时出现的时候,可能会导致Master IO剧增宕机。

 

持久化

rdb快照形式

快照形式是在指定的时间间隔内将内存中的数据集快照写入磁盘,实际操作过程是fork一个子进程,先将数据集写入临时文件,写入成功后,再替换之前的文件,用二进制压缩存储。

1). 一旦采用该方式,那么你的整个Redis数据库将只包含一个文件,这对于文件备份而言是非常完美的。比如,你可能打算每个小时归档一次最近24小时的数 据,同时还要每天归档一次最近30天的数据。通过这样的备份策略,一旦系统出现灾难性故障,我们可以非常容易的进行恢复。
2). 对于灾难恢复而言,RDB是非常不错的选择。因为我们可以非常轻松的将一个单独的文件压缩后再转移到其它存储介质上。
3). 性能最大化。对于Redis的服务进程而言,在开始持久化时,它唯一需要做的只是fork出子进程,之后再由子进程完成这些持久化的工作,这样就可以极大的避免服务进程执行IO操作了。

4). 相比于AOF机制,如果数据集很大,RDB的启动效率会更高。

RDB又存在哪些劣势呢?
1). 如果你想保证数据的高可用性,即最大限度的避免数据丢失,那么RDB将不是一个很好的选择。因为系统一旦在定时持久化之前出现宕机现象,此前没有来得及写入磁盘的数据都将丢失。
2). 由于RDB是通过fork子进程来协助完成数据持久化工作的,因此,如果当数据集较大时,可能会导致整个服务器停止服务几百毫秒,甚至是1秒钟。
 

aof日志形式

AOF持久化以日志的形式记录服务器所处理的每一个写、删除操作,查询操作不会记录,以文本的方式记录,可以打开文件看到详细的操作记录。

1). 该机制可以带来更高的数据安全性,即数据持久性。Redis中提供了3中同步策略,即每秒同步、每修改同步和不同步。事实上,每秒同步也是异步完成的,其 效率也是非常高的,所差的是一旦系统出现宕机现象,那么这一秒钟之内修改的数据将会丢失。而每修改同步,我们可以将其视为同步持久化,即每次发生的数据变 化都会被立即记录到磁盘中。可以预见,这种方式在效率上是最低的。至于无同步,无需多言,我想大家都能正确的理解它。
2). 由于该机制对日志文件的写入操作采用的是append模式,因此在写入过程中即使出现宕机现象,也不会破坏日志文件中已经存在的内容。然而如果我们本次操 作只是写入了一半数据就出现了系统崩溃问题,不用担心,在Redis下一次启动之前,我们可以通过redis-check-aof工具来帮助我们解决数据 一致性的问题。
3). 如果日志过大,Redis可以自动启用rewrite机制。即Redis以append模式不断的将修改数据写入到老的磁盘文件中,同时Redis还会创 建一个新的文件用于记录此期间有哪些修改命令被执行。因此在进行rewrite切换时可以更好的保证数据安全性。

4). AOF包含一个格式清晰、易于理解的日志文件用于记录所有的修改操作。事实上,我们也可以通过该文件完成数据的重建。

AOF的劣势有哪些呢?
1). 对于相同数量的数据集而言,AOF文件通常要大于RDB文件。RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。
2). 根据同步策略的不同,AOF在运行效率上往往会慢于RDB。总之,每秒同步策略的效率是比较高的,同步禁用策略的效率和RDB一样高效。
二者选择的标准,就是看系统是愿意牺牲一些性能,换取更高的缓存一致性(aof),还是愿意写操作频繁的时候,不启用备份来换取更高的性能,待手动运行save的时候,再做备份(rdb)。rdb这个就更有些 eventually consistent的意思了。
 

数据淘汰机制

Redis提供了5种数据淘汰策略:

volatile-lru:使用LRU算法进行数据淘汰(淘汰上次使用时间最早的,且使用次数最少的key),只淘汰设定了有效期的key
allkeys-lru:使用LRU算法进行数据淘汰,所有的key都可以被淘汰
volatile-random:随机淘汰数据,只淘汰设定了有效期的key
allkeys-random:随机淘汰数据,所有的key都可以被淘汰
volatile-ttl:淘汰剩余有效期最短的key

最好为Redis指定一种有效的数据淘汰策略以配合maxmemory设置,避免在内存使用满后发生写入失败的情况。

一般来说,推荐使用的策略是volatile-lru,并辨识Redis中保存的数据的重要性。对于那些重要的,绝对不能丢弃的数据(如配置类数据等),应不设置有效期,这样Redis就永远不会淘汰这些数据。对于那些相对不是那么重要的,并且能够热加载的数据(比如缓存最近登录的用户信息,当在Redis中找不到时,程序会去DB中读取),可以设置上有效期,这样在内存不够时Redis就会淘汰这部分数据。

LRU算法实现:http://blog.jobbole.com/107084/

     标准的LRU算法是基于双向链表和Hash表来进行数据使用时间的管理,在get数据的时候通过Hash获取数据指针,然后将指针移到双向链表的头部,防止他被清理,然而Redis的淘汰算法并不是一个严格的LRU算法,只是一个近似LRU算法,他是基于LRU时钟进行选样淘汰的机制,Redis会维护一个全局时钟,每个Redis数据(redisObject)对象内也有一个时钟,当内存超过Redis设置的最大内存是,会淘汰数据,这里会选取 maxmemory_samples 参数指定的样本数,默认一般为5,我司设置的是3, 然后从中淘汰最近最少使用的数据,当等于10的时候,基本跟严格LRU算法结果非常接近,但是性能损耗同样也非常大,一般我们会选取折中的方式。 这里淘汰分为主动淘汰和被动淘汰,主动淘汰是serverCron每间隔1000/hz ms会调用databasesCron方法来检测并淘汰过期的key,主动淘汰频率如果过长, 可能会导致每隔一段时间有更多的数据需要被淘汰,导致线程阻塞,应用可能会超时,被动淘汰是在用户get的时候,检查get是否过期,进行淘汰。

配置方法:

maxmemory-policy volatile-lru #默认是noeviction,即不进行数据淘汰

 

Redis与Memcached的不同

>>Memcached

Memcached的优点:
    Memcached可以利用多核优势,单实例吞吐量极高,可以达到几十万QPS(取决于key、value的字节大小以及服务器硬件性能,日常环境中QPS高峰大约在4-6w左右)。适用于最大程度扛量。             ***(所以我们查词用的是Memcahced,查词是我们请求量最大的服务)

    支持直接配置为session handle。

   memcached,相信我们搞linux后端的农民工都知道!这里简单的分析一下memcached是如何处理大量并发的连接的。memcached是个单进程程序,单进程多线程的程序(linuxer可能会会心一笑,这不就是多进程嘛)。memcached底层是用的libevent来管理事件的,下面我们就来看看这个libevent的经典应用是如何运转的。其实一开始memcached是个正宗的单进程程序,其实使用了异步技术后基本能把cpu和网卡的性能发挥到极限了(这种情况下硬是多线程反而会使程序性能下降),只不过后来随着多核cpu的普及,为了榨光cpu的性能,引入多线程也是顺势而为。

    memcached的源码结构非常简单,其中线程相关的代码基本都在Thread.c中。简单的说,memcached的众多线程就是个Master-Worker的模型,其中主线程负责接收连接,然后将连接分给各个worker线程,在各个worker线程中完成命令的接收,处理和返回结果。

Memcached的局限性:
    只支持简单的key/value数据结构,不像Redis可以支持丰富的数据类型。
    无法进行持久化,数据不能备份,只能用于缓存使用,且重启后数据全部丢失。
    无法进行数据同步,不能将MC中的数据迁移到其他MC实例中。
    Memcached内存分配采用Slab Allocation机制管理内存,value大小分布差异较大时会造成内存利用率降低,并引发低利用率时依然出现踢出等问题。需要用户注重value设计。

https://blog.csdn.net/huithe/article/details/8006186

>>Redis

Redis的优点:
    支持多种数据结构,如 string(字符串)、 list(双向链表)、dict(hash表)、set(集合)、zset(排序set)、hyperloglog(基数估算)
    支持持久化操作,可以进行aof及rdb数据持久化到磁盘,从而进行数据备份或数据恢复等操作,较好的防止数据丢失的手段。
    支持通过Replication进行数据复制,通过master-slave机制,可以实时进行数据的同步复制,支持多级复制和增量复制,master-slave机制是Redis进行HA的重要手段。
    单线程请求,所有命令串行执行,并发情况下不需要考虑数据一致性问题。
    支持pub/sub消息订阅机制,可以用来进行消息订阅与通知。

    支持简单的事务需求,但业界使用场景很少,并不成熟。

    单进程单线程好处
            代码更清晰,处理逻辑更简单
            不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗
            不存在多进程或者多线程导致的切换而消耗CPU
            单进程单线程弊端
            无法发挥多核CPU性能,不过可以通过在单机开多个Redis实例来完善;
    其他一些优秀的开源软件采用的模型
            多进程单线程模型:Nginx
            单进程多线程模型:Memcached

Redis的局限性:
    Redis只能使用单线程,性能受限于CPU性能,故单实例CPU最高才可能达到5-6wQPS每秒(取决于数据结构,数据大小以及服务器硬件性能,日常环境中QPS高峰大约在1-2w左右)。
    支持简单的事务需求,但业界使用场景很少,并不成熟,既是优点也是缺点。
    Redis在string类型上会消耗较多内存,可以使用dict(hash表)压缩存储以降低内存耗用。
    Mc和Redis都是Key-Value类型,不适合在不同数据集之间建立关系,也不适合进行查询搜索。比如redis的keys pattern这种匹配操作,对redis的性能是灾难。

内存管理方面:
   1)Memcache 使用预先分配内存池的方式,使用slab和大小不同的chunk来管理内存,Item根据内存大小来选择合适的chunk存储,内存池减少了内存的申请和释放操作,并且能减小内存碎片产生,但是会造成一定的空间浪费,所以当内存还有空间时,新的数据也可能被剔除,,原因可以参考Timyang的文章:http://timyang.net/data/Memcached-lru-evictions/。
   2)Reids 不会预先分配内存,每次新数据都会进行申请,会产生一定的内存碎片;redis根据存储命令参数,会把带过期时间的数据单独放在一起,将其称作为临时数据,非临时数据是永远不会删除的,即使内存不够导致swap也不会剔除非临时数据(会尝试剔除临时数据),这点上redis更适合做存储而不是cache。
   3)Reids和Memcache不一样,redis不会将所有数据一直存储在redis中,档内存用完时,会将部分数据swap到磁盘中,具体可以参考顶部的第三篇博客。

数据一致性方面:
    1)Memcache提供了cas命令,保证并发情况下同一数据的一致性问题。参考:http://langyu.iteye.com/blog/680052

 2)Reids提供了事务功能,保证了一串命令的原子性,中间不会被任何操作打断。参考:https://blog.csdn.net/d1562901685/article/details/54881862

网络IO模型:
   1)Memcache是多线程,非阻塞IO复用的网络模型,分为监听主线程和worker子线程,监听线程监听网络连接,接收请求后,将连接描述子pipe传递给worker子线程,进行读写IO,网络层使用libevent封装的事件库,多线程模型可以发挥多核作用,但是引入了cache coherency和锁的问题,比如,Memcached最常用的stats 命令,实际Memcached所有操作都要对这个全局变量加锁,进行计数等工作,带来了性能损耗。
         cache coherency 参考连接:http://www.infoq.com/cn/articles/cache-coherency-primer/   
         Memcached最常用的stats 参考连接:https://www.cnblogs.com/Alight/p/3546400.html
   2)Redis使用单线程的IO复用模型,封装了AeEvent事件处理框架,主要实现了epoll,kqueue和select,对于单存只有IO的操作来说,单线程可以将速度优势发挥到最大,但是Redis也提供了一些简单的计算功能,如排序和聚合等,这些操作,单线程模型实际会严重影响吞吐量,CPU计算过程中,整个IO调度都会被阻塞。

性能方面: 
       两者的性能方面都非常的出色,但是具体到细节方面,由于Redis使用单核,Memcache可以使用多核,所有平均每一个核上Redis在存储小数据时比Memcache性能更高。而在100K以上的数据,Memcache的性能要高于Reids,虽然Redis在存储大数据的性能上也做过优化,但是比起Memcache还是稍有逊色。  
       1)Memcache 单key最多存储1M的数据,超过1M会出现异常,同时value的值越大,读取性能越缓慢,同时不建议key的长度超过250,同时不能够遍历所有的Item。参考连接:https://www.cnblogs.com/shirly1981/p/5771786.html
       2)Reids 官网发布的是:redis的key和value 不允许超过512M,但是也不建议存储单key存储大量数据。关于Key的数量方面:Redis can handle up to 232 keys, and was tested in practice to handle at least 250 million of keys per instance。

          Redis的性能压测对比参考连接:https://www.zhihu.com/question/19599545

使用场景
     1)Memcache: 适合多度少些,大数据量的情况,如大量查询用户信息、好友信息。

     2)Redis适用于对读写效率要求都很高,数据处理业务复杂和对安全性比较高的系统,如新浪微博的计算和微博发布系统。

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值