具体实现地址本篇文件是基于网络上知识的整合
redis 是一个基于内存的高性能 key-value数据库
支持丰富的数据类型(String,List , Set ,Sorted Set,Hash )
redis中的单个value的存储限制是1G,比 Memcached的1MB要强大太多
哎呀,还是以问答的方式来写这篇文章吧!
1.redis有什么优缺点
redis是内存数据库,所以当数据量达到一定程度的时候,单机版的必然是其瓶颈所在,这个时候我们就需要引入主从复制方案,用了主从复制之后我们就会发现,主库值负责读写,从库负责读,数据量达到千万级的时候写的库还是扛不住,所以就更加深入的使用了集群。
2.redis的线程模型
redis内部使用的是文件事件处理器方式(file event handler)它是一个单线程的,所以redis也是单线程模型的关于这个文件事件处理器方式可参照下图
此图形象的标出了redis存储原理
- 客户端 socket01 向 redis 的 server socket 请求建立连接,此时 server socket 会产生一个
AE_READABLE
事件,IO 多路复用程序监听到 server socket 产生的事件后,将该事件压入队列中。文件事件分派器从队列中获取该事件,交给连接应答处理器
。连接应答处理器会创建一个能与客户端通信的 socket01,并将该 socket01 的AE_READABLE
事件与命令请求处理器关联。 - 假设此时客户端发送了一个
set key value
请求,此时 redis 中的 socket01 会产生AE_READABLE
事件,IO 多路复用程序将事件压入队列,此时事件分派器从队列中获取到该事件,由于前面 socket01 的AE_READABLE
事件已经与命令请求处理器关联,因此事件分派器将事件交给命令请求处理器来处理。命令请求处理器读取 socket01 的key value
并在自己内存中完成key value
的设置。操作完成后,它会将 socket01 的AE_WRITABLE
事件与令回复处理器关联。 - 如果此时客户端准备好接收返回结果了,那么 redis 中的 socket01 会产生一个
AE_WRITABLE
事件,同样压入队列中,事件分派器找到相关联的命令回复处理器,由命令回复处理器对 socket01 输入本次操作的一个结果,比如ok
,之后解除 socket01 的AE_WRITABLE
事件与命令回复处理器的关联。
很简单,多读几篇,就可理解。
3.为什么redis单线程效率也能那么高
3.1.纯内存操作,数据什么的都存在服务器内存中(使用的时候要注意设置服务器可分不配给redis的内存大小,并且根据业务使用持久化机制)
3.2.非阻塞的IO多路复用机制
3.3.单线程避免了多线程情况下的上下文切换问题(内部使用了队列技术,避免了传统DB控制串行的开销)
4.redis有几种持久化方式
4.1RDB
rdb是全量持久化,是在配置文件中指定持久化的间隔时间,然后将内存中的数据集快照写入磁盘,实际操作是fork一个子进程然后将数据集写入一个临时磁盘,字后覆盖掉以前的数据集文件。
优点,可以灵活的设置备份频率和周期因为是自己设置的,并且因为备份的一个数据集文件,所以当redis宕机的时候很容易就可以备份,内部使用了一个子进程所以在持久化的时候可以保证redis的高性能。恢复数据比AOF要快
缺点 :因为rdb是以时间为单位存储的,比如每5分钟写一次,那么在第4分钟59秒的时候redis服务器宕机了那么在这4分多钟里面的数据就会丢失。严重还是影响蛮大的。
4.2AOF
aof可以带来更高的数据安全性,aof中有3中同步策略,(1.每秒同步 2.没执行一个修改命令就同步3.不同步)
每秒同步是异步执行了,效率高但如果redis宕机了那么那一秒的数据就丢了
每修改同步。redis中每次有写的操作,都会同步到磁盘,效率比上面的每秒同步要低一些,但是极度安全
持久化的时候采用的是append的方式,由此可见恢复策略做的比较好,不会出现持久化文件错乱问题,并且这个写入磁盘的效率是非常之高,没有磁盘寻址操作(kafka的消息存储也是这个干的,追加写的形式)
如果日志过大,Redis可以自动启用 rewrite 机制。即使出现后台重写操作,也不会影响客户端的读写。因为在 rewrite log 的时候,会对其中的指令进行压缩,创建出一份需要恢复数据的最小日志出来。
缺点
同数据量下aof文件比rdb文件要大的多,因为它记录的是所有的写指令,并且恢复速度也慢一点
总结:使用的时候可以同时开启两种持久化方式,利用aof来保证数据不丢失,在aof无法使用的时候,在用reb的备份文件做替补恢复,在两种模式都开启的情况下,默认采取aof来进行数据恢复
AOF设计实现
RDB设计实现
5.redis有几种数据过期策略
5.1被动删除:当读/写 一个过期的key时,会触发惰性删除策略,直接删除这个key
5.2主动删除由于惰性策略无法保证冷数据已经被删除,所以redis会定期主动删除一些已过期的key,或者当内存达到为redis服务器设置的最大内存的时候,会主动的删除一些数据(触发数据淘汰策略)
6.什么是redis的数据淘汰策略
- volatile-lru
- volatile-ttl
- volatile-random
- allkeys-lru
- allkeys-random
- no-enviction
7. MySQL 里有 2000w 数据,Redis 中只存 20w 的数据,如何保证 Redis 中的数据都是热点数据?
设置过期策略为volatile-lru 或者allkeys-lru 这样就会删除不常用的,留下来的就是热点数据了。
8.redis回收进程工作原理
1.在写入redis之前查看是已经达到最大内存了,如果达到了就走数据淘汰策略,然后在写入
9.redis如何解决缓存雪崩,缓存穿透,缓存击穿
缓存雪崩呢就表示 在某个时间段设置了过期时间的key都一起失效了,那么db肯定扛不住那么大的并发量,可能会崩溃,所以我们在设置一些热点key的时候,要尽量设置不同的过期时间,以避免在高并发的情况下缓存雪崩问题
缓存穿透呢 从字面上理解就是因为每次做查询请求的时候无法从缓存中查询,从而直接将请请求落入DB,刚好db中也没这条数据,那么返回的就是null,就不会放入缓存中,周而复始知道db炸裂。 这种方案简单粗暴的解决方式就是当db中查询为null的时候自己给个设置过期时间 的默认值,从而解决掉这种查询直接到达db的频率
缓存击穿,某个热点key突然过期了,那么必然会导致,大量的请求入db那么db就会扛不住压力,凉凉。解决方案设置锁,如果是分布式系统就设置分布式锁。
10.redis如何实现分布式锁呢
实际上要理解的原理就是 设置key的过期时间,然后有请求到来的时候,去redis写入这个key,如果这个key已经被写入redis说明此锁已经被占用了,那么就循环就如等待,上一个占用锁的人,在处理完逻辑后会释放掉锁,那么下一个人就可以获取这个锁了然后执行自己的逻辑,总结来就是分布式系统下的强制串行操作。
11.redis使用场景
1数据缓存(不做过多介绍)
2.手机验证码多长时间过期(可通过设置key的过期时间实现)
3.积分排行(设置key的过期时间,然后定时做统计)
4.计算器(单线程计数,不考虑并发情况下的数据不一致问题)
5.限流(通过设置某个ip的在1分钟内访问次数,来达到限流效果)
6.消息队列
7.分布式锁
8.热门列表(根据访问次数来进行统计)
9.限制登录次数(如果密码错了就计算1次,设置一个过期时间,连错5次就锁定此账号)
10.唯一登录,在系统登录成功后记录这个账号信息,如果此账号在其他地方又登录,就清除某个token信息
11.token信息保存,刚好对应sprong-session中的核心处理
12.后续关于
主从复制的原理和实现
哨兵机制的原理和实现
redis事务的原理和实现
发布订阅机制的原理和实现
rdb和aof和实现
以及几个使用场景的实现
https://github.com/LxyTe/redis
13记录一点关于redis和db的数据一致性问题。
这个问题读者应该在面试的时候都会被问题,其实这个已经涉及到了两个数据库了,已经算作分布式事务的领域了。下面来说下解决方案。
方案一 没有使用MQ的系统(并发写量小的系统 )
先更新数据库,数据库更新完成之后,更新缓存(直接将结果给缓存不在查询db)。(直接更新,不是删除),缺点,数据库如若成功,redis更新失败,数据就不一致了。
方案二 使用了MQ的系统
先更新数据库,数据库更新成功返回状态,然后把更新redis的操作使用MQ异步发出,然后使用MQ的消费者机制来保证一定会消费,从而避免由redis更新失败带来 数据不一致。(直接将结果给缓存不在查询db)
方案三(并发量特别大的情况(这里的并发只的是并发写。),如果一个key的修改频率大于查询,那么就要考虑这个key是否 适合缓存)
这个时候使用方案二会出现一个弊端,那就是在你使用MQ消费的时候是肯定存在延迟的,这个时候如果db的值已经改变那么就会造成数据不一致,那么这个时候第二步的做法就可以修改为 消息到达mq只有,从db中查询一次,然后将结果重新放在redis中
方案四 没有使用MQ,系统并发量写太大,但是并发读不是很大,因为删缓存,并发读又太大那么容易造成缓存穿透)
那么可以直接使用先更新数据库,然后删除缓存(等缓存使用的时候,从db中查询,按需加载)
方案五 并发写很大,并发读也很大(秒杀系统(下单,那么就要减库存,下单前要判断是否还与库存))
此种解决方案的核心点是限流,通过各种限流将并发量减小,如果达到db能承受的范围,在达到db能承受的范围之后,将请求下放的数据库层,然后先减去库存,然后修改成功,在修改redis的数值(实际上的做法是先在redis层做限流,然后减去redis的库存,然后再到db去修改。那么这个时候就不用管redis的库存了。)
方案六 基于canal订阅mysql master的binlog,然后进行写入redis,可以实现最小程度的数据不一致。也是最佳方案,不过成本较高。
总结:redis和db的数据一致性在不同的情景下有不同的做法,上面的六种场景第1种是用的最多的,如果对数据一致性比较重视,那么可以用第二种,第三种的使用场景适用于少量写,大量读,情况下,数据的相对一致性。
分布式系统下的数据一致性其实是可以有一点不一致的(据说12306那种级别的网站,也是在每天晚上的时候,做一次数据同步,将db中的数据,重新刷到缓存中,为什么要重新呢,这里就不多解释了吧,肯定是db和缓存数据不一致呀),因为分布式系统访问的时候肯定会出现网络延迟,有网络延迟,肯定会出现数据不一致,以前有个同学和我说过, 如果你的系统连一点点数据一致性偏差都不能承受,那你还玩什么分布式系统呢。
14.如何解决rediskey倾斜问题
什么是热点key倾斜?就是在redis集群中某个节点一直承受这大量的查询操作,压力非常大,而其他节点压力都较小。这个时候需要解决这个压力太大的问题
解决思路有两种
1.在将一些特别热点的key直接放在客户端进行存储,设置过期时间,过期后从后台查询。
2.上面的方法可以解决热点key的倾斜问题,但是如果存储在前端的数据在同一个时间都过期了呢,那么还是有大量的请求到达redis的某一个节点,这个时候我们可以将一个热点key 复制出多分子key,每个子key的value值一样,查询的时候使用hash取模算法,将压力分配到不同的节点