《Redis設計與實現》讀書筆記(一)簡單動態字符串與鏈表

  翻這本書已經不止一次了,可是老是學完就忘,藉着此次部門組織興趣小組再從新系統的學習一遍。但當我和同事討論這本書學來有什麼用的時候,你們都有點兩眼一抹黑,我自問本身也有點迷茫。「是啊,我看過了,而後我也不知道這個有什麼用「。因此我就google了一下」SDS的好處「。html

  像咱們平時用的都是從使用者的角度。好比:string、list、hash、set、sorted set。redis

  從內部實現的角度。好比:dict、sds、ziplist、quicklist、skiplist數組

  我恍然大悟,我明白了redis是如何用內部的數據結構去實現經常使用的數據結構。這樣我就能明白這本書所表達的含義了。安全

  在討論任何一個系統的內部實現的時候,咱們都要先明確它的設計原則,這樣咱們才能更深入地理解它爲何會進行如此設計的真正意圖。在本文接下來的討論中,咱們主要關注如下幾點:bash

  • 存儲效率。壓縮數據、減小內存碎片。
  • 快速響應時間。
  • 單線程。不在於CPU資源,而在於內存訪問和網絡IO。

1、簡單動態字符串

Redis本身構建了一個數據結構 命名爲 簡單動態字符串(simple dynamic string,SDS)網絡

struct sdshdr {
  //記錄buf數組中已使用字節的數量 等於SDS所保存字符串的長度
  int len;
  //記錄buf數組中未使用字節的數量
  int free;
  //字節數組,用戶保存字符串
  char buf[];
}複製代碼

SDS遵循C字符串以空字符串結尾的慣例,好處是能夠直接重用一部分C字符串函數庫裏面的函數。可是C字符串不知足Redis對字符串在安全性、效率以及功能方面的要求。數據結構

  1. 常數複雜度獲取字符串的長度

C字符串自己不記錄自身的長度信息,獲取長度信息時,須要遍歷整個字符串進行計數,直到遇到字符串結尾的空字符串爲止。操做的複雜度爲O(N)。app

而SDS中len屬性中記錄了SDS的長度,因此獲取一個SDS的長度複雜度爲O(1)。函數

   2.杜絕緩衝區溢出post

C字符串自己不記錄自身的長度,執行拼接字符串時,沒有預先擴容,則可能會出現內存其餘內容被意外修改

而SDSAPI須要對SDS進行修改時,API會先檢查SDS的空間是否知足修改所須要的要求,若是不知足的話,API會自動將SDS的空間擴展至執行修改所需的大小,而後才執行實際的修改操做

  3.減小修改字符串時帶來的內存重分配次數

內存重分配

(1)空間預分配

優化SDS的字符串增加操做。

基於修改後的長度是否大於1MB,小於則將free屬性的值設置爲len的值;大於則將free屬性的值設爲1MB。

基於以上策略,當連續增加N次字符串所須要的內存重分配次數從一定N次下降爲最多N次。

(2)惰性空間釋放

優化SDS的字符串縮減操做。

當SDS的API須要縮短SDS保存的字符時,程序並不當即使用內存從新分配來回收縮短出來後多出來的字節,而是使用free屬性將這些字節的數量記錄起來,並等待未來使用。

以空間換時間

  4.二進制安全

C字符串中的字符必須符合某種編碼,而且除了字符串的末尾以外,字符裏不能包含空字符,不然最早程序讀入的空字符串將被誤認爲是字符串結尾,致使C字符串只能保存文本數據。

因此SDS的API都是二進制安全的,全部SDS API都會以二進制的方式來處理SDS存放在buf數組裏的數據,程序不會對其中數據作任何限制、過濾、或者假設,數據在寫入時啥樣,讀取就是啥樣。

  5.兼容部分C字符串函數

2、鏈表

typedef struct listNode {
  //前置節點
  struct listNode *prev;
  //後置節點
  struct listNode *next;
}複製代碼

參考連接:zhangtielei.com/posts/blog-… 

相關文章
相關標籤/搜索