关于Redis的一些知识

项目中Redis是如何使用的?
 
项目中使用Redis主要有两方面的用途,高性能和高并发。
 
高性能:比如说我们有一个系统, 每次在数据库进行查询都要耗费800ms,而且这个数据可能在相当长的时间内是不会变化的。这个时候我们就可以引入Redis,还是查询数据,只不过这次查询到数据后会在缓存中写一份。等下一次想要查询相同的数据不用走数据库,只需要从Redis中查询即可。如果想要更新数据,只需要在更改数据库的同时更新Redis中的数据即可。
 
高并发:MySQL是属于比较重的数据库,高并发场景下不是很友好,它一般最多只能支撑起2000qps。所以如果有一个系统,每秒4000个请求,如果这些请求全部打到MySQL上,mysql就会宕机。所以可以引入Redis,假设3000个请求走Reids,1000个请求走Mysql,系统就可以正常运行了。
 
说了这么多Redis的好处,那么他有什么不足呢?
 
常见的缓存问题其实有四个, 缓存和数据库双写不一致、缓存穿透、缓存雪崩、缓存并发竞争。
 
为什么Redis是单线程效率也能这么高?
 
  • 完全基于内存,绝大部分请求都是纯粹的内存操作,执行效率高(不受磁盘I/O速度影响)
  • 数据结构简单,对数据操作也简单 不使用表(不是关系型数据库)
  • 采用单线程,单线程也能处理高并发(不是并行)请求(串行处理)制约redis的瓶颈更多的是内存和网络带宽而不是cpu
  • 使用多路I/O复用模型,多路指的是多个网络连接,复用是指复用同一个线程。采用多路I/O复用模型可以让单个线程高效的处理多个连接请求(可以减少网络的I/O时间消耗),可以理解为单线程的原子操作,避免上下文切换的时间和性能消耗;加上内存的处理速度很自然的提高了响应速度。
 
 
Redis都有哪些数据类型?分别适合什么场景?
 
 

1.String

常用命令: set,get,decr,incr,mget 等。
String数据结构是简单的key-value类型,value其实不仅可以是String,也可以是数字。 常规key-value缓存应用; 常规计数:微博数,粉丝数等。二进制安全,可以存储任何数据(包括图片)可以做计数器 
 
我们有个web项目,想要记录每个用户每天的登录次数,通过拼接用户id和时间作为key 然后   incr userID20190516
 
2.Hash
常用命令: hget,hset,hgetall 等。
hash 是一个 string 类型的 field 和 value 的映射表,hash 特别适合用于存储对象,后续操作的时候,你可以直接仅仅修改这个对象中的某个字段的值。 比如我们可以 hash 数据结构来存储用户信息,商品信息等等。
 
 

3.List

常用命令: lpush,rpush,lpop,rpop,lrange等
list 就是链表,Redis list 的应用场景非常多,也是Redis最重要的数据结构之一,比如微博的关注列表,粉丝列表,消息列表等功能都可以用Redis的 list 结构来实现。最新消息排行榜
 
 

4.Set

常用命令: sadd,spop,smembers,sunion 等
 
set 对外提供的功能与list类似是一个列表的功能,特殊之处在于 set 是可以自动排重的。
当你需要存储一个列表数据,又不希望出现重复数据时,set是一个很好的选择,并且set提供了判断某个成员是否在一个set集合内的重要接口,这个也是list所不能提供的。可以基于 set 轻易实现交集、并集、差集的操作。
比如:在微博应用中,可以将一个用户所有的关注人存在一个集合中,将其所有粉丝存在一个集合。Redis可以非常方便的实现如共同关注、共同粉丝、共同喜好等功能。这个过程也就是求交集的过程。
 
 

5.Sorted Set

常用命令: zadd,zrange,zrem,zcard等
和set相比,sorted set增加了一个权重参数score,使得集合中的元素能够按score进行有序排列。
举例: 在直播系统中,实时排行信息包含直播间在线用户列表,各种礼物排行榜,弹幕消息(可以理解为按消息维度的消息排行榜)等信息,适合使用 Redis 中的 Sorted Set 结构进行存储。value学号 score分数 普通消息为2 重要消息为1
 
Redis的过期策略有哪些?
 
Redis的过期策略主要是定期删除+惰性删除的方式。你可以在set key的时候,设置一个expire time就是过期时间,可以指定缓存到期就失效。如果你对一批key都设置了相同的过期时间,比如说一个小时。那么他们就会采取上面说的策略。
 
Redis默认是每隔100ms就随机抽取一些设置了过期时间的key,检查其是否过期,如果过期就删除。假设redis里放了10万个key,都设置了过期时间,如果每隔几百毫秒,就检查10万个key,那么Redis的CPU负载会很高,性能都消耗在检查key上了有宕机的风险。实际上redis是每隔100ms随机抽取一些key来检查和删除的,但是定期删除可能会导致很多过期key到了时间并没有被删除掉。那么就会引入惰性删除的机制,你在获取某个key的时候,Redis会检查一下这个key是否过期,如果过期就会删除。
 
