一.Redis简介

Redis 是齐全开源收费的,是一个高性能的key-value类型的内存数据库。整个数据库通通加载在内存当中进行操作,定期通过异步操作把数据库数据flush到硬盘上进行保留。因为是纯内存操作,Redis的性能十分杰出,每秒能够解决超过 10万次读写操作,是已知性能最快的Key-Value DB。

Redis的杰出之处不仅仅是性能,Redis最大的魅力是反对保留多种数据结构,此外单个value的最大限度是1GB,因而Redis能够用来实现很多有用的性能,比方说用List来做FIFO双向链表,实现一个轻量级的高性 能音讯队列服务,用他的Set能够做高性能的tag零碎等等。另外Redis也能够对存入的Key-Value设置expire工夫。总结来说,应用Redis的益处如下:

1.速度快,因为数据存在内存中,读的速度是 110000 次 /s, 写的速度是 81000 次 /s;

2.反对丰盛数据类型,反对string,list,set,sorted set,hash;

3.反对事务,操作都是原子性,对数据的更改要么全副执行,要么全副不执行,事务中任意命令执行失败,其余命令仍然被执行。也就是说 Redis 事务不保障原子性,也不反对回滚;事务中的多条命令被一次性发送给服务器,服务器在执行命令期间,不会去执行其余客户端的命令申请。

4.丰盛的个性:可用于缓存,音讯(反对 publish/subscribe 告诉),按key设置过期工夫,过期后将会主动删除,具体淘汰策略有:

  4.1.volatile-lru:从曾经设置过期工夫的数据集中,筛选最近起码应用的数据淘汰

  4.2.volatile-ttl:从曾经设置过期工夫的数据集中,筛选行将要过期的数据淘汰

  4.3.volatile-random:从曾经设置过期工夫的数据集中,随机筛选数据淘汰

  4.4.allkeys-lru:从所有的数据集中,筛选最近起码应用的数据淘汰

  4.5.allkeys-random:从所有的数据集中,随机筛选数据淘汰

  4.6.no-enviction:禁止淘汰数据

具体过期键的策略有:定时删除(缓存过期工夫到就删除,创立timer耗CPU),惰性删除(获取的时候查看,不获取始终留在内存,对内存不敌对),定期删除(CPU和内存的折中计划)

5.反对数据长久化,能够将内存中的数据保留在磁盘中,重启的时候能够再次加载进行应用;

6.反对数据的备份,即 master - slave 模式的数据备份。

Redis的次要毛病是数据库容量受到物理内存的限度,不能用作海量数据的高性能读写,因而Redis适宜的场景次要局限在较小数据量的高性能操作和运算上。

二.Redis的数据类型

Redis 反对 5 中数据类型:string(字符串),hash(哈希),list(列表),set(汇合),zset(sorted set:有序汇合)。每种数据类型的具体命令请参考Redis 命令参考

string

string 是 redis 最根本的数据类型。一个 key 对应一个 value。string 是二进制平安的。也就是说 redis 的 string 能够蕴含任何数据。比方 jpg 图片或者序列化的对象。string 类型是 redis 最根本的数据类型,string 类型的值最大能存储 512 MB。

hash

Redis hash 是一个键值对(key - value)汇合。Redis hash 是一个 string 类型的 key 和 value 的映射表,hash 特地适宜用于存储对象。并且能够像数据库中一样只对某一项属性值进行存储、读取、批改等操作。

list

Redis 列表是简略的字符串列表,依照插入程序排序。咱们能够网列表的右边或者左边增加元素。list 就是一个简略的字符串汇合,和 Java 中的 list 相差不大,区别就是这里的 list 寄存的是字符串。list 内的元素是可反复的。能够做音讯队列或最新消息排行等性能。

set

redis 的 set 是字符串类型的无序汇合。汇合是通过哈希表实现的,因而增加、删除、查找的复杂度都是 O(1)。redis 的 set 是一个 key 对应着多个字符串类型的 value,也是一个字符串类型的汇合,和 redis 的 list 不同的是 set 中的字符串汇合元素不能反复,然而 list 能够。利用唯一性,能够统计拜访网站的所有独立 ip。

Zset

redis zset 和 set 一样都是字符串类型元素的汇合,并且汇合内的元素不能反复。不同的是 zset 每个元素都会关联一个 double 类型的分数。redis 通过分数来为汇合中的成员进行从小到大的排序。zset 的元素是惟一的,然而分数(score)却能够反复。可用作排行榜等场景。

三.redis实用场景

1.会话缓存(Session Cache)

