redis学习总结

redis数据结构原理
待整理~

redis持久化
RDB持久化
1.执行流程

  • 父进程执行fork操作创建子进程,这个过程中父进程是阻塞的,Redis不能执行来自客户端的任何命令。
  • 子进程创建RDB文件,根据父进程内存快照生成临时快照文件,完成后对原有文件进行原子替换,替换后子进程信号通知主进程

2.rdb自动持久化配置:
时间策略要按照实际情况配置多条,数据的存储时不均匀的,高峰期短时间间隔要存一次,低峰期长时间间隔要存一次。

# 文件名称
dbfilename dump.rdb

# 时间策略
save 900 1
save 300 10
save 60 10000

# 文件保存路径
dir /etc/redis/data/

# 如果持久化出错,主进程是否停止写入
stop-writes-on-bgsave-error yes

# 是否压缩
rdbcompression yes

# 导入时是否检查
rdbchecksum yes

3.rdb策略将内存中的数据生成快照保存到磁盘,是全量存储,内存大的话会比较耗时,大量磁盘io。
4.持久化的触发方式:

  • save命令:client向server发送save命令,同步阻塞。
  • bgsave命令:server中执行命令,异步子进程执行。

自动持久化,通过save配置项完成。
5.rdb文件恢复:如果开启了aof,则不生效。如果没开启aof,则寻找dir配置下的rdb文件。

AOF持久化

1.aof同步步骤:

  • Redis收到写命令后首先会追加到AOF缓冲区aof_buf(write)
  • 通过一定时机(配置决定),调用系统函数 fsync() 把AOF缓冲区的数据刷到磁盘

2.落盘时机配置(fsync):

  • always: 命令写入aof缓冲区后立即调用系统fsync操作同步到AOF文件,数据绝对安全。
  • no:有操作系统决定何时调用fsync()
  • everysec:每秒调fsync()一次。 若损失数据只损失1秒,对于大多数系统来说足够了。

3.文件重写rewrite

  • 由父进程fork子进程进行重写
  • 使用写时重写技术,在重写完成期间,Redis的写命令同时追加到aof_buf和aof_rewirte_buf两个缓冲区。
  • 重写文件完成后,将aof_rewirte_buf数据输入新文件
  • 用新aof文件替换老aof文件。

redis集群三种模式
主从模式(实现主从分离,提高吞吐,多机备份)

从数据库一般都是只读的,并且接收主数据库同步过来的数据
要点:

  1. 主从复制还是哨兵和集群能够实施的基础,主从复制是Redis高可用的基础。
  2. 再看CAP,由于redis复制是异步复制,会导致短时的数据不一致,所以无法满足一致性C,但可以保证当网络分区发生时,各个节点依旧可用。redis主从模式是CP,而不是AP。redis会使用最终一致性策略,保证主从同步数据一致。
  3. 主从复制实现原理:

6大步骤: 1. 设置主节点ip及端口、2. 建立套接字socket、 3. 发送ping命令、 4. 权限验证 、 5. 同步 6. 命令传播
设置主节点ip及端口: 使用slaveof命令,可配置文件使用、可命令行使用
复制阶段:从节点向主节点发送psync或sync命令(2.8版本之前),可以实现全量复制与增量复制
命令传播:当通过复制阶段后,主从节点进入命令传播阶段,主节点将自己执行的写命令发送给从节点,从节点接收命令并执行,从而保证主从节点数据的一致性。命令传播是异步的,主节点并不会等从节点同步执行完命令,这样会导致“延迟不一致”现象。

  • 延迟不一致现象:网络波动、写命令频率过高,会导致数据延迟不一致。同时,repl-disable-tcp-nodelay配置也会影响,设置为yes,会将代发数据合并发送,延迟大概40ms(取决于系统),如果设置为no,写命令实时发送同步。
  • 全量复制和部分复制

1.全量复制:用于初次复制或其他无法进行部分复制的情况,将主节点中的所有数据都发送给从节点,是一个非常重型的操作
2.部分复制:用于网络中断等情况后的复制,只同步网络断开期间的缓存写数据,如果网络中断时间过长,导致主节点没有能够完整地保存中断期间执行的写命令,则无法进行部分复制,仍使用全量复制。

  • 全量复制原理剖析:

fork子进程,开始执行bgsave,生成RDB文件,同时开辟缓冲区记录从现在开始的写命令。2. 将rdb文件传输给从服务器,从服务器执行完毕后,主节点将缓冲区写数据同步到从节点,保证最终一致性。3. 如果从节点开启了AOF,会触发bgrewriteof,从而保证从节点的aof文件最新。

  • 部分复制原理剖析:

