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

  • 相关阅读:
    VFIO PF SRIOV IOMMU UIO概念解释、关联
    集群节点间网络通信TIPC
    1. C语言中的数据结构.md
    第三讲. COTS包交换介绍
    SYSTick 定时器
    热电偶基础知识介绍-04
    附录1· 初识Linux操作系统
    热电偶冷端补偿
    珍惜是最宝贵的财富。
    CSS 设置标题文字只显示一行,多余显示省略号
  • 原文地址:https://www.cnblogs.com/yahoo17/p/13791072.html
Copyright © 2011-2022 走看看