最罕用的一种应用Redis的情景是会话缓存(session cache)。用Redis缓存会话比其余存储(如Memcached)的劣势在于:Redis提供长久化。当保护一个不是严格要求一致性的缓存时,如果用户的购物车信息全副失落,大部分人都会不快乐的。

2.队列

Reids在内存存储引擎畛域的一大长处是提供 list 和 set 操作,这使得Redis能作为一个很好的音讯队列平台来应用。Redis作为队列应用的操作,就相似于本地程序语言(如Python)对 list 的 push/pop 操作。

3.全页缓存

大型互联网公司都会应用Redis作为缓存存储数据,晋升页面相应速度。即便重启了Redis实例,因为有磁盘的长久化,用户也不会看到页面加载速度的降落。

4.排行榜/计数器

Redis在内存中对数字进行递增或递加的操作实现的十分好。汇合(Set)和有序汇合(Sorted Set)也使得咱们在执行这些操作的时候变的非常简单。

四.Redis高可用架构

1.长久化

Redis 是内存型数据库,为了保证数据在断电后不会失落,须要将内存中的数据长久化到硬盘上。Redis提供了两种长久化的形式,别离是RDB(Redis DataBase)和AOF(Append Only File)。

RDB

简而言之,就是在不同的工夫点,将redis存储的数据生成快照并存储到磁盘等介质上,能够将快照复制到其余服务器从而创立具备雷同数据的服务器正本。如果零碎产生故障,将会失落最初一次创立快照之后的数据。如果数据量大,保留快照的工夫会很长。

AOF

换了一个角度来实现长久化,那就是将redis执行过的所有写指令记录下来,在下次redis重新启动时,只有把这些写指令从前到后再反复执行一遍,就能够实现数据恢复了。将写命令增加到 AOF 文件(append only file)开端。

应用 AOF 长久化须要设置同步选项,从而确保写命令同步到磁盘文件上的机会。这是因为对文件进行写入并不会马上将内容同步到磁盘上,而是先存储到缓冲区,而后由操作系统决定什么时候同步到磁盘。选项同步频率always每个写命令都同步,eyerysec每秒同步一次,no让操作系统来决定何时同步,always 选项会重大减低服务器的性能,everysec 选项比拟适合,能够保证系统解体时只会失落一秒左右的数据,并且 Redis 每秒执行一次同步对服务器简直没有任何影响。no 选项并不能给服务器性能带来多大的晋升,而且会减少零碎解体时数据失落的数量。随着服务器写申请的增多,AOF 文件会越来越大。Redis 提供了一种将 AOF 重写的个性,可能去除 AOF 文件中的冗余写命令。

其实RDB和AOF两种形式也能够同时应用,在这种状况下,如果redis重启的话,则会优先采纳AOF形式来进行数据恢复,这是因为AOF形式的数据恢复残缺度更高。如果你没有数据长久化的需要,也齐全能够敞开RDB和AOF形式,这样的话,redis将变成一个纯内存数据库。

2.复制

Redis为了解决单点数据库问题,会把数据复制多个正本部署到其余节点上,通过复制,实现Redis的高可用性,实现对数据的冗余备份,保证数据和服务的高度可靠性。Redis有主从和主备两种形式解决单点问题,主备(keepalived)模式下主机备机对外提供同一个虚构IP,客户端通过虚构IP进行数据操作,失常期间主机始终对外提供服务,宕机后VIP主动漂移到备机上。主从模式下当Master宕机后,通过选举算法(Paxos、Raft)从slave中选举出新Master持续对外提供服务,主机复原后以slave的身份重新加入,此模式下能够应用读写拆散,如果数据量比拟大,不心愿过多节约机器,还心愿在宕机后,做一些自定义的措施,比方报警、记日志、数据迁徙等操作,举荐应用主从形式,因为和主从搭配的个别还有个治理监控核心(哨兵)。

①从数据库向主数据库发送sync(数据同步)命令。

②主数据库接管同步命令后,会保留快照,创立一个RDB文件。

③当主数据库执行完放弃快照后,会向从数据库发送RDB文件,而从数据库会接管并载入该文件。

④主数据库将缓冲区的所有写命令发给从服务器执行。

⑤以上解决完之后,之后主数据库每执行一个写命令,都会将被执行的写命令发送给从数据库。能够同步发送也能够异步发送,同步发送能够不必每台都同步,能够配置一台master,一台slave,同时这台salve又作为其余slave的master。异步形式无奈保证数据的完整性,比方在异步同步过程中主机忽然宕机了,也称这种形式为数据弱一致性。

留神:在Redis2.8之后,主从断开重连后会依据断开之前最新的命令偏移量进行增量复制。

3.哨兵

