Redis架构原理及应⽤实践
Redis架构原理及应⽤实践
简介
Redis 是完全开源免费的,遵守BSD协议,是⼀个灵活的⾼性能key-value数据结构存储,可以⽤来作为数据库,缓存和消息队列。 Redis ⽐其他key – value 缓存产品有以下三个特点: 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提供持久化,Memcached⼏乎⽤的很少了。
2.队列
Reids在内存存储引擎领域的⼀⼤优点是提供 list 和 set 操作,这使得Redis能作为⼀个很好的消息队列平台来使⽤。Redis作为队列使⽤的操作,就类似于本地程序语⾔(如Python)对 list 的 push/pop 操作,这种功能往往被其他消息中间件取代。
3.全页缓存
redis支持的五种数据类型⼤型互联⽹公司都会使⽤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将变成⼀个纯内存数据库。
四: 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 分钟,如果此时存储层添加了这个数据,那此段时间就会出现缓存层和存储层数据的不⼀致,此时可以利⽤消息系统或者其他⽅式清除掉缓存层中的空对象。
c2)布隆过滤器拦截
如下图所⽰,在访问缓存层和存储层之前,将存在的 key ⽤布隆过滤器提前保存起来,做第⼀层拦截。如果布隆过滤器认为该⽤户 ID 不存在,那么就不会访问存储层,在⼀定程度保护了存储层。有关布隆过滤器的相关知识,可以参考: 布隆过滤器,可以利⽤ Redis 的Bitmaps 实现布隆过滤器,GitHub 上已经开源了类似的⽅案,读者可以进⾏参考:redis bitmaps实现布隆过滤器
五:缓存雪崩
数据未加载到缓存中,或者缓存同⼀时间⼤⾯积的失效,从⽽导致所有请求都去查数据库,导致数据库CPU和内存负载过⾼,甚⾄宕机。可以从以下⼏个⽅⾯防⽌缓存雪崩:
1)保证缓存层服务⾼可⽤性
和飞机都有多个引擎⼀样,如果缓存层设计成⾼可⽤的,即使个别节点、个别机器、甚⾄是机房宕掉,依然可以提供服务,例如前⾯介绍过的 Redis Sentinel 和 Redis Cluster 都实现了⾼可⽤。
2)Redis备份和快速预热
Redis备份保证master出问题切换为slave迅速能够承担线上实际流量,快速预热保证缓存及时被写⼊缓存,防⽌穿透到库。
3)依赖隔离组件为后端限流并降级
⽆论是缓存层还是存储层都会有出错的概率,可以将它们视同为资源。作为并发量较⼤的系统,假如有⼀个资源不可⽤,可能会造成线程全部 hang 在这个资源上,造成整个系统不可⽤。降级在⾼并发系统中是⾮常正常的:⽐如推荐服务中,如果个性化推荐服务不可⽤,可以降级补充热点数据,不⾄于造成前端页⾯是开天窗。
4)提前演练
在项⽬上线前,演练缓存层宕掉后,应⽤以及后端的负载情况以及可能出现的问题,在此基础上做⼀些预案设定。
总结
项⽬中使⽤redis很平常, ⼀般使⽤集就够⽤了,数据量⼤⼀点,防⽌缓存穿透,⽤⼆级缓存ehcache也就差不多了,布隆过滤器⽬前还没尝试过,暂且先记⼀笔
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论