通過簡單的KV資料庫理解Redis 分為訪問模塊,操作模塊,索引模塊,存儲模塊 底層數據結構 除了String類型,其他類型都是一個鍵對應一個集合,鍵值對的存儲結構採用哈希表 哈希表由多個哈希桶組成,桶中存儲entry元素,存儲key和value的地址 但是當hash衝突元素過多會導致查詢效率變慢, ...
分為訪問模塊,操作模塊,索引模塊,存儲模塊
除了String類型,其他類型都是一個鍵對應一個集合,鍵值對的存儲結構採用哈希表
哈希表由多個哈希桶組成,桶中存儲entry元素,存儲key和value的地址
但是當hash衝突元素過多會導致查詢效率變慢,所以引入rehash操作
採用兩個全局hash表,但是從一個哈希表複製到另一個哈希表肯定會造成線程阻塞,所以使用漸進式哈希 :分攤到多次拷貝
接受第一次請求就拷貝第一個索引的entry元素,下一次再拷貝第二個,以此類推
對於集合類型的底層數據結構:雙向鏈表,壓縮列表,哈希表,跳錶,整數數組
壓縮列表:
跳錶:
增加多級索引,通過索引位置的跳轉,快速找到元素 (時間複雜度為logn)
不同操作所對應的時間複雜度也不同
對於單個元素的操作一般為O(1)
範圍操作一般為O(n),一般使用scan來代替
統計操作,因為底層的數據結構中有元素個數的統計,所以時間複雜度為O(1)
單線程(多路復用)不會阻塞在一個客戶端的請求連接上,因為在請求過程中有可能會因為監聽到有連接請求但是遲遲沒有建立起連接或者建立起連接數據一直沒有到達都會發生阻塞,所以採用多路復用非阻塞結構,允許在內核中存在多個監聽套接字和已連接套接字,採用**事件回調機制**,當有事件到來,進入一個隊列,redis處理隊列中的請求,針對每個事件都有相應的回調函數
在記憶體中操作數據
高效的底層數據結構
mysql資料庫是寫前日誌,redis是寫後日誌,所以不會阻塞當前的寫操作
但是有兩個風險:1.redis宕機,有丟失一段時間內數據的可能 2.因為在主線程中進行aof操作,有可能會阻塞下一個操作
但是隨著時間的推移,aof文件越來越大怎麼辦,數據恢復效率肯定會降低?
引入aof重寫
多變一,把多條對同一個鍵的舊操作合併成最新的一條操作
但是aof重寫會阻塞主線程嗎?
不會的,和aof追加寫入不一樣,採用的不是主線程,是由主線程fork出的子線程bgrewriteaof線程重寫,子線程里也有父線程的記憶體的最新數據(共用頁表)
在重寫過程中,客戶端的操作也會由主線程寫入當前aof的緩衝區和重寫aof的緩衝區,保證宕機也是數據齊全的,重寫之後數據也是最新的
把某一時刻的狀態記錄到磁碟上
兩個命令生成RDB:1. save,使用主線程,會阻塞主線程 2. bgsave,使用子線程,避免了阻塞問題(但是主線程只可讀,不可寫)
那麼在記錄到磁碟的時間段內,怎麼保證數據不被修改?
使用bgsave+寫時複製:主線程修改處於RDB中的數據時,需要生成一個副本,子進程把副本數據寫進RDB文件
雖然RDB恢復速度快,但是進行持久化的時間間隔卻不好把控,所以Redis4.0推出AOF和RDB結合的方法:在兩次RDB持久化期間,期間發生的改動由AOF記錄,這樣恢複數據即快,又避免了頻繁fork子線程對主線程的阻塞。
主從同步過程
第一步:從庫發送同步命令,包括主庫ID和複製進度offset
第二步:主庫生成RDB文件,發送RDB文件,全量複製,發送包含兩個參數:主庫ID和複製進度offset
第三步:主庫把第二步過程中收到的命令發送到從庫
那麼當從庫增多,主庫fork子線程生產RDB的壓力必然會增大,所以採用主-從-從架構,進行從庫之間的級聯,取一些從庫幫主庫分擔壓力
那麼網路斷了怎麼辦?
redis2.8之前是再進行一次全量複製
之後是進行增量複製
在redis中存在一個環形緩衝區,記錄主庫寫的進度和從庫複製的進度,由兩個參數確定,master-repl-offset和slave-repl-offset
網路斷開再重連之後,從庫根據offset位置進行增量複製就好
但是如果從庫複製進度趕不上主庫寫的進度,緩衝區就會被覆蓋,就會觸發從庫的全量複製,所以一般增大緩衝區為2倍