1. 程式人生 > >Redis設計與實現之資料庫

Redis設計與實現之資料庫

1,資料庫
Redis使用redis.h/redisServer結構儲存資料庫狀態。其中的陣列redisDb *db,儲存資料庫中所有的資料庫。另一個變數 int dbnum屬性記錄當前伺服器中資料庫的數量。dbnum預設為16,也就是redis初始化時會預設建立16個數據庫。
2,切換資料庫
每一個redis客戶端都有自己的目標資料庫,客戶端執行資料庫的寫/讀命令時目標資料庫就是成為命令操作的物件。預設情況下,redis客戶端使用0號資料庫,並可以使用select命令來切換目標資料庫。切換資料庫的過程也是redisClient資料結構中 redisDb *db指標重新賦值的過程。
3,資料庫鍵空間


Redis是一個鍵值對資料庫伺服器,每一個數據庫使用redis.h/redisDb結構描述,其中有一個dict字典儲存了該資料庫的所有鍵值對,成為鍵空間;

typedef struct redisDb {
    dict *dict; /* The keyspace for this DB */
} redisDb;

鍵空間的鍵也就是資料庫的鍵,每一個鍵都是一個字串物件
鍵空間的值也就是資料庫的值,每一個值可以是字串物件、列表物件、雜湊表物件、集合物件和有序集合物件的任意一種。
新增、刪除、更新和獲取資料庫中的鍵,就是對dict字典的操作。注意每一種值物件對應著不同的鍵獲取命令,如get,lrange,hget,smembers,zrange等。
其他一些針對資料庫本身的命令,也是通過對鍵空間進行處理來完成的。如flushdb用於刪除鍵空間中所有鍵值對,dbsize返回資料庫鍵數量等。
Redis對鍵空間的一些維護操作:


1,讀取鍵時,根據鍵是否存在跟新鍵空間命中次數和不命中次數,並用keyspace_hits和keyspace_misses屬性記錄。可用info stats命令檢視。
2,更新鍵的lru(最後一次使用)時間,用於計算閒置時間。使用object idletime 命令檢視閒置時間。
3,讀取鍵時,如果過期,要先刪除過期鍵在執行後續操作。
4,客戶端使用watch命令,監視一個鍵,如果該鍵被修改,就會被標記為髒(dirty),以使用事務程式對其處理。
5,伺服器每修改一個鍵都會講髒鍵計數器+1,該計數器會觸發伺服器的持久化以及複製操作。
6,如果伺服器開啟了資料庫通知功能,對鍵修改後,伺服器會按配置傳送相應的資料庫通知。
4,鍵的生存週期

使用expire,pexpire命令(單位分別是s和ms)設定鍵的生存時間TTL。expire ;經過指定的時間後,資料庫會自動產出ttl為0的鍵。可以使用ttl或者pttl命令檢視帶有ttl鍵的剩餘時間。
使用expireat,pexpireat命令將過期時間設定為相應的時間戳。expireat ;
實際上expire,pexpire,expireat最終都是呼叫了pexpireat實現了設定ttl的功能。
expire(將s轉化為ms)->pexpire(將ttl轉換為timestamp)->->pexpireat
expireat(將s級timestamp轉化為ms級)->pexpireat
redisDb結構使用名為expires的字典儲存資料庫中所有鍵的過期時間,也稱為過期字典。

typedef struct redisDb {
    dict *expires; /* Timeout of keys with a timeout set */
} redisDb;

expires的鍵時指標,指向鍵空間的某個鍵物件
expires的值是longlong型別的整數,儲存了指向資料庫鍵的過期時間,一個ms級unix時間戳。
使用persist可以解除給定鍵的過期時間。
使用ttl或者pttl命令獲得鍵的剩餘生存時間,實現在db.c/ttlGenericCommand(),偽碼如下:

def ttlGenericCommand(key):
    if key 不在資料庫中
          return -2
    if key 沒有設定expire
          return -1
    ttl = expire時間戳 - 當前時間戳
    if ttl 小於 0
          ttl = 0
    按照ms標示將ttl的單位設定為轉化為ms或者s
    返回 ttl