但是也有可能你定期删除漏删了很多key,也没有走惰性删除,大量过期key就都积压在内存中,内存可能会耗尽。这个时候就要走内存淘汰机制,常用的是 allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key。
 
通过上面的策略,可以保证Redis的正常运行。
 
 
 
 
 
 
如何保证Redis的高并发和高可用?
 
 
高并发:主从架构,一主多从
主从架构可以做成读写分离的模式,主节点负责写数据并且将数据同步到其他节点上,从节点负责读,所有读请求都会走从节点。假设一台机器可以支撑5wqps的读请求,有四台机器,现在是20wqps。如果想要增加读qps,那么水平扩容继续加机器就行了。
 
高可用:主从架构部署+哨兵
 
主从复制的原理
 
一个master node可以连接多个slave node,Redis采用异步方式复制数据到slave node。slave node在复制的时候不仅不会阻塞master的正常工作,也不会阻塞自己正常的查询工作,它会使用旧的数据对外提供服务,当复制完成之后它会删除旧的数据集,加载新的数据集,这个时候就会暂停工作了。可以增加slave node的台数(横向扩容)提高吞吐量。而且不建议用slave node的数据作为master的备份,必须开启master的持久化。因为如果master宕机,重启,没有本地数据可以恢复,他会认为自己数据为空,然后同步到slave上,造成大量数据丢失。
 
什么是Redis的高可用,什么是不可用
 
Redis的高可用就是指在一定时间期限内,99.99%情况下你的系统都是可以正常工作的。不可用:比如有1个master两个slave,slave宕机了有另一个slave还可以正常工作,但是master宕机了,就没法写数据了,就是不可用。一般情况下出现这种情况,mysql就会被打到崩溃。
 
Redis如何才能做到高可用?
 
可以使用哨兵的机制,哨兵是Redis中非常重要的组件,主要有以下功能:
集群监控:监控master、slave能否正常工作
消息通知:某个redis实例出现故障,会发送消息给管理员
故障转移:master挂掉了,会自动转移到salve上
配置中心:故障转移发生了,会通知客户端新的master地址
 
 
哨兵至少需要3个实例,来保证自己的健壮性,哨兵 + redis主从的部署架构,是不会保证数据零丢失的,只能保证redis集群的高可用性。
 
 
 
 
 
Redis的持久化机制
 
 
什么是Redis的雪崩和穿透
 
缓存雪崩: 缓存同一时间大面积的失效,所以,后面的请求都会落到数据库上,造成数据库短时间内承受大量请求而崩掉。A本身依赖于Redis,然后缓存失效。5000的qps,直接落到数据库上,数据库会直接崩溃。DBA开启数据库,然后又会被新的流量打死。
 
解决方法:
 
  • 事前:尽量保证整个 redis 集群的高可用性,发现机器宕机尽快补上。选择合适的内存淘汰策略。
  • 事中:本地ehcache缓存 + hystrix限流&降级,避免MySQL崩掉
  • 事后:利用 redis 持久化机制保存的数据尽快恢复缓存
 
 
ehcache是一个本地的小缓存,收到请求先查ehcache,里面没有再查redis和数据库。还可以用hystrix限流&降级,你可以设置每秒只允许2000个请求,假设来了5000个请求那么只有2000个请求可以通过限流组件进入数据库。剩余的3000个请求会走降级的组件,返回默认值或者一些提示。这样可以保证数据库不会崩溃。多点几次
 
 
 
缓存穿透
简介:一般是黑客故意去请求缓存中不存在的数据,导致所有的请求都落到数据库上,造成数据库短时间内承受大量请求而崩掉。
解决办法: 有很多种方法可以有效地解决缓存穿透问题,最常见的则是采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被 这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。另外也有一个更为简单粗暴的方法(我们采用的就是这种),如果一个查询返回的数据为空(不管是数 据不存在,还是系统故障),我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。
 
如何保证数据库和缓存双写一致性
 
问题:先修改数据库,再删除缓存,如果删除缓存失败了,那么会导致数据库中是新数据,缓存中是旧数据,数据出现不一致
 
解决思路
 
先删除缓存,再修改数据库。如果删除缓存成功了修改数据库失败了,那么数据库中是旧数据缓存中是空的,那么数据不会不一致。
 
 
数据发生了变更,先删除了缓存,然后要去修改数据库。此时还没修改,一个请求过来,去读缓存,发现缓存空了,去查询数据库,查到了修改前的旧数据,放到了缓存中。数据变更的程序完成了数据库的修改。导致了双写不一致
 
更新数据的时候根据数据的唯一标识,比如说商品ID,jvm会维护一个内部队列。商品Id对内存队列的个数取模,然后分发到不同的内存队列中,一个内存队列对应一个工作线程。一个队列的操作是按顺序执行的,比如说我们刚才的情况,它会先删除缓存然后修改数据库,最后才会执行读操作。
 
 
 
 
 

posted on 2019-08-13 15:32  将图南  阅读(223)  评论(0编辑  收藏  举报