redis周记
- redis持久化是用来做故障恢复和备份的,RDB和AOF,rdb是每隔xx时间生成一个完整的内存快照,aof是追加写命令到文件中,当大到一定程度
会进行rewrite操作,会基于当前最新的内存重新生成aof,重启后会优先用aof恢复数据。rdb适合做冷备,而且恢复数据比较快,而且写的时候
基于内存,比较快,只有一定时间才会写磁盘,不过丢数据可能比较多。aof一般每隔一秒就会从oscache刷一次,执行fsync操作,可以两种都用
- rdb的save可以配置多个,每隔xx有xx个改动就生成rdb快照 默认打开,900 1/300 10/60 10000
- 默认 关闭 appendonly设置yes打开
- rdb的内存快照和aof的重写不会同时发生,如果aof数据缺失,不会去读rdb的
- redis企业级容灾恢复,一般会写一个shell脚本,定时生成一个rdb快照,比如每小时生成,每天生成,小时级保存最新48小时,天级别保存1个月等
最后每天生成一分快照扔到云服务器,一旦发生数据被清空,可以即时恢复。比如先删除aof防止重启后自动按空的加载到内存,顺便关闭aof方式,先确定加载到内存后
再热修改启动aof。
- redis依靠主从架构实现读写分离可以提高读请求,写数据到主节点,异步复制到备份节点,提高吞吐量,主节点一定开启备份,否则有可能把脏数据同步其他节点
- 从节点第一次连接主节点(或断开很久)会进行一次全量复制,一般就是主节点生成一分rdb快照到从节点,落磁盘(也可以设置直接基于内存),再读回内存,
然后把复制期间新生成的同步过去,以后就直接内存一条一条的复制,复制支持断点续传,从节点不会过期key,如果主节点过期了或者淘汰了,会模拟一个del命令给从节点
- 完整复制流程 1-slave启动,仅保存master node信息,包括host和ip,但是复制还没开始 2-定时任务扫描到可以连接master了,就建立连接 3 如果第一次就全量复制 4异步写
- 数据同步核心机制:1-master和salve都会维护一个offset 告诉对方自己发送到哪了 2-backlog master有一个,默认1M,复制数据的时候也会在这同步写一份
3-master run id 如果master节点重启或者改了其他,run id发生变化,slave感知到会进行同步
- 全量复制 master将rdb文件复制的时候如果超过60秒会失败,如果数据有几G有可能失败,所以可以适当修改大点,复制期间新生成的命令放在缓冲区,如果持续超过64M
或者一次超过256M,也会复制失败 ,从节点接收到rdb后,清空自己旧数据开始复制,不过不中断,用旧数据代替
- 增量复制 全量数据复制中断,再次连接会触发增量复制,master从自己的logback获取部分丢失数据发送给从节点,是从slave发送的offset获取数据
- 主从节点会发送心跳,master默认每10s发送,从节点每1秒发送
- master接收到写命令,先在内部写入,再异步发送给master
- 为保证高可用性,要有哨兵集群,设置quorum=1和majority=2(意思是哨兵集群有1个认为宕机了就要切换,但必须有2个哨兵活着才行,5的majority是3),所以至少
要有3台哨兵集群
- 主备之间切换的时候可能会丢失数据,如异步复制,集群脑裂,可以通过设置min-slaves-to-write 1 min-salves-max-lag 10,至少有一个salve跟自己保持连接,并且
差异的数据不能超过10s,否则就会拒绝client的请求 脑裂:某个master由于网络分区或者其他原因,跟哨兵集群连接不上,导致重新选举,但是他跟client的数据保持连接
- 哨兵集群相互发现是通过redis的pub/sub系统实现,每个哨兵都会从_sentinel_:hello这个channel发送一个消息,其他哨兵消费,每隔两秒,每个哨兵都会发送,内容包括自己的
ip,host,runid和对master的监控配置,哨兵也会负责纠正slave的配置,让它们连接到正确的master上
- slave/master选举算法 先过滤那些超时10倍的slave 再排序,先按优先级,可配置,越低优先级越高,再看offset,越靠后越优先,再用runid最小的
- 如果要对redis横向扩容,就要多节点存储数据,可以用redis cluster模式,常见取模算法,hash算法(对机器取模,不推荐,会导致大量数据缓存失效),一致性hash算法
(hash环,顺时针走,加多个虚拟节点,解决缓存热点问题)hash slot算法(默认,对16384取模,每个节点会持有部分hash slot,如果节点挂掉就移动hash slot)
- redis cluster对读写分离支持不好,一般slave节点就是为了主从切换,如果要动态扩容节点,先增加节点,还得同步其他master数据到新节点上,删除节点需要先把数据移除到其他节点
- 可以给集群加冗余的slave,假如有3主4从,多出的一个从节点会在其他从节点挂了的时候可以切换成他的salve,所以可以冗余几个节点做应急,防止节点挂掉
- redis在fork子进程进行rdb备份的时候,一般1个g数据需要20ms左右,如果内存过多,可能会出现fork时间过久,建议主进程内存不超过10g
- 数据库缓存双写一致性保证:简单版本就是更新时候先删除缓存,再更新数据库,但是有可能出现刚清除缓存,正在修改数据库的时候,有读请求过来,拿到了脏数据
此时就会出现不一致。解决方案就是用内存队列,比如先初始化10个内存队列,再启动10个线程监听,线程就不断的循环,一直读内存队列的请求
判断如果是写缓存,那就正常处理,如果是读缓存,就先看是不是有已经存在的写缓存,如果有就放到后面排队,如果已经有写和读缓存了,剩下的读缓存
就没必要了。当写请求进来的时候,异步丢给内存队列,读请求的时候,也丢到内存队列,但是hang200ms,每隔20ms就去查一下是不是已经被缓存了
如果200ms还是没有,就去数据库查一下,并且给个强制刷新的标记。这样就能保证读请求一定是在写请求之后完成,也就能保证一致性
- 三层缓存架构:nginx分两层,分发层和应用层,分发层用lua脚本负责路由,目的是相同产品id要到一个nginx机器上,应用层则负责用lua脚本判断,如果找不到就去调用缓存服务,拿
结果出来然后缓存到nginx上,可以设置10分钟过期,找到就直接返回。不过在写本地缓存时候有可能出现一致性问题,比如机器A在00秒查出来了缓存准备刷新,
但是nginx路由到下一台机器01秒上查询也是空,也准备去刷新缓存,一旦01秒机器先完成缓存刷新,00秒的机器此时有可能覆盖掉01秒的记录。所以可以引入
zk的分布式锁,先弄个监听的内存队列,一旦重建缓存,就扔到内存队列,然后在监听的内存队列处理,先看是否有其他消息在更新,如果有(分布式锁获取不到)
就循环继续判断,直到拿到锁,看一下更新时间是不是跟当前时间有冲突,正常就处理,不正常就丢弃,最后达到一种效果,重建缓存的时候,一定是按顺序的
哪怕多台机器去创建缓存,也能保证先后顺序
- hadoop:分布式存储,分布式计算,每天把所有数据收集起来,第二天凌晨统一计算,离线处理,批处理
- storm:实时计算,然后把结果存储,nimbus,storm主节点,负责元数据入口,资源调度,实时计算作业的入口,每个实时计算程序都可以设置进程数和线程数
supervisor->worker>excutor>task
Topology,Spout,Bolt,Tuple,Stream
常见面试题
- redis为什么单线程下效率还这么高:文件事件处理器是单线程的 ,基于内存,单线程减少上下文切换
- redis的io多路复用:redis内部存在一个多路复用器(单线程执行),监听客户端的连接,发现连接请求就压入队列,被文件事件分派器消费,并将对应的AE_Readable
和命令请求处理器绑定,当监听到socket的set请求时,会继续压入队列,由命令请求处理器存入数据,并且将他的AE_Writeable事件和命令回复处理器绑定
当有查询请求时会触发AE_Writeable事件,由命令回复处理器回复
- 内存过期策略:惰性删除--查的时候发现过期了就删除 定期删除--比如每隔100ms随机查询一批,过期了就删除
- 内存淘汰:内存满了就会淘汰一批数据,常见的是lru,删除最近最少使用的,也有满了就报错不让塞的,或者随机删除,或者设置了过期时间的移除最少使用的,更早时间的等
- 手写lru,比较简单的就是继承linkedhashmap,初始化设置最多缓存多少数据 主要是 super ((int) Math.ceil(size/0.75)+1,0.75f,true),这个true就会按照访问顺序最近访问的在头
- redis的并发竞争:用分布式锁,并且用时间戳判断,保证旧数据不会覆盖新数据
- redis数据结构 list常用来微博关注列表之类 set共同好友等 sortedset 排行榜
- redis的值超过设定大小时就不会去压缩了
- 主从复制流程:从节点会发送psync请求发送自己的节点信息,包括数据源,偏移量之类,主节点会判断如果不是自己发送的数据,就全量复制,否则就增量复制
默认异步复制
- 哨兵会从配置文件中,通过info命令,进行主从信息发现