判斷鍵是否過期:
1,檢查指定鍵是否存在於過期字典;如果存在,取得過期時間,否則返回false
2,檢查當前時間戳是否大於過期時間戳;如果大於,鍵已過期返回true,否則返回false
5,過期鍵的刪除策略
過期鍵的刪除策略有三種:
1,定時刪除
設定過期時間的同時建立一個定時器,讓定時器在鍵的過期時間來臨時立即執行對鍵的刪除。
該策略對記憶體比較友好,保證過期鍵會被儘快刪除,但是它對cpu時間最不友好,大量的過期鍵需要進行刪除操作時會佔用大量的cpu時間。此外定時器使用時間事件處理,redis的時間事件存放在一個無序連結串列中,查詢時間複雜度為O(N),並不高效。
2,惰性刪除
放任過期鍵不管,但是每當從鍵空間中獲取到一個鍵時,都要檢查是否過期,如果過期執行刪除操作,否則返回該鍵。
該策略對cpu時間最友好,不會在刪除其他無關的過期鍵上花費cpu時間,但是對記憶體最不友好。大量的過期鍵得不到刪除會佔用大量的記憶體,甚至是記憶體洩露。
3,定期刪除
每隔一段時間對資料庫進行一次檢查,刪除遇到的過期鍵。每次檢查多少個鍵,多少個數據庫,由演算法決定。
該策略是前兩種策略的折中;
1,每隔一段時間執行一次過期鍵的刪除,並通過限制刪除操作執行的時長和頻率來減少對cpu時間的影響
2,定期刪除,也有效的減少了大量過期鍵帶來的記憶體浪費。
該策略的難度在於刪除操作執行的時長和頻率:
1,太頻繁,退化為定時刪除,佔用大量cpu時間
2,太少,退化為惰性刪除,記憶體浪費
6,redis的過期鍵刪除策略
redis同時使用惰性刪除和定期刪除兩種策略。在CPU時間和記憶體之間取得平衡。
惰性刪除。所有讀寫資料庫的redis命令在執行之前都會呼叫db.c/expireIfNeeded檢查是否過期,實現如下:
1,如果鍵過期了,expireIfNeeded函式將輸入鍵從資料庫中刪除
2,如果鍵未過期,expireIfNeeded不做操作
注:
1,當鍵存在且未過期時,命令按照鍵存在的情況執行
2,當鍵不存在或者過期時,命令按照鍵不存在的情況執行
定期刪除。redis.c/activeExpireCycle函式實現了定期刪除,當伺服器週期性操作redis.c/serverCron函式執行時activeExpireCycle被執行:
1,函式每次執行都從一定數量的資料庫中隨機選取一定數量的鍵進行檢查並刪除其中的過期鍵。數量分別由REDIS_DBCRON_DBS_PER_CALL和ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP指定。
2,全域性變數current_db記錄當前檢查進度。每檢查一個數據庫,current_db+1,下一次執行同樣從current_db開始
3,當所有資料庫都被檢查一遍後,current_db置0
7,AOF、RDB和複製功能對過期鍵的處理
生成RDB檔案。使用save或者bgsave命令建立一個新的RGB檔案時,程式會對資料庫中的鍵進行檢查,過期的鍵並不會寫入到新建立的RDB檔案中。
載入RDB檔案。啟動redis資料庫時,如果啟用了RDB功能,那麼伺服器將對RDB檔案進行載入:
1,redis伺服器以主伺服器模式執行,載入RDB檔案時,程式會對檔案中儲存的鍵進行檢查,過期鍵不會被載入到資料庫中
2,redis伺服器以從伺服器模式執行,載入RDB檔案時,檔案中的所有鍵無論是否過期都會被載入到資料庫。但是主從同步時,從資料庫的資料會被清空,所以過期鍵對載入RDB檔案也不會有影響。
AOF檔案寫入。在AOF持久化模式下,如果鍵已過期但是還沒有被刪除,那麼對AOF檔案不會有影響。當過期鍵被刪除時,程式會向AOF檔案append一條del命令,顯式記錄該鍵已被刪除。
AOF檔案重寫時的情況和生成RDB檔案的情況是一樣的。
主從複製模式下伺服器的過期鍵刪除由主伺服器控制,從而保證了主從資料一致性:
1,主伺服器刪除過期鍵時,向所有從伺服器傳送del命令,告知刪除這個過期鍵
2,從伺服器執行客戶端命令時,及時遇到過期鍵也不會將過期鍵刪除,而是將過期鍵當做非過期鍵處理
3,從伺服器只有收到主伺服器的del通知之後,才會刪除相應的過期鍵
5,資料庫通知
使用subscribe命令訂閱針對鍵或者命令的操作資訊,來獲知鍵的變化。如:

