道听途说redis

Posted 小时候有好奇心

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了道听途说redis相关的知识,希望对你有一定的参考价值。

应小伙伴要求,来聊聊redis。

数据类型

    redis支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。简略提一下:
1、string
日常使用比较多的一种数据类型,就是简单的k/v,但是可能有些小伙伴不知道这种类型的一种用法bitmap(二进制位图)。也就是说,可以在redis内使用bitmap进行位运算来满足一些特殊场景需求,例如用一个bitmap不同位来记录一批用户当日是否登录,一位映射一个用户,这样可以直接使用redis支持的bitmap命令来快速获得一天内这批用户的登录用户数。
2、hash
在许多业务场景需要使用的数据类型,例如用户不同维度个人信息存储到一个key中使用hashkey区分维度数据,还例如分页查询可以对同一个查询条件为key的不同页面数据做hashkey的区分,这里也不赘述了。
相对上面两种数据类型,接下来的三种的应用可能要少很多,选择哪种数据类型主要还是看业务场景。
3、list
list日常使用,主要为了实现数组、栈、队列的数据结构特性。可以结合业务需求选择需要的结构特性。
4、set
无序且不重复,一种应用场景可以拿它做发号器,例如抽奖把奖品存入set再根据规则pop出每次抽奖结果。
5、zset
带分数的有序set,如果要做一个热门排行榜可以使用它,用热门程度做分数redis自动帮你维护排序。

集群模式
这里非实战,不会搬出整体集群配置,只点出几个关键点。
1、主从
可以通过设置节点的从属关系实现主从集群,两种设定追随主节点的方式:
  • 在redis-cli客户端命令行操作,redis版本4.0之前用SLAVEOF 4.0之后用REPLICAOF
  
    
    
  
>REPLICAOF localhost 6379
   执行命令后当前节点追随localhost:6379的redis实例节点,当localhost:6379接收从追随请求时,当前节点成为slave,首次追随主时,主会自动bgsave生成一份dump.rdb落磁盘然后发给从,进而完成首次的数据同步。
  • 启动节点时加参数replicaof
  
    
    
  
->redis-server ./6381.conf --replicaof 127.0.0.1 6379
     当节点6381启动连接上主后,主节点会save一个rdb到磁盘,然后把rdb发给当前节点。当前节点会先flush删了自己所有的旧数据,然后load主给的rdb,进而完成追随后的首次主从数据同步。

      主从的集群模式下,主节点会记录所有追随它的从节点,主从间保持通信,进而从挂机时主会得知。主节点为每个从节点维护一个队列记录当前数据同步的offset从节点挂机重启后,如果要重新追随主节点,则会获得增量数据。需要注意的是,队列的大小有限制。可能会存在从节点重启时,主节点给它维护的队列已有数据被挤出队列,进而造成主从数据不同步的情况。
       简单的主从模式还需要注意的一点是,它不具备自动故障转移的功能,也就是说主宕机时,需要人为手动处理,例如在redis-cli客户端执行命令把当前从节点切换成主:
  
    
    
  
>REPLICAOF no one
2、sentinel哨兵
      针对主从模式无法自动故障转移,后来有了哨兵模式,也就是在主从模式基础上,加入特殊的角色 哨兵。哨兵的作用就是在主宕机后,做决策,把某个从节点选出来并切换成主。进而完成主宕机后的自动故障转移。
哨兵的启动需要拥有自己的配置文件告诉它监听哪些集群,某集群主是哪个节点,例如如下配置:
  
    
    
  
port 26379 sentinel moniter name1 127.0.0.1 6379 投票权重值  
上面配置了一个哨兵节点的端口是26379,监听的集群叫name1(需要对集群命名的原因实际是哨兵可以监听多个集群,需要通过名字区分,当然这不是重点,实际应用中,一套哨兵只监听单一的一个集群),该被监听集群主节点是127.0.0.1:6379,投票权重值实际是一个数字,是一次决策投票通过的投票数量。
      哨兵的存在是对主故障的自动发现处理,则我们需要哨兵本身可以保证一个高可用的状态,因而需要多个哨兵组成哨兵的集群来一起监听集群。多个哨兵就有多个决策,为避免脑裂,正常配置哨兵都是遵守过半原则(可以自行百度),即哨兵个数过半数(n/2+1)的权重值。例如我们要配3台哨兵,则投票权重可以配2,如下:
  
    
    
  
sentinel moniter name1 127.0.0.1 6379 2
看上面对name1集群的选主投票,只需要2个哨兵投票成功既可以切换。
哨兵的存在只为主,那么具体是如何工作的呢?
      哨兵在启动时,从配置文件可以获知自己需要监听的集群信息(主要是主节点),进而连接上被监听的主,和主节点间通过发布订阅保持联系,会从主节点拉取集群从节点的信息。
