Redis(四)——Redis主从同步与对象模型
Redis主从同步与对象模型
淘汰策略
当redis管理的内存达到了上限,想要继续操作但是没有了内存,此时会触发淘汰机制,可能会淘汰部分数据来写入新数据,但默认策略是不淘汰任何数据,返回错误给客户端
过期指令
expire
和pexpire
命令
expire [key] [time秒]
:设置key-value对在time秒后过期pexpire [key] [time毫秒]
:设置key-value对在time毫秒后过期ttl key / pttl key
:查看过期时间persist key
:移除过期命令,永久存在测试
淘汰策略
- 禁止淘汰,这是默认策略,不淘汰任何数据,继续写入会报错
- 对于使用了expire/pexpire的并且过期了的key
volatile-lru
:最长时间没使用volatile-lfu
:最少次数使用volatile-ttl
:最近要过期volatile-random
:随机- 对于所有的key
allkeys-lru
allkeys-lfu
allkeys-random
持久化
redis的数据全部在内存中,如果突然宕机,可能会丢失全部数据,因此需要持久化
持久化:将内存中的数据写入到磁盘中,再次启动时从磁盘中读取数据来恢复数据
背景
1
redis的数据保存在内存中,也就是用户态缓冲区中
当执行
fwrite()
或类似操作时,数据会先写入 C 库的用户态缓冲区,然后通过write()
系统调用进入内核的 page cache此时数据尚未真正写入磁盘
系统会在适当时机或通过显式调用
fsync(fd)
/sync()
,将 page cache 中的数据刷新到磁盘
如果是正常的关闭进程,系统会将内核态缓冲区中的数据写入到磁盘的,但是如果是机器断电等异常行为,用户态缓冲区和内核态缓冲区中的数据就可能会丢失
2、fork的写时复制
fork可以在父进程执行位置生成一个新的子进程,相当于对那一刻的父进程打了一个快照
理论上,fork会将父进程的整个地址空间,包括代码段、堆、栈、全局变量等都复制到子进程中,但是这样做显然浪费很多资源,包括时间、内存
写时复制核心思想:fork时不立即复制父进程的物理内存页,而是父子进程共享同一份物理内存;只有当一方要修改某个页时,才复制一份
写时复制的实现
- 复制页表而非直接复制内存
- 内核给子进程创建新的页表,但页表项指向与父进程相同的物理页
- 所有共享的页都被标记为 只读
- 同时,内核在内存管理结构中记录这些页的 共享计数
- 此时父子进程是共享同一份物理内存,都只能读不能写
- 当任意进程尝试写一块共享物理内存时
- 系统为该进程分配一个新的物理页,并将原页内容复制到新页中
- 修改该进程页表,让它指向新的页,并取消只读标志
- 从此之后,两进程各自拥有独立的那一页
redis的持久化方式
aof,append only file
aof会将redis执行的每一条命令追加到一个日志文件(appendonly.aof)中,redis重启时将重新执行这些命令来恢复数据特点
- 数据持久性强;
- 但是日志文件会很大,因为记录了很多冗余命令,如
set teacher 1
和set teacher 2
两条依次执行的命令都会被记录,但显然前者没什么必要;- 因为执行冗余命令,浪费时间多;
可选策略
always
:每次执行命令都会通过fsync
将命令写入磁盘,很慢但安全;every_sec(默认)
:每秒执行一次fsync
,并且是在其他线程中执行;no
:由操作系统来决定什么时候写入磁盘;
rbd,redis database snapshot
通过快照的方式将当前数据库中所有数据周期性写入磁盘,并记录到一个(dump.rdb)文件中
特点
- fork一个子进程,由子进程负责将数据写入文件,主进程继续提供服务;
- 性能好,子进程执行过程中主进程几乎不受影响,可以继续提供服务;
- 记录的文件会更小,rdb文件是压缩的二进制文件;
- 恢复数据时比aof方式快;
- 当redis中数据很多时,fork子进程会带来一定的开销;
aof-rewrite,借鉴了rdb,不再记录所有的命令,而是在后台通过遍历所有的键值对,重新生成一个可以生成当前数据的最小的命令集
过程
- redis fork出一个子进程;
- 子进程根据当前内存中的数据重新生成一个aof文件;
- 生成完毕后主进程将缓冲区中的新写入的命令追加进去;
- 替换旧的aof文件;
aof-rdb混用,aof重写时,可以选择先写入rdb格式的数据快照,再继续追加aof命令;也就是,文件的前半部分是rdb格式,后半部分是aof日志
优点是在恢复数据的速度上接近rdb,在数据安全性上接近aof
配置
redis持久化的相关配置
#Redis 的配置主要通过 redis.conf 文件来完成#在 redis.conf 中找到以下配置项: appendonly yes # 开启 AOF 持久化 appendfsync always # 每次写入都 fsync(最安全但性能最低) # appendfsync everysec # 每秒 fsync 一次(默认值,推荐) # appendfsync no # 不主动 fsync,依赖系统调度(性能好,安全性低)#AOF 重写相关配置: auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 64mb #意思是:当 AOF 文件大小是上一次 rewrite 后的 100%,且超过 64MB,就会自动触发重写(即瘦身优化)#RDB 持久化配置: save 900 1 # 900 秒内有 1 个 key 改变就触发快照 save 300 10 # 300 秒内有 10 个 key 改变就触发快照 save 60 10000 # 60 秒内有 10000 个 key 改变就触发快照# 若不需要 RDB 持久化可注释所有 save 行 # stop-writes-on-bgsave-error yes # RDB 异常时是否停止写入# 过期策略配置(maxmemory-policy) maxmemory 256mb # 设置内存上限,建议结合使用 maxmemory-policy allkeys-lru # 淘汰策略(默认是 noeviction) maxmemory-samples 5 # 随机选择几个 key 进行淘汰评估#启动 Redis 并应用配置 redis-server /etc/redis/redis.conf
高可用
主从复制
一个主节点可以有多个从节点,从节点主动从主节点复制数据,使得数据在多个节点间保持一致
特点
- 异步复制;
- 环形缓冲区主节点维护一个固定大小的缓冲区保存最近的写命令,用户断线后快速增量同步;
- 复制偏移量:主节点维护一个偏移量,表示同步的数据位置,用于比对是否需要全量(主从节点之间的数据全部重新同步的过程)或增量复制(主从节点之间同步断开期间 丢失的数据部分补充同步,无需全量);
- runid:每个redis示例有唯一运行id,复制过程中标识主节点身份;
- 只能解决单点故障;
哨兵模式
在主从复制的基础上,实现了自动故障转移和监控的机制,一个或多个哨兵进程持续监控redis主从节点的健康状态
功能:
- 监控:定期向主从节点发送ping,判断主从节点是否正常在线;
- 通知:如果发现节点异常,发出警报;
- 主节点发生故障时
- 多个哨兵节点会推选出一个领头烧饼;
- 该哨兵选择将某个从节点提升为新的主节点;
- 让其他从节点重新复制新的主节点;
- 客户端通过哨兵获取新的主节点的地址
特点:
- 故障自动转移、读写分离等;
- 部署很复杂,设置主从节点本身就复杂,部署哨兵后更加复杂,生产环境中也不实用;
cluster集群
cluster时redis官方提供的分布式集群方案,同时实现了数据分片和高可用性
工作原理
- 数据分片:整个数据库被分成16384个哈希槽,每个主节点负责一部分槽,槽的分配决定了key存放的位置;
- 节点结构:一个集群包含多个主节点,每个主节点负责部分数据槽,每个主节点还会有一个或多个从节点,用于备份数据防止故障;
- 自动故障转移:若某个主节点宕机,集群会自动从对应的从节点中选举新的主节点;
- 客户端访问:客户端可以通过连接任意节点访问,若访问的key不在连接的节点上,返回MOVED,客户端会重定向到正确的节点;
特点
- 去中心化:不止一个主节点;
- 水平扩展,数据自动分片,可扩展写性能;
配置cluster集群
创建文件夹
mkdir redis-cluster cd redis-cluter mkdir -p 7001 7002 7003 7004 7005 7006
创建配置文件
cd 7001 vim 7001.conf # 将下面内容写入conf文件中,路径需要修改 pidfile "/mnt/d/C++/VSCode/forInternship/0voice/linux_advanced/4.1/4.1.4/redis-cluster/7001/7001.pid" logfile "/mnt/d/C++/VSCode/forInternship/0voice/linux_advanced/4.1/4.1.4/redis-cluster/7001/7001.log" dir /mnt/d/C++/VSCode/forInternship/0voice/linux_advanced/4.1/4.1.4/redis-cluster/7001/ port 7001 daemonize yes cluster-enabled yes cluster-config-file nodes-7001.conf cluster-node-timeout 15000 # 之后拷贝到其他文件夹下并对应修改内容 cp 7001/7001.conf 7002/7002.conf cp 7001/7001.conf 7003/7003.conf cp 7001/7001.conf 7004/7004.conf cp 7001/7001.conf 7005/7005.conf cp 7001/7001.conf 7006/7006.conf
得到
回到redis-cluster文件夹下创建
start.sh
文件vim start.sh # 文件中写入下面内容 #!/bin/bash redis-server 7001/7001.conf redis-server 7002/7002.conf redis-server 7003/7003.conf redis-server 7004/7004.conf redis-server 7005/7005.conf redis-server 7006/7006.conf # 给文件追加权限 chmod +x start.sh
关闭原本的redis-server并启动start文件
chmod +x start.sh
得到
测试cluster集群
通过下面命令启动redis
redis-cli --cluster create 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 127.0.0.1:7006 --cluster-replicas 1
得到
可以发现,一共有三个主节点,分别对应7001、7002、7003,而7004、7005、7006分别成为了三个主节点的从节点
从任意节点连接redis并操作redis