哨兵是Redis集群架构中十分重要的一个组件,哨兵的呈现次要是解决了主从复制呈现故障时须要人为干涉的问题。

1.Redis哨兵次要性能

(1)集群监控:负责监控Redis master和slave过程是否失常工作

(2)音讯告诉:如果某个Redis实例有故障,那么哨兵负责发送音讯作为报警告诉给管理员

(3)故障转移:如果master node挂掉了,会主动转移到slave node上

(4)配置核心:如果故障转移产生了,告诉client客户端新的master地址

2.Redis哨兵的高可用


原理:当主节点呈现故障时,由Redis Sentinel主动实现故障发现和转移,并告诉利用方,实现高可用性。哨兵机制建设了多个哨兵节点(过程),独特监控数据节点的运行状况。同时哨兵节点之间也相互通信,替换对主从节点的监控情况。每隔1秒每个哨兵会向整个集群:Master主服务器+Slave从服务器+其余Sentinel(哨兵)过程,发送一次ping命令做一次心跳检测。这个就是哨兵用来判断节点是否失常的重要依据,波及两个新的概念:主观下线和主观下线。一个哨兵节点断定主节点down掉是主观下线,只有半数哨兵节点都主观断定主节点down掉,此时多个哨兵节点替换主观断定后果,才会断定主节点主观下线。基本上哪个哨兵节点最先判断出这个主节点主观下线,就会在各个哨兵节点中发动投票机制Raft算法(选举算法),最终被投为领导者的哨兵节点实现主从自动化切换的过程。

4.集群

至多部署两台Redis服务器形成一个小的集群,次要有2个目标:

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

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

可在客户端组件上实现负载平衡,依据不同服务器的运行状况,分担不同比例的读申请压力。


缓存数据量一直减少时,单机内存不够应用,须要把数据切分不同局部,散布到多台服务器上。可在客户端对数据进行分片,数据分片算法详见一致性Hash详解、虚构桶分片。

当数据量继续减少时,利用可依据不同场景下的业务申请对应的分布式集群。这块最要害的是缓存治理这块,其中最重要的局部是退出了代理服务(Codis和Twemproxy)。利用通过代理拜访实在的Redis服务器进行读写,这样做的益处是防止越来越多的客户端间接拜访Redis服务器难以治理,而造成危险,在代理这一层能够做对应的安全措施,比方限流、受权、分片,防止客户端越来越多的逻辑代码,岂但臃肿降级还比拟麻烦。代理这层无状态的,可任意扩大节点,对于客户端来说,拜访代理跟拜访单机Redis一样。


Redis Cluster是Redis官网给出的集群架构

客户端与Redis节点直连,不须要两头Proxy层,间接连贯任意一个Master节点,依据公式HASH_SLOT=CRC16(key) mod 16384,计算出映射到哪个分片上,而后Redis会去相应的节点进行操作

具备如下长处:

(1)无需Sentinel哨兵监控,如果Master挂了,Redis Cluster外部主动将Slave切换Master

(2)能够进行程度扩容

(3)反对自动化迁徙,当呈现某个Slave宕机了,那么就只有Master了,这时候的高可用性就无奈很好的保障了,万一Master也宕机了,咋办呢?针对这种状况,如果说其余Master有多余的Slave ,集群主动把多余的Slave迁徙到没有Slave的Master 中。

毛病:

(1)批量操作是个坑,不同的key会划分到不同的slot中,因而间接应用mset或者mget等操作是行不通的。如果执行的key数量比拟少,就不必mget了,就用串行get操作。如果真的须要执行的key很多,就应用Hashtag保障这些key映射到同一台Redis节点上。

(2)资源隔离性较差,容易呈现相互影响的状况。

五.Redis高并发及热key解决之道

1.并发设置key及分布式锁

Redis是一种单线程机制的nosql数据库,基于key-value,数据可长久化落盘。因为单线程所以Redis自身并没有锁的概念,多个客户端连贯并不存在竞争关系,然而利用jedis等客户端对Redis进行并发拜访时会呈现问题。比方多客户端同时并发写一个key,一个key的值是1,原本按程序批改为2,3,4,最初是4,然而程序变成了4,3,2,最初变成了2。应用分布式锁避免并发设置Key的原理及代码见:应用Redis实现分布式锁及其优化,另外一种形式是应用音讯队列,把并行读写进行串行化。

2.热key问题

热key问题说来也很简略,就是霎时有几十万的申请去拜访redis上某个固定的key,从而压垮缓存服务的情状况。其实生存中也是有不少这样的例子。比方XX明星结婚。那么对于XX明星的Key就会霎时增大,就会呈现热数据问题。那么如何发现热KEY呢:

