redis系列之------資料庫

来源:https://www.cnblogs.com/wenbochang/archive/2019/10/19/11694402.html
-Advertisement-
Play Games

前言 當我們在Redis資料庫中set一個KV的時候,這個KV保存在哪裡?如果我們get的時候,又從哪裡get出來。時間複雜度,空間複雜的等等,怎麼優化等等一系列問題。 伺服器中的資料庫 Redis伺服器將所有資料庫信息都保存在redis.h##redisService結構體中。代碼如下: 列了幾個 ...


前言

當我們在Redis資料庫中set一個KV的時候,這個KV保存在哪裡?如果我們get的時候,又從哪裡get出來。時間複雜度,空間複雜的等等,怎麼優化等等一系列問題。

伺服器中的資料庫

Redis伺服器將所有資料庫信息都保存在redis.h##redisService結構體中。代碼如下:

 1 struct redisServer {
 2 
 3     // ...
 4 
 5     /* General */
 6 
 7     // 配置文件的絕對路徑
 8     char *configfile;           /* Absolute config file path, or NULL */
 9 
10     // 資料庫
11     redisDb *db;
12 
13      // 是否設置了密碼
14     char *requirepass;          /* Pass for AUTH command, or NULL */
15 
16     // PID 文件
17     char *pidfile;              /* PID file path */
18 
19     // TCP 監聽埠
20     int port;                   /* TCP listening port */
21 
22    // ...
23 
24 }

列了幾個,我認為比較重要的。其中最重要的,肯定是redisDb *db; 這個數據結構保存了我們所有的數據。

Redis 是一個鍵值對(key-value pair)資料庫伺服器, 伺服器中的每個資料庫都由一個 redis.h/redisDb 結構表示, 其中, redisDb 結構的 dict 字典保存了資料庫中的所有鍵值對, 我們將這個字典稱為鍵空間(key space)。代碼如下:

/* Redis database representation. There are multiple databases identified
 * by integers from 0 (the default database) up to the max configured
 * database. The database number is the 'id' field in the structure. */
typedef struct redisDb {

    // 資料庫鍵空間,保存著資料庫中的所有鍵值對
    dict *dict;                 /* The keyspace for this DB */

    // 鍵的過期時間,字典的鍵為鍵,字典的值為過期事件 UNIX 時間戳
    dict *expires;              /* Timeout of keys with a timeout set */

    // 正處於阻塞狀態的鍵
    dict *blocking_keys;        /* Keys with clients waiting for data (BLPOP) */

    // 可以解除阻塞的鍵
    dict *ready_keys;           /* Blocked keys that received a PUSH */

    // 正在被 WATCH 命令監視的鍵
    dict *watched_keys;         /* WATCHED keys for MULTI/EXEC CAS */

    struct evictionPoolEntry *eviction_pool;    /* Eviction pool of keys */

    // 資料庫號碼
    int id;                     /* Database ID */

    // 資料庫的鍵的平均 TTL ,統計信息
    long long avg_ttl;          /* Average TTL, just for stats */

} redisDb;

