redis架构设计

一、 简介

  1. 高性能(目前已知性能最快)
    1. 读速度:110000 次 /s
    2. 写速度:81000 次 /s
  2. key-value(单个value的最大限制是1GB)类型的内存数据库
  3. 数据库在内存中进行操作
  4. 支持数据持久化
    1. 定期异步操作将数据库数据flush到硬盘上
  5. 支持string,list,set,sorted set,hash
  6. 操作都是原子性
  7. 支持事务
    1. 对数据的更改要么全部执行,要么全部不执行
    2. 事务中任意命令执行失败,其余命令依然被执行(Redis 事务不保证原子性,也不支持回滚)
    3. 事务中的多条命令被一次性发送给服务器,服务器在执行命令期间,不会去执行其他客户端的命令请求
  8. 支持数据备份( master - slave 模式的数据备份)
  9. 数据库容量受到物理内存的限制,不能用作海量数据的高性能读写
    1. 适合的场景局限在较小数据量的高性能操作和运算上

二、 可实现的功能

2.1 消息队列服务

用List来做FIFO双向链表(前一个元素和后一个元素),实现一个轻量级的高性能消息队列服务

2.2 tag系统

Set可以做高性能的tag系统

2.3 缓存

高并发情况下,将用户基本信息放到redis中,减少对数据库的访问,直接做法就是建立用户的内存模型

存储用户会话缓存信息

 

 

2.4 消息(支持 publish/subscribe 通知)

 

2.5 排行榜/计数器

计数器:Redis在内存中对数字进行递增或递减的操作实现的非常好。

排行榜:有序集合(Sorted Set)通过分数来实现

 

 

三、淘汰策略

3.1从已经设置过期时间的数据集中

volatile-lru: 挑选最近最少使用的数据淘汰
volatile-ttl: 挑选即将要过期的数据淘汰
volatile-random: 随机挑选数据淘汰

3.2从所有的数据集中

allkeys-lru: 挑选最近最少使用的数据淘汰
allkeys-random:,随机挑选数据淘汰

3.3 no-enviction:禁止淘汰数据
 

四、过期键的策略

4.1定时删除

缓存过期时间到就删除,创建timer耗CPU

4.2惰性删除

获取的时候检查,不获取一直留在内存,对内存不友好

4.3定期删除

CPU和内存的折中方案

 

五、数据类型分析

5.1 string

最基本的数据类型。
二进制安全的,可以包含任何数据。
最大能存储 512 MB。

5.2 hash

  • 一个键值对(key - value)集合。
  • 一个 string 类型的 key 和 value 的映射表,
  • 适合用于存储对象
  • 可以对对象某一项属性值进行存储、读取、修改等操作。

 

5.3 list

  • 是简单的字符串列表(集合)。
  • 按照插入顺序排序。我们可以网列表的左边或者右边添加元素。
  • 元素是可重复的。
  • 适用做消息队列或最新消息排行等功能。

 

5.4 set

  • 无序
  • 通过哈希表实现,因此添加、删除、查找的复杂度都是 O(1)。
  • 是一个 key 对应着多个字符串类型的 value的集合
  • 集合元素不能重复
  • 可以统计访问网站的所有独立ip。

5.5 Zset

  • 和set 一样
  • 不同的是zset 每个元素都会关联一个 double 类型的分数,通过分数来为集合中的成员进行从小到大的排序。
  • value元素是唯一的,但是分数(score)却可以重复。
  • 可用作排行榜等场景。

 

六、Redis高可用架构

6.1、持久化

 

6.1.1、RDB

  • 定期将存储的数据生成快照并存储到磁盘上,
  • 可以将快照复制到其他服务器从而创建具有相同数据的服务器副本。
  • 系统故障,会丢失最后一次创建快照之后的数据。
  • 数据量大,保存快照的时间会很长。

6.1.2、AOF

  • 将redis执行过的所有写指令记录下来,在下次redis重新启动时,只要把这些写指令从前到后再执行一遍,就可以实现数据恢复。
  • 将写命令添加到 AOF 文件(append only file)末尾。
  • 对文件进行写入并不会马上将内容同步到磁盘上,而是先存储到缓冲区,然后由操作系统决定什么时候同步到磁盘。
  • AOF有重写的特性,去除AOF文件中的冗余写命令。
  • redis重启的话,则会优先采用AOF方式来进行数据恢复(aop数据更完整)
  • 需要设置同步选项,从而确保写命令同步到磁盘文件上的时机。