以上面的例如来说,3台哨兵,权重设2。当不少于2个哨兵发现主宕机后发起选主,通过对各个从节点数据等信息分析,投出获得不少于2个哨兵投票的从节点,然后再让一个哨兵去对该从节点进行升级主的操作:
REPLICAOF no one
切换成功,每个哨兵会自动修改自己的配置文件,把新的主节点信息替换进去(即修改 sentinel   moniter)。
可见,哨兵就是把人工监听主故障和人工切换主节点给完成了。
3、cluster集群
      随着业务量的增加,单主redis已经无法承载越来越大的数据量和并发量,cluster集群的出现可以解决这个问题。cluster集群把单主变成多主,数据分片到多台redis节点上,实现数据分治。流量也被打到多台机器上。再对每个主加上从的配置完成高可用。在这个模式下,cluster集群中的每个主还承担了类似哨兵的工作,即主的自动故障转移。自动故障转移原理和哨兵类似,不赘述。

经常提到的知识点
1、redis线程模型
      了解过redis工作原理的朋友都知道redis是单线程的,后来又有人说redis是多线程的。是单线程还是多线程得说明你描述的角度是什么。
      在redis6.0之前redis是单线程的,即reids请求在redis内部工作流中是串行的,redis必须完成当前请求的io读取计算处理和返回才可以进行下一条请求的读取处理和返回。因此redis不存在多线程并发的问题。
      在6.0开始redis支持了多线程,这个多线程其实是单个工作线程和多个的io线程。当多个请求打到redis,多个io线程同时去读取请求,先读完的请求可以被工作线程执行,当执行完成后,接着执行其他已被io读取完成的请求。执行结果会被io线程处理。也就是说请求的计算过程和别的请求的io处理可以并行。可见每个请求的非io处理过程还是在redis单线程即工作线程中串行执行的。
2、持久化
      redis的持久化分rdb和aof,当然可以不开启持久化。rdb是把当前全量数据做一个二进制落盘,aof是记录每一次的redis指令。对rdb和aof的选择,可单独使用也可以混合使用,修改对应的redis配置即可。
      单独使用rdb的问题就在于什么时机做一次全量的落盘。单独使用aof,虽然redis对重复写的指令做了抵消合并,整个文件的体量还是会比较大(单纯的一次get执行,在aof中有很多指令记录,如果删除了大量key,可能存在redis的有效数据量很小,但aof会很大)。支持aof和rdb混合使用的版本,开启混合模式后,会把rdb的全量加到aof上,然后追加增量的aof。这种模式可以很好的解决了aof体量大、无用指令过多的问题,还有rdb落盘时机的问题。
3、缓存淘汰策略
截取了redis5.0可配置的缓存淘汰策略:
  
    
    
  
# volatile-lru -> Evict using approximated LRU among the keys with an expire set. # allkeys-lru -> Evict any key using approximated LRU. # volatile-lfu -> Evict using approximated LFU among the keys with an expire set. # allkeys-lfu -> Evict any key using approximated LFU. # volatile-random -> Remove a random key among the ones with an expire set. # allkeys-random -> Remove a random key, any key. # volatile-ttl -> Remove the key with the nearest expire time (minor TTL) # noeviction -> Don't evict anything, just return an error on write operations.
理解策略描述可以从两个方面:操作群体和算法。
  • 操作群体有volatile和allkeys,volatile是设置了过期时间的所有key,allkeys是全部key。

  • 从操作对象中选择的被淘汰者的算法有:lru、lfu、random和ttl,lru是至今谁最久没被使用过选择谁,lfu是选择被访问频率最小的,random是随机选择,ttl是在有过期时间下选择最快要失效的。neviction是不删除,达到最大内存直接返回错误。

4、缓存穿透、击穿、雪崩
  1. 缓存穿透

    是指查询数据库中不存在的数据,这种数据不存在,正常不会设置无结果的缓存,因为无效入参的量不可估计。如果某块业务必须要解决缓存穿透的问题,则可以考虑布隆过滤器过滤绝大部分无效入参的请求。

  2. 缓存击穿

    指被热点访问的key,在缓存失效期间大量流量走到数据库的场景。解决击穿的问题,需要对特定的热点key缓存进行特殊处理,例如加分布式锁把流量拦住,只让一个请求访问数据库再写到缓存,这次场景需要考虑分布式锁的实现,要避免死锁,实现比较复杂。是否要对某些key做击穿的处理需要考虑业务需求和流量场景。

  3. 缓存雪崩

    雪崩的场景是大量的热点key在同一时间都是失效状态,也就是大量key的击穿。遇到这种问题需要考虑:是否需要对缓存预热;是否有大量热点key设置了同一过期时间,是则打乱过期时间。


end

以上是关于道听途说redis的主要内容,如果未能解决你的问题,请参考以下文章

如何利用redis来进行分布式集群系统的限流设计

jedis连接redis

2022-09-11 mysql列存储引擎-宣讲-第二讲-一条SQL在Tianmu引擎中的运行

redis存储session配制方法

机器学习游学记

Redis实现分布式锁(设计模式应用实战)