其中最重要的就是 dict *dict; 他是一個字典,不太瞭解的小伙伴,可以看我前一篇的文章(https://www.cnblogs.com/wenbochang/p/11673590.html),對redis的字典有詳細的介紹。

這個dict存儲了我們真正的數據。

鍵空間和用戶所見的資料庫是直接對應的:

  • 鍵空間的鍵也就是資料庫的鍵, 每個鍵都是一個字元串對象。
  • 鍵空間的值也就是資料庫的值, 每個值可以是字元串對象、列表對象、哈希表對象、集合對象和有序集合對象在內的任意一種 Redis 對象。

因為資料庫的鍵空間是一個字典, 所以所有針對資料庫的操作 —— 比如添加一個鍵值對到資料庫, 或者從資料庫中刪除一個鍵值對, 又或者在資料庫中獲取某個鍵值對, 等等, 實際上都是通過對鍵空間字典進行操作來實現的。那麼複雜度顯而易見基本就是O(1)級別了,這也是redis為什麼能這麼快的一個重要原因。

 

對鍵取值

對一個資料庫鍵進行取值, 實際上就是在鍵空間中取出鍵所對應的值對象。代碼如下:

 1 /*
 2  * 返回字典中包含鍵 key 的節點
 3  *
 4  * 找到返回節點,找不到返回 NULL
 5  *
 6  * T = O(1)
 7  */
 8 dictEntry *dictFind(dict *d, const void *key)
 9 {
10     dictEntry *he;
11     unsigned int h, idx, table;
12 
13     // 字典(的哈希表)為空
14     if (d->ht[0].size == 0) return NULL; /* We don't have a table at all */
15 
16     // 如果條件允許的話,進行單步 rehash
17     if (dictIsRehashing(d)) _dictRehashStep(d);
18 
19     // 計算鍵的哈希值
20     h = dictHashKey(d, key);
21     // 在字典的哈希表中查找這個鍵
22     // T = O(1)
23     for (table = 0; table <= 1; table++) {
24 
25         // 計算索引值
26         idx = h & d->ht[table].sizemask;
27 
28         // 遍歷給定索引上的鏈表的所有節點,查找 key
29         he = d->ht[table].table[idx];
30         // T = O(1)
31         while(he) {
32 
33             if (dictCompareKeys(d, key, he->key))
34                 return he;
35 
36             he = he->next;
37         }
38 
39         // 如果程式遍歷完 0 號哈希表,仍然沒找到指定的鍵的節點
40         // 那麼程式會檢查字典是否在進行 rehash ,
41         // 然後才決定是直接返回 NULL ,還是繼續查找 1 號哈希表
42         if (!dictIsRehashing(d)) return NULL;
43     }
44 
45     // 進行到這裡時,說明兩個哈希表都沒找到
46     return NULL;
47 }

 

看代碼其實是很簡單的。

  • 首先判斷字典是否為空,如果為空,沒有繼續下去的必要了,直接return null
  • 第二步,如果在進行rehash,則先進行漸進式rehash。(不懂的,翻我上一篇博文)
  • 第三步,計算key的hash值。
  • 第四步,查找ht[0],ht[1]兩張table表。其中如果是鏈表,則while迴圈查找即可。
  • 找到返回,沒找到返回null。非常的簡單清晰的邏輯。
  • 大致如下圖:

 

           

 

對鍵增加,刪除,更新類似於查找。我就不一一列出源碼了。

後言

當使用 Redis 命令對資料庫進行讀寫時, 伺服器不僅會對鍵空間執行指定的讀寫操作, 還會執行一些額外的維護操作, 其中包括:

  • 在讀取一個鍵之後(讀操作和寫操作都要對鍵進行讀取), 伺服器會根據鍵是否存在, 以此來更新伺服器的鍵空間命中(hit)次數或鍵空間不命中(miss)次數, 這兩個值可以在 INFO stats 命令的 keyspace_hits 屬性和 keyspace_misses 屬性中查看。
  • 在讀取一個鍵之後, 伺服器會更新鍵的 LRU (最後一次使用)時間, 這個值可以用於計算鍵的閑置時間, 使用命令 OBJECT idletime <key> 命令可以查看鍵 key 的閑置時間。
  • 如果伺服器在讀取一個鍵時, 發現該鍵已經過期, 那麼伺服器會先刪除這個過期鍵, 然後才執行餘下的其他操作。
  • 如果有客戶端使用 WATCH 命令監視了某個鍵, 那麼伺服器在對被監視的鍵進行修改之後, 會將這個鍵標記為臟(dirty), 從而讓事務程式註意到這個鍵已經被修改過。
  • 伺服器每次修改一個鍵之後, 都會對臟(dirty)鍵計數器的值增一, 這個計數器會觸發伺服器的持久化以及複製操作執行。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • DHCP服務概述: 名稱:DHCP - Dynamic Host Configuration Protocol 動態主機配置協議。 功能:DHCP(Dynamic Host Configuration Protocol,動態主機配置協議)是一個區域網的網路協議,主要優點: 特點: C/S 模式 自動 ...
  • crontab 是用來讓使用者在固定時間或固定間隔執行程式之用 參數說明 選項 功能 -e 編輯crontab定時任務 -l 查詢crontab任務 -r 刪除當前用戶所有的crontab任務 時間格式 項目 含義 範圍 第一個“*” 一小時當中的第幾分鐘 0-59 第二個“*” 一天當中的第幾小時 ...
  • 回到目錄 (續上小節) 3. 分壓偏置 前面的“改進型固定偏置”電路,雖然情況比原始的固定偏置電路好了一點,但還是不太理想,於是人們又設計出了性能更加穩定的分壓偏置(voltage-divider bias configuration)電路,如下圖所示: 圖3-6.06 分壓偏置電路的穩定性非常完美 ...
  • https://www.jb51.net/article/82608.htm https://blog.csdn.net/taian1665/article/details/86492400 https://blog.51cto.com/bantu/1982399 https://blog.csdn ...
  • 今日工作:資料庫連接、數據寫入 一、資料庫連接:使用了pymysql庫 二、數據寫入 代碼部分結束,雖然今天的代碼看起來又少又容易,但sql語句可真是廢了不少力氣。 最終資料庫成果,總共1729條數據: ...
  • 1. 鎖分類 MySQL中主要分為全局鎖、表級鎖和行鎖三類。本篇主要涉及全局鎖和表級鎖。 2. 全局鎖 全局鎖是對整個資料庫實例進行加鎖。 Flush table with read lock(FRTWRL)該命令用於加全局鎖。使用該命令之後,整個庫處於只讀狀態,不能執行數據的增刪改查、建表、修改表 ...
  • 列的查詢 語法1-1 基本的SELECT語句 SELECT <列名>,... FROM <表名>; 語法1-2 查詢出表中所有的列 SELECT * FROM <表名>; 星號(*)是代表全部列的意思。使用星號無法設定列的顯示順序。 語法1-3 1.為列設定別名 eg:SELECT product_ ...
  • 作為一名應用系統開發人員,為什麼要關註數據內部的存儲和檢索呢?首先,你不太可能從頭開始實現一套自己的存儲引擎,往往需要從眾多現有的存儲引擎中選擇一個適合自己應用的存儲引擎。因此,為了針對你特定的工作負載而對資料庫調優時,最好對存儲引擎的底層機制有一個大概的瞭解。 今天我們就先來瞭解下關係型資料庫My ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...