subscribe __keyspace@0__:<key|command>

此外,伺服器配置notify_keyspace_events選項決定了伺服器傳送通知的型別:
AKE———-伺服器傳送所有鍵空間通知和鍵事件通知
AK————伺服器傳送所有鍵空間通知
AE————伺服器傳送所有鍵事件通知
K$————伺服器傳送所有和字串相關的鍵空間通知
El————-伺服器傳送所有和列表鍵相關的鍵事件通知
傳送通知。當命令被執行的時候,如果執行成功後會呼叫notify.c/notifyKeyspaceEvent函式傳送通知。虛擬碼如下:

def notifyKeyspaceEvent(type,event,key dbid):
    if 通知型別type不是server允許傳送通知型別:
          return
    #傳送鍵空間通知
    if server.notify_keyspace_events & NOTIFY_KEYSPACE
          #將通知傳送給頻道[email protected]<dbid>__:<key>,內容為發生的事件通知<event>
          chan = __keyspace@<dbid>__:<key>
          pubsubPublishMessage(chanobj, eventobj);
    #傳送鍵事件通知
    if server.notify_keyspace_events & NOTIFY_KEYEVENT
          #將通知傳送給頻道[email protected]<dbid>__:<event>,內容為發生的事件通知<key>
          chan = __keyspace@<dbid>__:<event>
          pubsubPublishMessage(chanobj, keyobj);

相關推薦

Redis設計實現資料庫

1,資料庫 Redis使用redis.h/redisServer結構儲存資料庫狀態。其中的陣列redisDb *db,儲存資料庫中所有的資料庫。另一個變數 int dbnum屬性記錄當前伺服器中資料庫的數量。dbnum預設為16,也就是redis初始化時會預設

Redis 設計實現RDB 和 AOF 兩種持久化模式詳解

在執行情況下, Redis 以資料結構的形式將資料維持在記憶體中, 為了讓這些資料在 Redis 重啟之後仍然可用, Redis 分別提供了 RDB 和 AOF 兩種持久化模式。 在 Redis 執行時, RDB 程式將當前記憶體中的資料庫快照儲存到磁碟檔案中, 在 Red

Redis設計實現連結串列--閱讀筆記

前言 連結串列提供了高效的節點重排能力,以及順序性的節點訪問方式,連結串列在 Redis 中的應用很廣泛,比如列表鍵的底層實現之一就是連結串列,除此之外,釋出與訂閱、慢查詢、監視器等功能也用到了連結串列,Redis 伺服器本身使用連結串列儲存多個客戶端的狀態資

Redis 設計實現資料庫

> 本文的分析都是基於 Redis 6.0 版本原始碼 > redis 6.0 原始碼:https://github.com/redis/redis/tree/6.0 ## 伺服器中的資料庫 Redis 伺服器將絕大部分的資訊都儲存在 `server.h/redisServer`。redis 的資料是儲存在

Redis 設計實現[3] -- 多機資料庫實現

