java面试基础知识-Redis集群和持久化
首先要了解:分布式锁需要解决的问题
1.互斥性
2.安全性
3.死锁
4.容错
方法一:
SETNX key value:如果key不存在,则创建并赋值
时间复杂度O(1),返回值:设置成功返回1 失败返回0(locknx已经存在无法更改,操作是原子的)
在执行某段代码时,先尝试使用SETNX对某个key设值,如果成功则没有其他线程占用该资源,如果失败则等待到SETNX成功。
但是如果SETNX某个key,则这个key就长久存在,如何解决SETNX长期有效的问题?
EXPIRE key seconds
设置key的生存时间,当key过期时(生存时间为0)会被自动删除
代码逻辑(伪代码)如下:
EXPIRE缺点:原子性得不到满足(当setnx执行成功后挂掉,未来得及执行expire,会导致key一直被占用,其他线程永远无法执行)
方法二:
set key value [EX seconds] [PX milliseconds] [NX|XX]
EX seconds:设置键的过期时间为second秒
PX milliseconds:设置键的过期时间为 milliseconds毫秒
NX:只在键不存在时,才对键进行设置操作
XX:只在键已经存在时,才对键进行设置操作
SET操作成功完成时,返回OK,否则返回nil
如果有大量的key同时过期的时候,由于清除大量的key很耗时,会出现短暂的卡顿现象:在设置key的过期时间的时候,给每个key设置随机值来避免卡顿现象发生。
如何使用Redis做异步队列
一个消息产生只有一个消费者消费:
使用list作为队列,RPUSH生产消息,LPOP消费消息
缺点,不等待队列里有值以后再消费
弥补:
方法一:可以通过在应用层引用sleep机制去调用LPOP重试
方法二:BLPOP key[key…]timeout:阻塞直到队列有消息或者超时
缺点:只能供一个消费者消费
一个消息产生有多个消费者消费:
pub/sub:主题订阅者模式
发送者(pub)发送消息,订阅者(sub)接收消息
订阅者可以订阅任意数量的频道
两个客户端都订阅同一个频道,另外一个订阅其他频道
第四个客户端发布两次消息(监听当前频道的两个客户端收到了需要关心的消息,另外一个并没有收到)
对另外一个频道发送消息,可以看到只有第三个客户端收到消息:
pub/sub的缺点:消息发布是无状态的,无法保证可达,传输过程中可能丢失,对于发送者,发送以后就丢失,如果订阅者在发送前掉线,发送后重连是无法获取消息的,可以用kafka消息队列解决。
Redis如何做持久化?
第一种:RDB(快照)持久化:保存某个时间点的全量数据快照(特定间隔)
Redis.conf: 持久化策略
表示以RDB方式做持久化:
save:阻塞Redis的服务器进程,直到RDB文件被创建完毕
bgsave:Fork(派生)出一个子进程来创建RDB文件,不阻塞服务器进程,父进程继续处理命令,父进程轮询接收子进程备份完成等信号。使用后台方式保存RDB文件,会立刻发送OK。
自动触发RDB持久化的方式:
根据redis.conf配置里的SAVE m n定时触发(使用BGSAVE)
主从复制是,主节点自动触发
执行debug reload
执行shutdown且没有开启AOF持久化
BGSAVE原理:
系统调用fork()创建子进程,实现了Copy-on-write写时复制
Copy-on-write写时复制:如果有多个调用者同事要求相同资源(内存或磁盘上的数据存储),他们会共同获取相同的指针指向相同的资源,直到某个调用者试图修改资源的内容时,系统才会真正复制一份专用副本给该调用者,而其他调用者所见到的最初的资源仍然保持不变。(如果父进程要修改数据,会使用一个副本,而子进程持久化的内容,会保持fork时刻的内容,保证原子性,当子进程完成后,父进程的修改才会合并)
缺点:内存数据的全量同步,数据量大会由于I/O而严重影响性能
可能会因为Redis挂掉而丢失,从当前至最近一次快照期间的数据
第二种:AOF(Append-Only-File)持久化:保存写状态
记录下除了查询以外的所有变更数据库状态的指令
以append的形式追加保存到AOF文件中(增量)
开启:vim redis.conf-> /appendonly->将appendonly选项设置成yes
always:缓存区内容发生变化就即时的将缓存区的变化持久化到AOF文件
everysec:一秒钟一次(默认,安全性不错速度也快)
no:交给操作系统来决定
设置完成需要重启服务器
递增计数器,等操作会导致AOF文件不断增大,使用日志重写方式解决:
1.调用fock()创建一个子进程
2.子进程把新的AOF写到一个临时文件里,不依赖原来的AOF文件
3.主进程持续将新的变动同时写到内存和原来的AOF里
4.主进程获取子进程重写AOF的完成信号,往新的AOF文件同步增量变动
5.使用新的AOF文件替换掉旧的AOF文件
RDB和AOF文件共存情况下重启即可恢复Redis数据,恢复流程:
RDB和ADF的优缺点:
RDB优点:全量数据快照,文件小,恢复快
RDB缺点:无法保存最近一次快照之后的数据
AOF优点:可读性高,适合保存增量数据,数据不易丢失
AOF缺点:文件体积大,恢复时间长
第三种:RDB-AOF混合模式(redis4.0默认)
先以RDB方式从管道写全量数据再使用AOF方式从管道追加
BGSAVE做镜像全量持久化,AOF做增量持久化
redis重启时,会使用bgsave持久化文件构建内容,再使用AOF重放指令恢复状态
pipeline:
与Linux管道类似
#批量生成redis测试数据:
#1.Linux bash下面执行
for(i=1;i<=20000000;i++);do echo “set ki” >>/tmp/redisTest.txt;done;
#生成两千万条redis批量设置kv的语句(key=kn,value=vn)写入到/tmp目录下的RedisTest.txt文件中
#2.用vim去掉行尾的换行符(^m):
vim/tmp/redis.txt:setfileformat=dos #设置文件格式,通过这句话去掉每行结尾的^m符号
::wq#保存退出
#3.通过redis提供的管道–pipe形式,去跑redis,传入文件的指令批量灌数据,需要花10分钟左右
cat/tmp/redisTest,txt | 路径/redis-5.0.0/src/redis-cli -h 主机ip -p 端口号 --pipe
redis基于请求/响应模型,单个请求处理需要一一应答
如果需要执行多个命令,会多次调用系统I/O
pipeline批量执行指令,节省多次i/o往返时间
有顺序依赖的指令建议分批发送
redis的同步机制:
主从同步原理:
一个Master来用于写操作,若干个Salve用于读操作,Master和Salve就是多个独立redis server实例,定期的数据备份操作也是一个Salve操作,为了让其保持数据的最终一致性,不保证Master和Salve时时数据同步,但是一段时间后,他们的数据是趋于同步的,redis可以使用主从同步和从从同步,第一次同步时,主节点使用一次BGSAVE,并同时将后续修改记录到内存的buffer中,待完成后将RDB文件全量同步到从节点里,从节点接受完成后将RDB镜像加载到内存中,加载完成后通知主节点将期间的操作和增量数据同步到从节点进行重放。
全同步过程:
1.salve发送sync命令到Master
2.Master启动一个后台进程,将redis中的数据快照保存到文件中
3.Master将保存数据快照期间的写命令缓存起来
4.Master完成写操作后,将该文件发送到Salve
5.Salve使用新的AOF文件替换掉旧的AOF文件
6.Master将Salve替换期间收集的增量写命令发送给Salve端
增量同步过程:
1.Master接收到用户的操作指令,判断是否需要传播到Slave(增删改操作需要)
2.将操作记录追加到AOF文件
3.将该操作传播到其他Slave:1、对其主从库;2、往相应缓存写入指令
4.将缓存中的数据发送给Slave
Redis Sentinel(哨兵)
解决主从同步Master宕机后的主从切换问题:
1)监控:检查主从服务器是否运行正常
2)提醒:通过API向管理员或其他应用程序发送故障通知
3)自动故障迁移:主从切换(将从服务器设为主服务器)
留言协议Gossip
每个节点都随机的与对方通信,最终所有节点状态达成一致
种子节点定期随机像其他节点发送节点列表以及需要传播的消息
不保证信息一定会传递给所有节点,但是最终会趋于一致
Redis集群
如何从海量数据中快速找到所需?
分片:按照某规则去划分数据,分散存储在多个节点中
常规的按照哈希划分无法实现节点的动态增减
一致性哈希算法:对2^32去模,将哈希值空间组织成虚拟的圆环
将各个服务器使用哈希进行哈希变换,使用ip或者主机名作为关键字进行哈希,这样每台服务器设定他在哈希环上的位置,设运算过后位置如下:
接下来对数据key使用相同函数哈希计算出哈希值,并确定此数据在患上的位置,圆环顺时针行走,第一台遇到的服务器就是数据的目标存储服务器:
假设Node C 宕机:C的数据会被存到D服务器 不会影响别的数据,最小化有损
新增服务器Node X,也只有B到X之间的数据受到影响
hash环的数据倾斜问题:例如只有两台服务器,顺时针方向都离某个服务器较近,服务器分布不均匀
引入虚拟节点解决数据倾斜问题,例如上面的情况,可以为每个服务器设置两个虚拟节点,数据定位算法不变,可以解决数据倾斜问题,实际应用中一般设置虚拟节点为32或更大,可以相对均匀的分布,保证高可用性