1. 程式人生 > 其它 >redis深度歷險-讀書筆記

redis深度歷險-讀書筆記

redis資料結構

  • string
    採取分配冗餘空間的方式減少記憶體頻繁分配
    當字串長度少於1MB,擴容都是成倍擴充套件
    當字串長度大於1MB,每次只會多擴1MB
    字串長度最大為512MB
    字串由多個位元組組成,每個位元組由8bit組成,就是bitmap(點陣圖)資料結構

  • list
    相當於java中的LinkedList
    redis底層儲存的是quicklist 快速連結串列
    資料量少的時候是ziplist,資料量大的時候改成quicklist
    quicklist就是多個ziplist連在一起

  • hash
    redis的hash只能是字串,
    rehash是漸進式rehash
    在漸進式rehash時,會保留新舊兩個hash結構,查詢會同時查詢兩個hash結構,然後在定時任務中漸漸將舊的結構的資料遷移到新的結構

  • set
    低層是hashset

  • zset
    類似於java的soreset和hashmap的結合體

分散式鎖

分散式鎖需要設定和超時為原子命令
set a a expire 5 nx
超時問題:如果加鎖和釋放鎖之間邏輯執行太長,超出了鎖的超時限制,建議分散式鎖下的程式執行時間儘量短
解決辦法:將set的value設定成隨機值,避免別的執行緒將自己的鎖釋放,確保當前執行緒佔有的鎖不會被其他執行緒釋放掉
除非這個鎖是因為到期了而被釋放

  • 可重入性
    如果一個鎖支援同一個執行緒同時加鎖,那麼這個鎖就是可重入的
    如果redis要支援可重入鎖,需要對客戶端的set方法進行包裝,使用ThreadLocal變數儲存當前持有鎖的計數
    儘量避免使用可重入鎖

延時佇列

實現阻塞佇列: 使用blpop和brpop取出資料,沒有資料的時候阻塞
佇列空了:可以使用阻塞指令,但是要注意超時的情況, 可以使用輪詢的方式,比較耗效能,可以加sleep讓cpu降下來

分散式鎖衝突處理: 1.直接拋異常 2.sleep後重試 3.將請求轉移到延遲佇列,稍後重試

延時佇列: 通過zset有序列表實現,將訊息序列化成zset的value,到期時間設定為score,然後用多個執行緒輪詢到期的任務

點陣圖

用於使用者簽到類業務,365天只需要365bit
點陣圖的陣列是自動擴充套件的,如果設定的某個偏移位超出了現有內容範圍,則自動將陣列進行零擴充
setbit s 1 1


getbit s 1

Scan

因為keys * 容易導致阻塞,所以用scan替代keys
scan 0 match a* count 1000

大key掃描

如果key太大,會導致資料遷移卡頓
如果需要擴容的時候,會一次性申請一塊大的記憶體,會導致卡頓,
如果key被刪除,會一次性回收一塊大的記憶體,也會導致卡頓

  • 掃描大key

redis單執行緒

1.基於記憶體
2.多路複用:select系列事件輪詢api,非阻塞io

  • 指令佇列:客戶端指令通過佇列順序處理
  • 響應佇列
  • 定時任務

redis持久化

使用作業系統的多程序Copy On Write機制進行
RDB: fork一個子程序做持久化,當父程序修改資料,會先拷貝一份出來修改
所以在子程序建立後,資料是不會變化的
AOF: redis會先執行指令再儲存日誌
AOF重寫: 開闢一個子程序對記憶體遍歷,轉換成一系列redis操作指令,序列化到一個新的AOF日誌檔案,序列化完成後,再將增量AOF日誌追加,完成後替換舊的AOF檔案

  • redis4.0增加混合持久化,重啟的時候先載入rdb的內容,然後再載入aof日誌,代替了之前的AOF全量檔案重放

redis叢集

redis主從同步是非同步的,不滿足一致性
主節點修改資料後會立即返回,從節點會盡量追趕主節點,redis保證資料最終一致性

  • 增量同步

\\

redis持久化機制

1.快照 2.AOF
快照是一次全量備份,以二進位制序列化的形式儲存,空間緊湊
AOF在長期執行後會變得很大,,需要定期做AOF重寫

  • 為什麼redis是單執行緒但是這麼快?
    1.基於記憶體
    2.多路服用