6.1.2.1、同步选项

always

  • 每个写命令都同步
  • 会严重减低服务器的性能

eyerysec

  • 每秒同步一次
  • 选项比较合适,可以保证系统崩溃时只会丢失一秒左右的数据,且每秒执行一次同步对服务器几乎没有任何影响。

no

  • 让操作系统来决定何时同步
  • 并不能给服务器性能带来多大的提升
  • 会增加系统崩溃时数据丢失的数量

 

6.2、复制

为了解决单点数据库问题(主从和主备两种模式),会把数据复制多个副本部署到其他节点上, 实现Redis的高可用性, 保证数据和服务的高度可靠性。


主备(keepalived)模式


主机备机对外提供同一个虚拟IP,客户端通过虚拟IP进行数据操作,正常期间主机一直对外提供服务,宕机后VIP自动漂移到备机上。

主从模式


当Master宕机后,通过选举算法从slave中选举出新Master继续对外提供服务,主机恢复后以slave的身份重新加入,此模式下可以使用读写分离,如果数据量比较大,不希望过多浪费机器,还希望在宕机后,做一些自定义的措施,比如报警、记日志、数据迁移等操作,推荐使用主从方式,因为和主从搭配的一般还有个管理监控中心(哨兵)。

 

6.3、 数据通过过程

6.3.1 Redis2.8之前同步

 

 

  • ①从数据库向主数据库发送sync(数据同步)命令。
  • ②主数据库接收同步命令后,会保存快照,创建一个RDB文件。
  • ③当主数据库执行完保持快照后,会向从数据库发送RDB文件,而从数据库会接收并载入该文件。
  • ④主数据库将缓冲区的所有写命令发给从服务器执行。
  • ⑤以上处理完之后,之后主数据库每执行一个写命令,都会将被执行的写命令发送给从数据库。可以同步发送也可以异步发送,同步发送可以不用每台都同步,可以配置一台master,一台slave,同时这台salve又作为其他slave的master。异步方式无法保证数据的完整性,比如在异步同步过程中主机突然宕机了,也称这种方式为数据弱一致性。
  • 注意:在Redis2.8之后,主从断开重连后会根据断开之前最新的命令偏移量进行增量复制。

6.3.2 Redis2.8之后同步

 

6.4 、哨兵

 当主节点出现故障时,由哨兵自动完成故障发现和转移,并通知应用方,实现高可用性。

6.4.1 Redis哨兵主要功能

集群监控:负责监控Redis master和slave进程是否正常工作
消息通知:如果某个Redis实例有故障,那么哨兵负责发送消息作为报警通知给管理员
故障转移:如果master node挂掉了,会自动转移到slave node上
配置中心:如果故障转移发生了,通知client客户端新的master地址

6.4.2 Redis哨兵的高可用

原理

  • 哨兵机制建立了多个哨兵节点(进程),共同监控数据节点的运行状况。
  • 同时哨兵节点之间也互相通信,交换对主从节点的监控状况。

哨兵用来判断节点是否正常的依据

每隔1秒每个哨兵会向整个集群:Master主服务器+Slave从服务器+其他Sentinel(哨兵)进程,发送一次ping命令做一次心跳检测。

