一、https://blog.youkuaiyun.com/hjm4702192/article/details/80518856如老师一般的讲解:
Redis
一、基础:
- Redis选择
- 性能:缓存,读取速度快。
- 并发:频繁访问数据库会导致连接异常,redis作为缓冲,可以使得请求先访问redis,而不是直接访问数据库。
- redis缺点:
- 缓存与数据库双写一致性问题
- 缓存雪崩 问题
- 缓存击穿问题
- 缓存的并发竞争问题
- redis单线程为什么快:
- 纯内存操作
- 单线程操作,避免频繁切换上下文
- 采用了非阻塞I/O多路复用机制(队列,redis还提供了select ,epoll,evport,kqueue等多路复用函数库)
- redis数据类型及使用场景:
- String(get、set操作,value可以是String也可以是数字,一般做一些复杂的计数功能的缓存)
- Hash(value存储的是结构化的对象,比较方便的就是操作其中的某个字段。)
- set(因为set堆放的是一堆不重复值的集合,所以可以做全局去重的功能,不适用JVM自带的set是因为系统一般是集群部署,使用jvm自带的set比较麻烦,为了一个全局去重,再起一个公共服务太麻烦;还有就是利用交并差集等操作可计算共同喜好,全部喜好和自己独有的喜好等功能)
- list(使用list的数据结构可以做简单的消息队列功能。还可以利用lrange命令做基于redis的分页功能,性能极佳,用户体验好,生产着消费者场景,list可很好的完成排队,先进先出的原则)
- sorted set(多了一个权重score,集合中的元素能够按score进行排列,可以做排行榜应用,取topN操作)
- redis过期策略和内存淘汰机制
- 分析问题角度是:存10G但是内存只有5G需要删除5G的内存,怎么删除,数据已经设置过过期时间,但是到期内存占比依然很高是怎么回事
- redis采用的是定期删除+惰性删除策略。
- 不用定时删除的原因是,定是删除就是用一个定时器监视key,过期则自动删除,虽然内存及时释放了,但是十分消耗CPU资源,在大并发请求下,CPU要将时间应用在处理请就上,而不是删除可以,所以没有采用这一策略。
- 定期删除+惰性删除的工作模式:定期删除:redis每隔100ms检查,是否有过期的可以,有则删除,并且采取的是随机抽取。如果只采用定期删除策略,会导致很多可以到期没有删除,所以会有惰性删除派上用场,在获取某个可以的时候,redis会检查,这个key如果设置了过期时间那么是够过期了,如果过期了就会删除。
- 问题:如果定期删除没有删除可以,也没有即使请求可以,也局势说惰性喊出没有生效,这样redis的内存会越来越高,那么就应该采用内存淘汰机制。
- redis.conf文件中有一行配置,#maxmemory-policyvolatile-lru该配置就是配内存淘汰策略的。
- noeviction(不推荐):当内存不足以容纳写入数据时,新写入操作会报错。
- allkeys-lru:但内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key。
- allkeys-random(不推荐):但内存不足以容纳新写入数据时,在键空间中,随机移除某个key
- volatile-random(不推荐):但内存不足以容纳新写入的数据时,在设置了过期时间的键空间中,随机移除某个key
- volatile-lru(不推荐):但内存不足以容纳新写入的数据时,在设置了过期时间的键空间中,一处最近最少使用的key,这种情况一般是把redis既当缓存又做吃牛华存储的时候才用。
- volatile-ttl(不推荐):当内存不足以容纳新写入的数据时,在设置了过期时间的键空间中,有更早过期时间的key优先移除。
- 如果没有设置expire的key不满足prerequisites,那么volitile-*和noeviction基本上一致。
- redis和数据库双一致性问题:
- 一致性问题是分布式常见问题,还可以再分为最终一致性和强一致性。数据库和缓存双写,就必然会存在不一致的问题。如果对数据有强一致性要求,则不能放缓存。所做的一切只能保证最终一致性。从根本上来说智能降低不一致发生的概率,无法完全避免,因此,有强一致要求的数据不能放缓存。
- 采取正确更新策略;
- 先更新数据库
- 再删除缓存;
- 因为可能存在删除缓存失败的问题,同一个补偿措施即可,例如利用消息队列。
- 一致性问题是分布式常见问题,还可以再分为最终一致性和强一致性。数据库和缓存双写,就必然会存在不一致的问题。如果对数据有强一致性要求,则不能放缓存。所做的一切只能保证最终一致性。从根本上来说智能降低不一致发生的概率,无法完全避免,因此,有强一致要求的数据不能放缓存。
- 缓存穿透和雪崩应对策略:
- 缓存穿透:黑客故意去请求不在缓存中的数据,导致酥油的请求都堆到数据库上,从而使得数据库连接异常。
- 解决方案:利用互斥锁,缓存失效的时候先去获得锁,得到锁了,再去请求数据库,没有得到锁,则休眠一段时间重试。
- 采用异步更新策略,无论key是否取到值,都直接返回。Value值中维护一个缓存失效时间,缓存如果过期,异步起一个线程去读数据库,更新缓存。需要做缓存预热(项目启动前,现价在缓存)操作。
- 提供一个能迅速判断请求是狗有效的拦截机制,比如,利用布隆过滤器,内部维护一系列合法有效的可以,迅速判断出,请求所携带的key是否合法有效。如果不合法,则直接返回。
- 缓存雪崩:即为缓存在同一时间内大面积失效,这个时候又来了一波请求,结果请求都堆到数据库上,从而导致数据库连接异常。
- 给缓存的失效时间加一个随机值,避免集体失效。
- 使用互斥锁,但是该方案吞吐量明显下降了。
- 双缓存,设两个缓存,AB,缓存A的失效时间为20分钟,缓存B不设失效时间。自己租缓存预热操作,然后
- 从A读数据库,有则直接返回。
- A没有直接从B读,直接返回,并且一步启动一个更新线程。
- 更新线程同时更新缓存A、B
- 缓存穿透:黑客故意去请求不在缓存中的数据,导致酥油的请求都堆到数据库上,从而使得数据库连接异常。
- redis并发竞争问题如何解决:
- 如果对这个key操作,不要求顺序,则准备一个分布式锁,大家去抢锁,抢到锁就做set操作即可。
- 如果有顺序要求,则对于每个系统获取key的时候都设置一个时间戳,保证顺序一致性,
- 利用队列,穿行访问。
二、一些零散redis知识:
- redis是非关系型数据库;mysql是关系型数据库,oracle,db2,也是。
- redis中的数据版存储在内存中,持久化,这是它速度极快的原因;mysql中的数据存储在本地磁盘中,会进行持久化。
- redis实现分布式锁的命令:set(key,value,NX,EX,expiretime);
- redis和memcachned的区别:
- 性能对比:redis只使用单核,memcached使用多核,所以平均每一个核上redis在存储小数据时比memcached性能更高。而在100k以上的数据中,memcached性能要高于redis,虽然redis最近也在存储大数据的性能上进行优化,但是比起memcached的还是稍有逊色。
- 内存使用效率对比:使用简单的key-value存储的话,memcached的内存利用率更高,而如果redis采用hash结构来做key-value存储,由于其组合式的压缩,其内存利用率会高于memcached。另外,memcached使用预分配的内存池的方式,带来一定程度的空间浪费,并且在内存仍然很大空间时,新的数据也可能会被剔除,而redis使用现场申请内存的方式来存储数据,不会剔除任何非临时数据,Redis更适合作为存储而不是cache。
- redis支持服务器端的数据操作;redis相比memcached来说,拥有更多的数据结构和支持更丰富的数据操作,通常在memcached里,你需要将数据拿到客户端来进行类似的修改再set回去。这大大增加了网络I\O的次数和数据体积。在redis中,这些复杂的操作通常和一般的get/set一样高效,所以,如果需要缓存能够支持更复杂的结构和操作,那么redis是不错的选择。
- redis优势:
- memcached所有的值均是简单的字符串,redis作为其替代者,支持更为丰富的数据类型;
- Redis的访问速度比memcached快很多;
- Redis可以持久化其数据volatile
- Redis支持数据的备份,即:master-slave模式的数据备份;
- 使用底层模型不同,她们之间底层实现方式以及与客户端之间通信的应用协议不一样。Redis直接自己构建了VM机制,因为一般的系统调用系统函数的话会浪费一定的时间去移动和请求。
- value大小,redis最大可达到1GB,而memcached只有1MB。
- Redis持久化:
- RDB:可丢失数据,但是效率高,redis默认持久化方式
- AOF:不丢失数据。
- 为什么Redis设计成单线程模型:
- 因为redis是基于内存的操作,CPU不是redis的瓶颈,redis的瓶颈最有可能是机器内存的大小或者网络带宽。既然单线程容易实现,而且CPU不会成为瓶颈,那就顺理成章的采用单线程的方案了。
-
- 编程简单:相较于多线程的实现方式,单线程实现较为简单。
- 并发帮助:采用并发操作实现类似多线程的效果。
- 计算量小。Redis主要用于存储数据,CPU不是瓶颈,网络通常是瓶颈。如果应用程序设计正确。能够避免I/O阻塞,下称僵尸最后需要担心的问题。
- 部署简单,由于redis是单线程,故能部署在低端单核CPU机器上,redis利用队列技术将并发访问变成为串行访问,消除传统数据库穿行控制的开销。
- 单线程的好处:
- 代码更清晰,处理逻辑简单;
- 不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗;
- 因为是单线程,所以同一时刻只能有一个操作,所以耗时的命令会导致并发下降,不只是读并发,写并发也会下降,而单一线程也只能用到一个CPU核心,所以可以在同一个多核服务器中,启动多个实例,组成master-slave或者master-master形式,耗时的读命令,完全可以在slave进行。
- 单线程的好处:
-
- 因为redis是基于内存的操作,CPU不是redis的瓶颈,redis的瓶颈最有可能是机器内存的大小或者网络带宽。既然单线程容易实现,而且CPU不会成为瓶颈,那就顺理成章的采用单线程的方案了。
- redis快的原因:
- 完全基于内存
- Redis 将数据储存在内存里面,读写数据的时候都不会受到硬盘 I/O 速度的限制,所以速度极快。
- 连接数为10000时,QPS(每秒内查询次数可达90000)
- 数据结构简单,对数据操作也简单
- 采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗;
- 使用多路 I/O 复用模型,非阻塞IO。(避免了使用锁)
- 多路I/O复用模型是利用 select、poll、epoll 可以同时监察多个流的 I/O 事件的能力,在空闲的时候,会把当前线程阻塞掉,当有一个或多个流有 I/O 事件时,就从阻塞态中唤醒,于是程序就会轮询一遍所有的流(epoll 是只轮询那些真正发出了事件的流),并且只依次顺序的处理就绪的流,这种做法就避免了大量的无用操作。这里“多路”指的是多个网络连接,“复用”指的是复用同一个线程。采用多路 I/O 复用技术可以让单个线程高效的处理多个连接请求(尽量减少网络 IO 的时间消耗),且 Redis 在内存中操作数据的速度非常快,也就是说内存内的操作不会成为影响Redis性能的瓶颈。
- 主要由以上几点造就了Redis 具有很高的吞吐量
- Redis会有主从分离吗?
- 有,“从”负责备份,“主”负责查询,当主机宕机又恢复后会从从机里恢复数据;
- Redis 如何实现高可用?
- Redis 高可用技术是为了解决 redis 宕机后影响整个服务的一种架构方式.主要采用主从结构来实现高可用.(主从挂载)
- 高可用实现的方式有哨兵和集群 2 种方式.主要思想都是监听主节点的信息. 我们把这种监听方式称之为心跳机制.当在规定的时间内主节点没有响应,则判断主节点死亡.则会启动从节点代替主节点为用户提供服务.
- Redis 分片机制是将多 reids 看做一个整体,每个 redis 只负责一个固定的区域.进行数据的更新和维护.这种方式的实现主要是基于 hash 一致性算法.根据 key 值计算出 hash 的位置,最终将数据保存.并且这个 key 值指向管理该区域的点