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哨兵

第17章 集群

第四部分 独立功能的实现

第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章 事务

第23章 慢查询日志

第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

posted @ 2020-10-07 17:05  Yan_Hao  阅读(350)  评论(1编辑  收藏  举报