主节点down掉依据(主观下线和客观下线

主观下线

一个哨兵节点判定主节点down掉是主观下线

客观下线

只有半数哨兵节点都判定主观下线,就会会判定主节点客观下线

补充

基本上哪个哨兵节点最先判断出这个主节点客观下线,就会在各个哨兵节点中发起投票机制Raft算法(选举算法),最终被投为领导者的哨兵节点完成主从自动化切换的过程。

 

6.5 、 集群

高可用性:在主机挂掉后,自动故障转移,使前端服务对用户无影响。

读写分离:将主机读压力分流到从机上。

6.5.1、小数据到大数据过程

 

问题

缓存数据量不断增加时,单机内存不够使用

解决方案

把数据切分不同部分,分布到多台服务器上。 可在客户端对数据进行分片,数据分片算法详见一致性Hash详解、虚拟桶分片。

 

问题

数据量持续增加时,越来越多的客户端直接访问Redis服务器难以管理,而造成风险

解决方案

根据不同场景下的业务申请对应的分布式集群。

加入了代理服务(Codis和Twemproxy),通过代理访问真实的Redis服务器进行读写

代理服务(Codis和Twemproxy)

在代理层做安全措施,比如限流、授权、分片,避免客户端越来越多的逻辑代码,不但臃肿升级还比较麻烦。
代理层无状态,可任意扩展节点,对于客户端来说,访问代理跟访问单机Redis一样。

 

 

 

6.5.2Redis官网的集群架构

 

 

6.5.2.1、原理

客户端与Redis(Master)节点直连,不需要中间Proxy层,根据公式HASH_SLOT=CRC16(key) mod 16384,计算出映射到哪个分片上,然后Redis会去相应的节点进行操作

 

6.5.2.2、优点

  • 无需Sentinel哨兵监控,如果Master挂了,自动将Slave切换Master
  • 可以进行水平扩容
  • 支持自动化迁移
    • 当出现某个Slave宕机了,那么就只有Master了,这时候的高可用性就无法很好的保证了,万一Master也宕机了,咋办呢? 针对这种情况,如果说其他Master有多余的Slave ,集群自动把多余的Slave迁移到没有Slave的Master 中。

6.5.2.3、缺点

  • 批量操作是个坑,不同的key会划分到不同的slot中,因此直接使用mset或者mget等操作是行不通的。
    • 解决方案:使用Hashtag保证这些key映射到同一台Redis节点上。
  • 资源隔离性较差,容易出现相互影响的情况。
     

 

七、Redis高并发及热key解决之道

7.1、并发设置key及分布式锁

问题

集群环境下的定时任务,存在A服务器执行任务t1,B服务器也执行了任务t1,如果t1是创建订单,那么就会出现重复订单。

解决方案

  • 分布式锁
  • 使用消息队列,把并行读写进行串行化。

 

7.2、热key问题

问题

瞬间有几十万的请求去访问某个固定的key,从而压垮缓存服务的情况

热key判断依据

  • 凭借业务经验,进行预估哪些是热key
  • 在客户端进行收集
  • 在Proxy层做收集
  • 用redis自带命令(monitor命令、hotkeys参数)


解决方案:

  1. 利用二级缓存,比如一个HashMap。在你发现热key以后,把热key加载到系统的JVM中。查找的时候先去本地jvm查询,查不到再去redis查询
  2. 备份热key,不要让key走到同一台redis上。我们把这个key,在多个redis上都存一份。可以用HOTKEY加上一个随机数(N,集群分片数)组成一个新key。
  3. 热点数据尽量不要设置过期时间,在数据变更时同步写缓存,防止高并发下重建缓存的资源损耗。


7.3、缓存穿透

 指查询一个根本不存在的数据,缓存层和存储层都不会命中,但是出于容错的考虑,如果从存储层查不到数据则不写入缓存层。

 7.3.1、影响

将导致不存在的数据每次请求都要到存储层去查询,失去了缓存保护后端存储的意义。

 7.3.2、缓存穿透原因


 业务自身代码或者数据出现问题,
 一些恶意攻击、爬虫等造成大量空命中

 7.3.3、解决缓存穿透方案

1)缓存空对象

缓存空对象会有两个问题

  1. 空值做了缓存,意味着缓存层中存了更多的键,需要更多的内存空间 ( 如果是攻击,问题更严重 ),比较有效的方法是针对这类数据设置一个较短的过期时间,让其自动剔除。
  2. 缓存层和存储层的数据会有一段时间窗口的不一致,例如过期时间设置为1分钟,如果此时存储层添加了这个数据,那此段时间就会出现缓存层和存储层数据的不一致,更新数据的时候清除掉缓存层中的空对象。

2)布隆过滤器拦截

如下图所示,在访问缓存层和存储层之前,将存在的 key 用布隆过滤器提前保存起来,做第一层拦截。如果布隆过滤器认为key不存在,那么就不会访问存储层,在一定程度保护了存储层。

可以参考: 布隆过滤器,可以利用 Redis 的 Bitmaps 实现布隆过滤器redis bitmaps实现布隆过滤器

 7.3.4、缓存空对象和布隆过滤器方案对比


 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章