1.凭借业务教训,进行预估哪些是热key

2.在客户端进行收集

3.在Proxy层做收集

4.用redis自带命令(monitor命令、hotkeys参数)

5.本人抓包评估

解决方案:

1.利用二级缓存,比方利用ehcache,或者一个HashMap都能够。在你发现热key当前,把热key加载到零碎的JVM中。

2.备份热key,不要让key走到同一台redis上。咱们把这个key,在多个redis上都存一份。能够用HOTKEY加上一个随机数(N,集群分片数)组成一个新key。

3.热点数据尽量不要设置过期工夫,在数据变更时同步写缓存,避免高并发下重建缓存的资源损耗。能够用setnx做分布式锁保障只有一个线程在重建缓存,其余线程期待重建缓存的线程执行完,从新从缓存获取数据即可。

3.缓存穿透

缓存穿透是指查问一个基本不存在的数据,缓存层和存储层都不会命中,然而出于容错的思考,如果从存储层查不到数据则不写入缓存层。缓存穿透将导致不存在的数据每次申请都要到存储层去查问,失去了缓存爱护后端存储的意义。造成缓存穿透的根本有两个。第一,业务本身代码或者数据呈现问题,第二,一些歹意攻打、爬虫等造成大量空命中,上面咱们来看一下如何解决缓存穿透问题。解决缓存穿透的两种计划:

1)缓存空对象

缓存空对象会有两个问题:

第一,空值做了缓存,意味着缓存层中存了更多的键,须要更多的内存空间 ( 如果是攻打,问题更重大 ),比拟无效的办法是针对这类数据设置一个较短的过期工夫,让其主动剔除。

第二,缓存层和存储层的数据会有一段时间窗口的不统一,可能会对业务有肯定影响。例如过期工夫设置为 5 分钟,如果此时存储层增加了这个数据,那此段时间就会呈现缓存层和存储层数据的不统一,此时能够利用音讯零碎或者其余形式革除掉缓存层中的空对象。

2)布隆过滤器拦挡

如下图所示,在拜访缓存层和存储层之前,将存在的 key 用布隆过滤器提前保存起来,做第一层拦挡。如果布隆过滤器认为该用户 ID 不存在,那么就不会拜访存储层,在肯定水平爱护了存储层。无关布隆过滤器的相干常识,能够参考:布隆过滤器,能够利用 Redis 的 Bitmaps 实现布隆过滤器,GitHub 上曾经开源了相似的计划,读者能够进行参考:redis bitmaps实现布隆过滤器。
缓存空对象和布隆过滤器计划比照

4.缓存雪崩

数据未加载到缓存中,或者缓存同一时间大面积的生效,从而导致所有申请都去查数据库,导致数据库CPU和内存负载过高,甚至宕机。

图片
能够从以下几个方面避免缓存雪崩:

1)保障缓存层服务高可用性

和飞机都有多个引擎一样,如果缓存层设计成高可用的,即便个别节点、个别机器、甚至是机房宕掉,仍然能够提供服务,例如后面介绍过的 Redis Sentinel 和 Redis Cluster 都实现了高可用。

2)Redis备份和疾速预热

Redis备份保障master出问题切换为slave迅速可能承当线上理论流量,疾速预热保障缓存及时被写入缓存,避免穿透到库。

3)依赖隔离组件为后端限流并降级

无论是缓存层还是存储层都会有出错的概率,能够将它们视同为资源。作为并发量较大的零碎,如果有一个资源不可用,可能会造成线程全副 hang 在这个资源上,造成整个零碎不可用。降级在高并发零碎中是十分失常的:比方举荐服务中,如果个性化举荐服务不可用,能够降级补充热点数据,不至于造成前端页面是开天窗。

在理论我的项目中,咱们须要对重要的资源 ( 例如 Redis、 MySQL、 Hbase、内部接口 ) 都进行隔离,让每种资源都独自运行在本人的线程池中,即便个别资源呈现了问题,对其余服务没有影响。然而线程池如何治理,比方如何敞开资源池,开启资源池,资源池阀值治理,这些做起来还是相当简单的,这里举荐一个 Java 依赖隔离工具Hystrix(https://github.com/Netflix/Hy...),如下图所示。

4)提前演练

在我的项目上线前,演练缓存层宕掉后,利用以及后端的负载状况以及可能呈现的问题,在此基础上做一些预案设定。

5.缓存预热

缓存预热就是零碎上线前,将相干的缓存数据间接加载到缓存零碎。这样就能够防止上线后在用户申请的时候,先查询数据库,而后再将数据缓存的问题!用户间接查问当时被预热的缓存数据!