標籤:

圖解Redis之資料庫篇

redis伺服器將所有資料庫都保存在伺服器狀態redis.h/redisServer結構的db中,db是一個redisDb數組類型,每個元素都代表一個資料庫(redisDb),而伺服器內部又保存一個redisClient結構,它包含了一個redisDb指針,用來表示當前所用資料庫.當我們想切換資料庫時,只需修改這個指針即可.下面是它的數據結構代碼

struct redisServer{ ... redisDb *db; //redisDb數組,表示伺服器中所有的資料庫 int dbnum; //數組大小,默認為16 ... }; typedef struct redisClient{ ... redisDb *db; //客戶端當前所選資料庫 ... }redisClient;typedef struct redisDb { int id; // 資料庫ID標識 dict *dict; // 資料庫鍵空間,存放著所有的鍵值對 dict *expires; // 鍵的過期時間 dict *watched_keys; // 被watch命令監控的key和相應client long long avg_ttl; // 資料庫內所有鍵的平均TTL(生存時間) } redisDb;

代碼中有個dict,這個字典(鍵空間)存放了資料庫所有的鍵值對,它的鍵是字元串對象,值可以使Redis的5中任意對象之一.下面給出一個它的實例圖(redis設計與實現p94)

關於鍵值對增刪改查的基本操作,不再敘述,這裡要說的是讀取操作會引起伺服器鍵空間命中次數(hit)和不命中次數(miss),以及LRU(最後一次訪問時間)的更新,如果讀取時發現某個鍵已過期,則刪之

當修改一個值時,如果有客戶端使用watch監視了某個鍵,那麼伺服器在被監視鍵修改後,會將這個key標記為臟(dirty),每修改一次,dirty加1;此外,如果伺服器開啟了通知功能,修改值後,伺服器還會按照配置發送相應的資料庫通知

好的介紹完鍵空間後我們一起聊聊鍵的生存和過期時間,以及過期鍵刪除策略

  • 設置鍵生存時間 EXPIRE key seconds, PEXPIRE key ms

  • 設置過期時間: EXPIREAT unix時間戳(s) , PEXPIREAT unix時間戳(ms)

過期鍵刪除策略

    • 定時刪除:通過設置定時器,在過期時立即刪除
    • 惰性刪除:放任不管,但在獲取鍵時發現過期,則刪除
    • 定期刪除:每隔一段時間,檢查一次資料庫,刪除過期鍵

定時刪除佔用CPU,影響伺服器響應時間和吞吐量;惰性刪除太佔用空間,可能發生內存泄露,定期刪除是兩者的中和,伺服器需要根據情況,合理的設置刪除操作的執行時長和執行頻率.

# 默認每次檢查的資料庫數量DEFAULT_DB_NUMBERS = 16# 默認每個資料庫檢查的鍵數量DEFAULT_KEY_NUMBERS = 20# 全局變數,記錄檢查進度current_db = 0def activeExpireCycle(): #初始化要檢查的資料庫數量 #如果服務榕的資料庫數量比DEFAULT DB NUMBERS 要小 #那麼以伺服器的資料庫數量為準 if server.dbnum < DEFAULT_DB_NUMBERS : db_numbers = server.dbnum else : db_numbers = DEFAULT_DB_NUMBERS #遍歷各個資料庫 for i in range(db_numbers) : #如果current_db 的值等於服務榕的資料庫數量 #這表示檢查程序已經遍歷了服務榕的所有資料庫一次 #將current_db重置為0 ,開始新的一輪遍歷 if current_db == server.dbnum: current_db = 0 # 獲取當前要處理的資料庫 redisDB = server.db[current db) #將資料庫索引增1 ,指向下一個要處理的資料庫 current_db += 1 #檢查資料庫鍵 for j in range(DEFAULT_KEY_NUMBERS): #如果資料庫中沒有一個鍵帶有過期時間,那麼跳過這個資料庫 if redisDB.expires.size () == 0: break # 隨機獲取一個帶有過期時間的鍵 key with_ttl = redisDb.expires.get_random_key () #檢查鍵是否過期,如果過期就刪除它 if is_expired (key with ttl): delete_key (key_with_ttl ) #已達到時間上限,停止處理 if reach_time_limit(): return

activeExpireCycle 函數的工作模式可以總結如下:

  1. 函數每次運行時,都從一定數量的資料庫中取出一定數量的隨機鍵進行檢查, 並刪除其中的過期鍵。

  2. 全局變數current db 會記錄當前activeExpireCycle 函數檢查的進度,並在下一次activeExpireCycle 函數調用時,接著上一次的進度進行處理。比如說,如果當前activeExpireCycle 函數在遍歷10 號資料庫時返回了,那麼下次activeExpireCycle 函數執行時,將從11號資料庫開始查找並刪除過期鍵。

  3. 隨著activeExpireCycle函數的不斷執行,伺服器中的所有資料庫都會被檢查一遍,這時函數將current_db變數重置為0,然後再次開始新一輪的檢查工作

下面介紹下一些常見操作對過期鍵的處理(摘自redis設計與實現)

到了這裡,我們基本了解了Redis資料庫,如果喜歡我的文章,請繼續閱讀圖解Redis之持久化篇
推薦閱讀:

知乎是如何做緩存的?
Redis源碼剖析--源碼結構解析
No-SQL資料庫中的事務性設計
如何評價360開源的pika項目?
redis相對於mysql有什麼劣勢,是不是能用redis盡量用?

TAG:Redis |