1. 程式人生 > 實用技巧 >Redis設計與實現(一) 分散式部分 複製 和 釋出/訂閱模式

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