1.复制偏移量:与mysql类似,主从节点各自维护offset字段,不一致则复制
2.复制积压缓冲区:底层结构是定长、先进先出FIFO的队列,可以repl-backlog-size参数设置大小。 当主从节点offset的差距过大超过缓冲区长度时,将无法执行部分复制,只能执行全量复制。
3.服务器运行id(runid):每个从节点都存储着主节点同步下来的runid,当网络分区发生时,重连后slave节点会判断同步的runid是否存在,如果存在优先考虑增量复制,如果不存在,则全量复制。

  • psync命令复制原理:主节点收到psync命令后,进行判断,如果命令为psync命令,则执行全量复制。如果收到的为psync
    {runid} {offset}命令,则执行增量复制。执行增量复制过程中如果offset差超过了buffer,则执行全量复制。
  • 心跳检测机制及其作用:
  • 检测主从链接 2. 辅助实现min-slaves选项 3. 检测命令丢失,保证数据一致
    通过判断在线的从节点数量,实现min-slaves。
    min-slaves概念,保证高可用:
    未达到下面两个条件时,写操作就不会被执行
    min-slaves-to-write 3 #最少包含的从服务器
    min-slaves-max-lag 10 #延迟值
    心跳会返回offset信息,通过offset判断从节点的数据同步是否及时,不及时则通过offset补发数据
  • 主从复制超时原理:断开连接后会尝试重连。

主节点判断超时:每秒1次调用复制定时函数replicationCron(),如果时间大于到上次REPLCONF ACK的时长,则断开连接,释放资源(缓冲器、连接、带宽),超时时间由参数repl-timeout值控制。
从节点判断超时:主要也是由repl-timeout参数控制。1. 连接建立阶段,若大于时间则断开连接。 2. 全量复制阶段:如果收到rdb的时间超时,则断开连接。 3. 命令传播阶段:如果收到主节点ping的时间过长,超过timeout,则断开连接。
如果rdb文件过大,会导致一直同步失败,会无线重连,应适当调大timeout值

  • 主库挂了发生什么?

不影响slave的读,但redis不再提供写服务,master重启后redis将重新对外提供写服务。
如果slave-serve-stale-data参数设置为yes,主节点挂掉后从节点可以继续提供服务,如果设置为no,主节点挂掉后从节点不再提供读数据服务,仅提供info、slaveof等少量命令。在强一致场景需要考虑设置为no,如分布式锁,如果主节点挂掉,数据没来得及同步从节点,会导致从节点读不到,锁失效。
重启master节点需要保证rdb文件或者aof文件是最新。

  • redis如何保证主从服务器连接正常且数据最终一致?

命令传播阶段,从服务器会利用心跳检测机制定时的向主服务发送消息。

  • 如果提高数据实时一致性?

优化主从节点之间的网络环境(如在同机房部署);监控主从节点延迟(通过offset)判断,如果从节点延迟过大,通知应用不再通过该从节点读取数据;

  • java客户端连接redis如何实现读写分离,读负载均衡?

常见的客户端有jredis,lettuce,redission。以lettuce为例,可以使用

StatefulRedisMasterSlaveConnection connection = MasterSlave.connect(redisClient, new Utf8StringCodec(), redisURI);
connection.setReadFrom(ReadFrom.NEAREST);
// MASTER 主读
// SLAVE 从读
// MASTER_PREFERRED 优先master -> slave
// SLAVE_PREFERRED 优先slave -> master
// NEAREST 最近节点读
// 实现很简单,重写ReadFrom的select方法,自带的方法均无法实现负载均衡
static final class ReadFromSlavePreferred extends ReadFrom {
 
        @Override
        public List<RedisNodeDescription> select(Nodes nodes) {
 
            List<RedisNodeDescription> result = new ArrayList<>(nodes.getNodes().size());
            //优先添加slave节点
            for (RedisNodeDescription node : nodes) {
                if (node.getRole() == RedisInstance.Role.SLAVE) {
                    result.add(node);
                }
            }
            //最后添加master节点
            for (RedisNodeDescription node : nodes) {
                if (node.getRole() == RedisInstance.Role.MASTER) {
                    result.add(node);
                }
            }
            return result;
        }

// 自定义负载均衡
@Bean(destroyMethod = "close")
   StatefulRedisMasterSlaveConnection<String, String> statefulRedisMasterSlaveConnection(RedisClient redisClient, RedisURI redisURI) {
       StatefulRedisMasterSlaveConnection connection = MasterSlave.connect(redisClient, new Utf8StringCodec(), redisURI);
       connection.setReadFrom(new ReadFrom() {
           @Override
           public List<RedisNodeDescription> select(Nodes nodes) {
               List<RedisNodeDescription> list = nodes.getNodes();
               Collections.shuffle(list);
               return list;
           }
       });
       return connection;
   }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值