1 複製 在 Redis 中,使用者可以通過 slaveof 命令讓一個伺服器去複製另一個伺服器,被複制的伺服器為主伺服器(master),對主伺服器進行復制的伺服器為從伺服器(slave),主伺服器和從伺服器將保持相同的資料。 1.1 舊版複製功能 Redis 的複製功能分為同步(sy

Redis 設計實現[2] -- 單機資料庫實現

1 鍵空間 set message "hello word" rpush alphabet "a" "b" "c" hset book name "Redis in action" hset book author "Josian L. Carlson" hset book publisher

redis設計實現》2-資料庫實現

上一篇文章介紹了redis基本的資料結構和物件《redis設計與實現》1-資料結構與物件篇 本文主要關於: redis資料庫實現的介紹 前面介紹的各種資料,在redis伺服器中的記憶體模型是什麼樣的的。 RDB檔案將這些記憶體資料持久化後的格式是什麼樣的 RDB和AOF序列化的區別是什麼

Redis設計實現讀書筆記第一部分: 資料結構物件

(一) 簡單動態字串SDS Redis沒有直接使用C語言傳統的字串表示(以空字串結尾的字串),而是構建了簡單動態字串SDS,其定義 struct sdshdr{ int len; //記錄buf陣列中已使用位元組的數量 int free; //記錄buf

Redis 設計實現》第二版讀書筆記整數集合

簡介 整數集合是集合鍵的底層實現之一,當一個集合只包含整數值元素,並且這個集合的元素數量不多時,Redis就會使用整數集合作為集合鍵的底層實現 實現 整數集合是Redis用於儲存整數值的集合抽象資料結構,它可以儲存型別為int16_t、int32_t、int64_t的整數值,並且

探索Redis設計實現9:資料庫redisDb鍵過期刪除策略

一. 資料庫Redis的資料庫使用字典作為底層實現,資料庫的增、刪、查、改都是構建在字典的操作之上的。 redis伺服器將所有資料庫都儲存在伺服器狀態結構redisServer(redis.h/redisServer)的db陣列(應該是一個連結串列)裡:struct redisServer { //..

Redis設計實現》[第二部分]單機資料庫實現-C原始碼閱讀(三)

3、AOF持久化 關鍵字:AOF持久化:檔案寫入與同步,AOF檔案重寫,資料一致性 與RDB持久化通過儲存資料庫中的鍵值對來記錄資料庫狀態不同,AOF持久化是通過儲存redis伺服器所執行的寫命令來記錄資料庫狀態的 被寫入AOF檔案的所有命令都是以

Redis 設計實現 6:五大資料型別字串

前文 [Redis 設計與實現 2:Redis 物件](https://www.cnblogs.com/chenchuxin/p/14187921.html) 說到,五大資料型別都會封裝成 `RedisObject`。 ```c typedef struct redisObject { unsigne

Redis 設計實現 8:五大資料型別雜湊

雜湊物件的編碼有兩種:`ziplist`、`hashtable`。 ## 編碼一:ziplist `ziplist` 已經是我們的老朋友了,它一出現,那肯定就是為了節省記憶體啦。那麼雜湊物件是怎麼用 `ziplist` 儲存的呢? 每次插入鍵值對的時候,在 `ziplist` 列表末尾,挨著插入 `fiel

Redis 設計實現(第六章) -- 整數集合(intset)

相同 spa edi redis cnblogs 保存 空間 數值 一個數 概述 1.intset概述 2.intset實現 3.intset升級 intset概述 整數集合是Redis集合鍵的底層實現之一,當值都為整數時,redis就會選擇整數集合作為底層實現。 可以保

Redis 設計實現(第九章) -- 數據庫

resize ger think contex sta 占用 return bsp null 概述 1.數據庫結構 2.數據庫鍵空間 3.鍵生存時間 4.持久化對過期鍵處理 5.數據庫通知 1.數據庫結構 Redis服務器將所有server狀態都保存在數據結構

Redis 設計實現(第九章) -- 持久化RBD

key fork amount del pty str server int name 概述 Redis為內存數據庫,即所有的鍵值對信息保存在內存中,那麽一旦服務器出現問題重啟,內存中的數據就會沒有了。所以Redis需要實現持久化,將內存中的數據持久化到硬盤,在重新啟動後

Redis 設計實現(第十章) -- 持久化AOF

written led atom 我們 continue ctc unixtime 機制 warn 概述 Redis除了RDB方式提供持久化外,還提供了AOF的方式,和RDB保存數據庫的鍵值對不同,AOF是記錄數據庫執行的命令來記錄數據庫狀態的。當AOF開始時,Redis

Redis 設計實現(第十三章) -- 客戶端

腳本 ons listening last then red logs 可能 reply 概述 對於每個與服務器連接的客戶端,服務器都為它創建相應的redisClient的數據結構,並保持了相應的狀態。Redis Server通過一個鏈表來保存所有的客戶端連接。 本章包括:

Redis 設計實現 (三)

check 就會 鍵值 鍵值對 是否 second 變化 write 次數 RDB 持久化  一、生成RDB   cmd:SAVE --阻塞進程,執行完,才能有效接收客戶端命令。   cmd: BGSAVE --非阻塞,開啟子進程保存。           客

Redis 設計實現 (五)--多機數據庫的實現

緩沖 所有 moved 啟動 tin 當前 body ica red 多機數據庫的實現 一、復制   slaveof 主服務器ip地址。形成主從關系。   1、同步       從向主服務器發送sync命令。     主服務器收到sync命令執行bgsav