zoukankan      html  css  js  c++  java
  • Redis设计与实现(三) 分布式和独立功能部分 事务 ,慢查询日志 和 监视 (本系列完结)

    Redis设计与实现

    第一部分 数据结构与对象

    Redis对象

    • 首先key value,key是固定的字符串对象,value可以是那5种中的一种,而那5种根据场景的不同,每种都有至少两种编码方式,也就是数据结构

    • 数据结构有linkedlist 双端链表

    • ziplist压缩列表

      • 这个用的太多了 以至于我有深刻的印象
    • skiplist

      • 跳跃表 类似于平衡树的作用 但是实现方式太友好了
    • raw

      • 都是sds simply dynamic string
    • embstr

      • 都是sds simply dynamic string 区别是这个更短喽 压缩过的 是连续的内存 所以速度比raw的快
    • hashtable

      • 两个表嘛ht[0]和ht[1]
      • 嗯 冲突就是在同一个哈希值组成链表 然后有一个负载因子 默认好像是1 进行rehash 然后rehasn的话会在另外一个大于当前数量number的最小的2的n次方那么大的扩容 扩容期间 服务器空闲就转移 当然 新插入的都是在这个新表里 hashtable数组ht[1] 然后全部拷到ht[1]后 就会把ht[0]换成ht[1] 然后h[1]又变成空
    • linkedlist

      • 就是一个简单的双链表
    • intset

      • int_8 int_16 int_32 int_64
      • 会保存编码方式
      • 一旦有东西需要往上提 比如从int_32到int_64了 这个过程是不可逆的 即使把64的都删了 也不会降编码回32了

    第八章 对象

    • 字符串

      • int

        • redis默认有0~9999的int字符串
        • 顺便说一下 整数都是先转字符串 然后用的时候再转回数字
      • raw

      • embstr

    • 列表

      • linkedlist
      • ziplist
    • 哈希对象

      • ziplist
      • dict 或者叫hashtable
    • 集合

      • intset
      • hashtable
    • 有序集合

      • ziplist

        • score key
      • 数据结构叫zset

        • skiplist

          • 子主题 1
        • dict

          • 子主题 1
        • 同时用这两个的原因是因为 既要单点查询的速度 也要范围查询的速度吧

    第二部分 单机数据库的实现

    第9章 数据库

    • 类型检查 命令多态

      • 先看key是不是符合那个命令的

        • 再看看编码方式是什么
    • 对象回收

      • 计数呗
    • 对象共享

      • 不共享包含字符串的对象

      • 对整数检查O(1)

      • 对字符串O(N)

      • 对象包含了多个值对象

        • O(N^2)
    • 空转时长

      • 有个lru时间 记录最后一次被程序访问的时间

    第10章 RDB持久化

    第11章 AOF持久化

    第12章 事件

    第13章 客户端

    第14章 服务器

    第三部分 多机数据库的实现

    第15章 复制

    • Redis的复制分为两步, 同步(sync)和传播(command propagate)

      • 在我理解无非一个是先获取一个服务器的快照
      • 另外一个是追赶式恢复
    • 1.同步

      • 客户端向服务器发送SYNC命令来完成

        • 1.从服务器向主服务器发送SYNC命令
        • 2.收到SYNC的master执行 BGSAVE命令, 在后台生成一个RDB文件, 并用一个缓冲区来记录现在开始的所有命令
        • 3.主服务器把RDB文件传给从服务器,从服务区更新至 主服务器执行BGSAVE的命令状态
        • 4.主服务器把缓冲区的所有命令发送给从服务器, 从服务器进行追赶式恢复
      • SYNC命令非常号资源,

        • 生成RDB文件需要CPU 内存, 磁盘I/O
        • 发送RDB文件需要网络带宽
        • 从节点载入的时候也无法处理请求
    • 2.命令传播

      • 无非就是主服务器执行命令 , 然后发送给 从服务器 来保持一致性
    • 旧版复制功能的缺陷

      • 每次都是把完整的RDB文件进行传输

        • 如果断线时间不是很长也要 全部复制一遍
        • 所有要引入部分复制(PSYNC)
    • 新版复制功能

      • 部分重同步的实现

        • 1.主服务器的复制偏移量(replication offset)

          • 主服务器和从服务器都维护一个偏移量

            • 每次主服务器向从服务器传播N个字节的数据,就把字节的偏移量加上N
            • 从服务器收到N个字节的数据, 也把自己的偏移量加上N
            • 如果二者偏移量不一样, 那就是数据不一致
        • 2.主服务器的复制挤压缓冲区(replication backlog)

          • 是一个固定长度的先进先出队列

            • 如果从服务器连接上主服务器的时候, 从服务器会把自己的偏移量发给服务器

              • 如果已经不在了

                • 执行完整的重同步
              • 如果这个偏移量还在复制积压缓冲区之内

                • 执行部分同步
            • 默认1MB

              • 一般设成= 2(冗余)* 平均重连时间*每秒写入命令的字节数
        • 3.服务器的运行ID

          • 主服务器会把自己的ID发送给客户端

            • 如果从服务器保存的运行ID和当前的主服务器发过来的一样, 说明之前连的就是这台, 尝试部分重同步
            • 如果不一样, 执行完整重同步
    • 复制功能的实现

      • 步骤1 设置主服务器的ip和端口

        • 加上俩机子服务器
        • 异步的, 返回ok, 然后后台执行复制
      • 步骤2 建立socket连接

        • 如果成功连接

          • 从服务器会被主服务器看成特殊的客户端

          • 对于普通的客户端来说

            • 从服务器还是个服务器
      • 步骤3 发送ping命令

        • 如果不成功会回到第2步的
      • 步骤4 身份验证

        • 主要是看主服务器和从服务器有没有设置密码, 常识
        • 失败就重试
        • 成功就准备复制
      • 步骤5 发送端口信息

        • 从服务器向主服务器发送从服务器的监听端口号
      • 步骤6 同步

        • 互为客户端
      • 步骤7 命令传播

        • 进入命令传播状态

          • 一直接受主服务器的写命令
    • 心跳检测

      • 命令传播阶段, 每1s ,从服务器会向主服务器发送replication ack [offset]

      • 作用

        • 1.检测主从服务器的连接状态

        • 2.辅助实现min-slaves选项

          • 比如min-slave-to-write 3
          • min-salves-max-lag 10
          • 如果从服务器少于3 , 拒绝写
          • 如果三个服务器延迟都大于等于10, 拒绝写的请求
        • 3.检测命令丢失

          • 如果主服务器发现落后于自己, 就会从复制挤压缓冲区中重新发给从服务器

    第16章 Sentinel哨兵

    • 现在我知道了,哨兵就是负责监视主服务器和从服务器 然后如果主服务器下线了 它会选出新的从服务器作为主服务器, 然后在原来的主服务器上线后把它降级成从服务器

    • 哨兵是Redis的高可用性解决方案

      • 由一个或多个哨兵组成的哨兵系统可用监视任意多的服务器
    • 启动并初始化Sentinel

      • 初始化服务器

      • 使用Sentinel专用代码

      • 初始化Sentinel状态

      • 初始化Sentinel状态的masters属性

      • 创建连接向主服务器的"异步"网络连接

        • 一个是命令连接

          • 用来发送命令 和 接受回复
        • 一个是 订阅连接

          • sentinel:hello频道
    • 获取主服务器信息

      • 每10s一次 发送INFO命令

        • 可以获取主服务器和 它的从服务器的信息
    • 获取从服务器信息

      • 会创建到从服务器的命令连接和订阅连接
    • 向主服务器和从服务器发送信息

      • 向__sentinel__:hello 频道发送信息 s_开头的是 sentinel本身的信息, m_开头的是 主服务器的信息
    • 接受来自主服务器和从服务器的频道信息

      • 如果有3个sentinel监视同一个服务器 , 三个sentinel都会收到 sentinelA发送的频道信息

      • 如果发现是自己的信息, 就丢弃

      • 如果发现是其他sentinel, 就更新信息, 也会保存其他sentinel

        • 然后会创建连向其他sentinel的命令连接
    • 检测主观下线状态

      • 默认情况,sentinel 会向 所有的 "命令连接" 发送PING命令(包括主服务器, 从服务器, 其他Sentinel

      • 有效回复

        • +PONG
        • -LOADING
        • -MASTERDOWN
      • 无效回复

        • 除那三个以外的
        • 或者无回复
      • 50000ms

        • 会用来判断 主服务器, 从服务器 ,以及所有监视主服务器的Sentinel的状态, 这些人 都是50s没回复, 本宝宝就认为你们下线了
    • 检测客观下线状态

      • 会问其他Sentinel,看他们是否认为下线了
      • 当认为下线的Sentinel达到一定数量, 我们会标记成客观下线状态
    • 选举领头Sentinel

      • 当Amaster下线后, 监视他的所有Sentinel会 选出一个领头Sentinel, 并由这个领头Sentinel来负责故障转移操作
      • 然后会在一个配置纪元里选出领头Sentinel
      • 选不出就继续选
      • Raft算法
    • 故障转移

      • 选出新的主服务器

        • 领头Sentinel向它选择的从服务器 发送SALVE no one, 然后每秒发一次INFO, 直到从slave变成 master
      • 修改从服务器的复制目标

        • 向其他的从服务器发送SLAVEOF命令
      • 将旧的主服务器变成从服务器

    第17章 集群

    • Redis集群是Redis提供的分布式数据库方案, 集群通过分片(sharding)来进行数据共享, 并提供复制和故障转移功能

    • 节点

      • 启动节点

        • 一个节点就是一个运行在集群模式下的Redis服务器

        • 一个节点会继续做所有单机模式中使用的服务器组件

          • 1,会继续使用文件事件处理器来处理命令请求和返回命令回复

          • 2,继续使用时间事件处理器来执行serverCron函数, 还会调用clusterCron函数

            • clusterCron函数负责执行在集群模式下的常规操作

              • 1,向其他节点发Gossip信息
              • 2,检查节点是否断线, 检查是否需要对下线节点进行自动故障转移等等
          • 3,继续数据库

          • 4, 继续RDB和AOF持久化模块

          • 5,继续使用发布/订阅模块

          • 6,继续使用复制模块

          • 7,继续使用Lua脚本

        • CLUSTER MEET命令的实现

          • 客户端发 CLUSTER MEET命令给节点A, 让节点A和节点B握手

            • 节点A发送MEET消息
            • 节点B发送PONG消息
            • 节点A发送PING消息
    • 槽指派

      • 集群通过分片的方式来保存 数据库的 键值对

        • 整个数据库被分为16384个槽, 只有这16384个槽都有节点处理 的时候, 集群才是上线状态的
      • 记录节点的槽指派信息

        • slots属性是一个二进制位数组(bit array)

          • 如果索引i上的二进制位的值位1, 表示处理槽i
          • 如果索引i上的二进制值位0 , 表示不处理i
        • 所以检查是否负责处理某个槽, 或者任命某个槽

          • 时间复杂度为O(1)
      • 传播节点的槽指派信息

        • 节点会向其他节点发送自己的slots数组
      • 记录集群所有槽的指派信息

        • clusterState.slots数组记录了所有 具体的槽 map到具体节点的信息
        • clusterNode记录了某个节点的槽指派信息
      • CLUSTER ADDSLOTS命令的实现

    • 在集群中执行命令

      • 客户端向节点发送数据库的命令时, 接受命令的节点会算出 key属于哪个槽 ,这个槽又是否属于自己

      • 计算键属于哪个槽

        • CLUSTER KEYSLOT

          • 可以看给定的key属于哪个槽
          • CRC16(key)& 16383
      • 判断槽是否由当前节点负责处理

        • 看hashtable是不是指向自己
      • MOVED错误

        • MOVED :
      • 节点数据库的实现

        • 会开一个跳跃表来保存 槽与键的 关系
        • 便于对属于某个或者某些槽的所有数据库键进行批量操作
    • 重新分片

      • 重新分片可以将任意数量 已经分派给A的 槽 改成 分派给B的

      • 可以在线进行, 并且可以继续处理命令请求

      • 重新分片的实现原理

        • 由Redis集群管理软件redis-trib负责进行的

        • 相当于一个controller

          • 对目标节点发送准备导入
          • 源节点准备迁移
          • 迁移
    • ASK错误

      • 具体步骤

        • 客户端 向源节点 发送关于key的命令

        • 在 源上, 执行命令

        • 不在源上 正在迁移?

          • 在迁移, 返回ASK错误
          • 不在迁移 返回key不存在
      • ASK错误和MOVED错误的区别

        • 都会导致客户端转向
        • MOVED 以后都会发给新的
        • ASK 只是临时的
    • 复制与故障转移

      • Redis中的节点分为主节点(master)和从节点(slave), 主节点用于处理槽 , 从节点用于复制某个主节点

      • 设置从节点

        • CLUSTER REPLICATE <node_id>
      • 故障检测

        • 子主题 1
      • 选举新的主节点

        • Raft算法
    • 消息

      • 主要有5种消息

        • MEET
        • PING
        • PONG
        • FAIL
        • PUBLISH

    第四部分 独立功能的实现

    第18章 发布与订阅

    • 发布与订阅的核心命令是两组

      • Publish

      • Subsctibe

        • Subscribe
        • UnSubscribe
        • PSubscribe
        • PUnSubscribe
    • 1.频道的订阅与退订

      • 订阅频道

        • 服务器有个字典叫pubsub_channels

          • 键是 某个被订阅的频道
          • 值是 一个链表, 这里面存了该频道的订阅者
      • 退订频道

        • 根据频道的名字在字典中找

          • 把客户端从链表中删除
          • 如果恰好是最后一个, 把这个频道也删除
    • 2.模式的订阅与退订

      • 服务器有个list叫pubsub_patterns

        • <client, pattern>作为list链表的元素
        • list<client,pattern>
      • 订阅模式

        • 新建一个node, 把这个node添加到list后面
      • 退订模式

        • 遍历这个list, 找到node然后删掉
    • 3.发送消息

      • 发送消息publish channelA "message"有两步,

        • 1.首先把消息发给channelA的所有订阅者
        • 2.然后把消息发给所有模式匹配channelA的
      • 1.很简单啦, 找到key对应的链表, 然后全部发一遍

      • 2.先查找所有list里的pattern, 如果匹配, 就把这个pattern对应的client都发一遍

    • 4.查看订阅消息

      • pubsub channels

      • pubsub numsub [channel]

        • 返回channel的订阅者数量
      • pubsub numpat

        • 返回被订阅模式的数量
        • number of pattern

    第19章 事务

    • Redis以MULTI命令开始, + 多个命令 +EXEC 提交给服务器来执行一个事务, 同时搭配上 WATCH命令, 来实现事务功能

    • 事务的实现

      • 三个阶段

        • 1.事务开始

          • 会把客户端从非事务状态切换成事务状态
        • 2.事务入队

          • 在非事务状态, 命令会立即执行

          • 在事务状态, 会看具体是啥命令

            • EXEC, DISCARD, WATCH, MULTI四个命令会立即执行
            • 其他命令放入一个队列, 返回QUEUED回复
        • 3.事务执行

          • 遍历这个队列, 依次执行
    • WATCH命令的实现

      • WATCH命令是一个乐观锁, 它可以在EXEC命令执行前 ,监视任意数量的数据库键, 并在EXEC命令执行时, 检查被监视的键是否至少有一个已经被修改了, 如果被改了, 服务器拒绝执行事务

      • 使用WATCH 命令监视数据库键

      • 监视机制的触发

        • 所有对数据库进行修改的命令, 都会对监视的数据结构(字典)进行检查, 如果匹配, 置安全性的标志位被破坏
      • 判断事务是否安全

        • 看一下安全性的标志位
      • 一个完成的WATCH事务 执行过程

    • 事务的ACID性质

      • 原子性

        • Redis不支持事务回滚机制(rollback)

          • 某个命令在执行期出现错误, 整个事务也会继续执行
          • 因为这种错误一般在开发环境, 很少在生产环境
      • 一致性

        • 入队错误

          • 入队出错, 其他成功入队的命令依旧正常执行
        • 执行错误

        • 服务器停机

          • 1.如果运行在无持久化的内存模式下, 重启后是空白的, 所以是一致性的
          • 2.有RDB文件, 也可以恢复到一致状态
          • 3.AOF, 也可以恢复到一致状态
      • 隔离性

        • 因为Redis使用单线程的方式来执行事务, 并且服务器保证 不会中断事务, 总是串行的方式运行, 所以事务也是有隔离性的
      • 持久性

        • 1.在内存模式下, 不具有持久性
        • 2.在RDB, 只有特定条件下, 才会执行BGSAVE命令, 而且异步的BGSAVE也不能保证事务第一时间保存在硬盘里, 也不具有持久性
        • 3.AOF模式, 且配置为always的时候, 同步的, 所以这有持久性
        • 4,AOF, 非always, 不具有持久性

    第20章 Lua脚本

    • 通过在服务器中嵌入Lua环境,Redis客户端可以使用Lua脚本, 直接在服务器端原子地执行多个Redis命令

    第21章 排序

    第22章 二进制位数组

    第23章 慢查询日志

    • Redis的慢查询日志功能用于记录执行时间超过给定时长的命令请求, 用户可以通过这个功能产生的日志来监视和优化 查询速度

    • 服务器配置有两个选项

      • slowlog-log-slower-than

        • 这个选项为100, 执行超过100微秒的命令就会被记录

          • CONFIG SET slowlog-max-len 5
          • SLOWLOG GET
      • slowlog-max-len

        • 最多保存多少条慢查询日志
    • 慢查询记录的保存

      • 用一个链表, 新的在表头
    • 慢查询纪录的阅览和删除

    • 添加新日志

      • 每次执行命令前, 都记录一下Unix时间戳, 执行完后, 也记录一下Unix时间戳

      • 检查有没有超出slowlog-log-slower-than
        的时间

      • 检查有没有超链表的长度

        • 有就删

    第24章 监视器

    • 客户端可以通过MONITOR命令 ,将客户端转换成监视器 ,接受并打印每个命令请求的相关信息
    • 把客户端的REDIS_MONITOR标识打开, 就可以从普通客户端变成监视器了
    • 服务器把所有监视器都存在一个链表里
    • 每次都会遍历链表, 挨个发送

    实践

    Redis的入门应用

    • Redis是key-value型数据库

    • Redis 里的单行命令都是原子的 是为了同时有多个用户对同一个数据修改

    • string

      • set key value

        • 子主题 1

          • SET server:name "fido"
            
          • GET server:name => "fido"
            
          • EXISTS server:name => 1
            
          • EXISTS server:blabla => 0
            
          • SET connections 10
            
          • INCR connections => 11
            
          • INCR connections => 12
            
          • DEL connections
            
          • INCR connections => 1
            
          • It is also possible to increment the number contained inside a key by a specific amount:
            
          • INCRBY connections 100 => 101
            
          • And there are similar commands in order to decrement the value of the key.
            
          • DECR connections => 100
            
          • DECRBY connections 10 => 90
            
      • get key

      • exists key

      • DEL key

      • EXPIRE key 100

        • 100秒后删除

        • TTL key

          • 显示还有几秒删除
          • 只要set key 一次 之前的计时失效 TTL key 返回-1
          • 返回-2说明计时过了 这东西被删了
        • PERSIST key

          • 让这个计时结束

            • 永久保存
      • INCR key

        • INCRBY key 100
      • DECR key

        • DECRBY key 100
      • 不能简单的get key value=value+1 set key value

    • list

      • 对头尾操作较快 且 有序号的

      • lpush friend "yanhao"

        • rpush friend "yanhao"

        • lpush friend 1 2 3 4

        • 执行顺序是

          • lpush friend 1
          • lpush friend 2
          • lpush friend 3
          • lpush friend 4
        • 所以结果是 4 3 2 1

      • lrange friend 0 -1

        • 切片

        • example

          • LRANGE friends 0 -1 => 1) "Sam", 2) "Alice", 3) "Bob"

          • LRANGE friends 0 1 => 1) "Sam", 2) "Alice"

          • LRANGE friends 1 2 => 1) "Alice", 2) "Bob"

          • lrange friend 0 10

              1. "Sam", 2) "Alice", 3) "Bob"
          • lrange friend -3 -1

              1. "Sam", 2) "Alice", 3) "Bob"
      • lset friend 0 "yanhao"

        • 把friend这个list里面的第0个元素改成"yanhao"
      • lpop friend

        • rpop friend
        • 子主题 2
      • llen

    • set

      • sadd setname "yanhao"

        • return 0

          • 没加成功,因为本来就有这个key了
        • return 1

          • 加成功了
      • sismember setname "yanhao"

      • smembers friend

      • sincr

      • sdecr

      • srem friend "yanhao"

        • return 0
        • return 1
      • srandmeber friend 2

        • 随机返回数据成员
      • spop

        • 随机删
      • sunion set1 set2

    • sorted set

      • 在set的同时加上一个用来排序的东西

      • ZADD fruit 1 apple

      • ZADD fruit 2 banano

      • Zrange fruit 0 1

        • 切片
    • hashes

      • hset user:37 name "yanhao"

      • hgetall user:37

      • hmset user:37 name "yanhao" age 17

        • 同时设置多个
      • hget user:37 name

      • hincrby user:37 name 2

        • name会加2
      • hdel

    XMind: ZEN - Trial Version

  • 相关阅读:
    springcloud 项目源码 微服务 分布式 Activiti6 工作流 vue.js html 跨域 前后分离
    springcloud 项目源码 微服务 分布式 Activiti6 工作流 vue.js html 跨域 前后分离
    OA办公系统 Springboot Activiti6 工作流 集成代码生成器 vue.js 前后分离 跨域
    java企业官网源码 自适应响应式 freemarker 静态引擎 SSM 框架
    java OA办公系统源码 Springboot Activiti工作流 vue.js 前后分离 集成代码生成器
    springcloud 项目源码 微服务 分布式 Activiti6 工作流 vue.js html 跨域 前后分离
    java 视频播放 弹幕技术 视频弹幕 视频截图 springmvc mybatis SSM
    最后阶段总结
    第二阶段学习总结
    第一阶段学习总结
  • 原文地址:https://www.cnblogs.com/yahoo17/p/13791072.html
Copyright © 2011-2022 走看看