redis知識總結

redis

redis事務

事務對於關係型數據庫很重要的功能。比如redis不支持新建一個帶有過期時間的key,如果用redis實現分佈式鎖,一般就是設置一個key,然後設置過期時間,但是這個過程又不能保證事務性。
redis也提供了事務的功能,但是跟平常理解的事務又有點不太一樣。
redis的事務只能保證要麼全部不執行,要麼全部都執行,而不會在某條命令出錯之後,把之前的命令回滾掉,取而代之的是繼續執行下去。

watch命令

watch可以監視redis中鍵的修改情況,配合事務使用。事務開始之前執行watch命令,監視一個鍵,如果鍵在redis客戶端的exec命令提交之前被其他客戶端修改了,那麼該客戶端的redis_dirty_cas標誌位就會被打開。
當向服務器發送exec命令時,服務器會拒絕執行這個事務。一般還需要在事務執行完畢後,關閉對key的監視

原子性
redis保證事務要麼一個都不執行,要麼全部執行。具有原子性。但是不會像mysql一樣,在執行錯誤後,回滾掉之前的操作。redis的設計者認爲,命令執行出錯是程序問題,不應該出現在生產環境中。回滾操作與redis簡單高效的設計理念是不相符的。

一致性
redis也可以保證一致性。如果提交的事務,在入隊階段就失敗了,比如提交了不存在的命令,那麼事務會被拒絕執行。
執行事務的階段也可能出錯,最常見的原因就是對鍵執行了不支持的命令。這些出錯命令不會對redis產生任何影響。

隔離性
redis使用單線程來執行事務,並且保證執行過程中,不會被中斷。所以總是以串行的方式執行事務中的命令。

redis面試

一篇不錯的博客link

pipeline

redis官方文檔link 翻譯過來就是:

  1. 執行命令的典型的過程就是,服務端通過socket發送命令,阻塞,服務端響應,客戶端繼續執行。即使執行命令的過程很快,但是也會有網絡開銷。
  2. pipeline是一種很老的技術了。客戶端可以發送多個命令到服務器,而無需等待回覆,只需在最後讀取回復即可。
  3. 當客戶端使用流水線發送命令時,服務器將被迫使用內存對答覆進行排隊;如果命令太多,可能會佔用很多內存,redis建議不超過1W。
  4. 其實通過pipeline能降低的不僅是網絡開銷。執行socket IO是比較慢的,這涉及調用read()和write()系統調用,這意味着從用戶態到內核態的轉換,上下文切換是一個巨大的速度懲罰。
    當使用pipeline時,許多命令通常通過單個read()系統調用來讀取,並且通過一次write()系統調用來傳遞多個響應。
redis對象 (redisObject的數據結構)
  1. redis中有的命令是對所有的類型的鍵通用的,DEL,expire,rename。還有一種命令只能對特定類型的鍵執行:set,get等。
  2. 同一種類型的對象又有不同的實現方式,比如列表,有ziplist和linkedlist,當執行llen命令時,會根據具體的存儲類型選擇不同的實現方式。
  3. redis使用引用計數來回收內存。
  4. 值爲整數的字符串對象,會被共享。其他複雜的對象不會被共享,因爲判斷對象是否相等的成本太高。
  5. 上面的操作,通過redisobject對象的type,encoding,ptr和refcount幾個屬性實現。
  6. redisobject的lru屬性記錄對象最後一次被訪問的時間,可用來統計對象的空轉時間。過期策略那裏會被用到。
處理過期鍵

對於有過期時間的鍵,redis專門有expire字典存儲這些鍵。過期字典對應的值,就是鍵的過期時間。專門起一個過期字典,是爲了定期刪除策略的存在。

redis同時採用兩種方法,惰性刪除和定期刪除。
惰性刪除:訪問一個鍵的時候,檢查這個鍵有沒有過期,如果過期把鍵刪除掉。返回空回覆給客戶端。這種方式對內存不友好。
定期刪除:啓動定時線程,訪問數據庫的過期鍵字典,隨機選取數據庫鍵進行檢查,發現過期就刪掉。執行一段時間後,終止掉。下次接着執行。

當redis是主動架構的時候,如果客戶端訪問的是從庫,從庫發現數據庫鍵過期,也不會把鍵刪除,而是把相應的值取出來返回給客戶端。這麼做是爲了保持
主從數據庫的一致性。

只有當客戶端訪問主庫,主庫發現鍵已經過期,就會把過期鍵刪掉,同時給從庫發送命令刪除這個鍵。這時再訪問從庫,也不會訪問到這個過期的鍵了。

rdb && aof(append only file)

redis是內存型的數據庫,一旦服務器宕機,數據就不見了,當然這對內存數據庫是可以容忍的。除非你在redis中存儲的是原數據本身,而不是數據副本。
針對這個問題,redis提供了兩種方式解決就是rdb和aof。

  1. rdb

rdb直譯過來就是redis database,保存redis數據庫中所有的鍵值對。當且僅當redis數據庫重啓的時候,會從rdb中讀取內容,恢復數據。rdb就是已一定格式保存redis鍵值對的文件。

問題1:這個文件什麼時候更新?
當我們調用save或者bgsave命令的時候,會生成rdb文件。save:會阻塞服務器,生成rdb文件期間,不能響應客戶端請求。一般來說對於互聯網應用是不能容忍的。
[bgsave]:服務器新起子線程來完成這項工作,這是服務器可正常接受響應。

實際工作過程中,應該要以定時任務的方式來做這項工作,不能靠人工輸入命令來保證。一般的解決方案就是,1.要麼定期執行,2.要麼服務器產生變更之後,自動執行。

自動間隔性保存
向服務器提供配置:save 300 10,表示服務器在300秒之內,至少進行10次修改,bgsave命令就要被執行了。採用這種方式要求服務器記錄兩個屬性:
dirtylastsave。dirty是上次bgsave以來,執行了多少次修改數據庫命令;lastsave是上次執行bgsave的時間。爲什麼要記錄dirty參數呢?
當服務器沒有變更的時候, 顯然是不需要重新執行保存命令了。

還有個問題沒想明白? 執行bgsave命令期間,客戶端又對數據庫產生了新的變更,那麼這個新的變更,怎麼被保存下來。

  1. aof

看到名字,第一印象想到的竟然是[append of file],慚愧。

相比於rdb的實現方式,aof記錄的是更改數據庫的命令,類似於數據庫的binlog。實時性比較強。
aof就是以一種append的方式,不斷的向文件末尾添加redis命令,更新文件速度還是比較快的。我們知道所有更改文件的操作,由於操作系統提供的文件緩衝功能,一般不會直接落盤
所以常見都是有三種解決方案:

  1. 執行完一次命令後,強制刷盤。最大程度保證數據的可靠性,最多隻會丟失一條命令而已,但是頻繁刷盤,性能較低。
  2. 定時刷盤。比如一秒鐘刷盤一次。折衷方案。
  3. 全部依賴操作系統。這種情況下,當文件緩衝區滿了之後,由操作系統自動刷盤。隔的時間最長,可靠性最差。

aof的問題:隨着時間流逝,aof文件內容越來越多,a文件也越來越大。因爲可能會對一個鍵產生很多次操作,aof要記錄很多條命令,其實很多情況下都是浪費的。
可以直接讀取數據庫當前的狀態,針對一個鍵生成一條命令來大量減少aof文件的大小。

aof後臺重寫功能:類似於bgsave後臺生成rdb文件一樣,也可以通過讀取數據庫狀態,後臺生成aof文件。

看下aof如何解決剛纔說的bgsave命令時,產生新變更的問題。當執行aof重寫時,服務器會新開一個aof重寫緩衝區,redis服務器執行完一個命令後,它會同時將命令發送給這個緩衝區。
當子進程將數據庫的內容讀取完之後,再將aof重寫緩衝區的內容讀入新的aof文件。這一切做完之後,通過rename操作將重寫之後的aof文件,命名爲正式的aof文件。這樣就完成了新舊文件的替換。

注意aof重寫的時候,原來的aof文件還是繼續更新的。在aof重寫完成後,舊的aof文件就不起作用了。

事件模型

redis是基於Reactor模式開發的網絡事件處理器。redis中的事件分爲兩類:文件時間和時間事件。

文件事件:是對套接字操作的抽象,包括服務端於客戶端的連接,通信,以及主從服務器的複製等。redis是單線程的服務器,通過多路複用技術,支持多個連接。
redis也爲每一類事件綁定了一類處理器,當事件發生時,提交給處理器處理。

時間事件:定時事件和週期事件(目前只有週期事件)。redis所有的時間事件保存在一個無序鏈表裏,要遍歷完整個鏈表才能拿到最快到期的事件。週期事件現在由serverCron負責:
更新服務器狀態、清理過期鍵、嘗試持久化操作、主從同步、集羣間通信等。

事件處理過程:服務器對文件事件和時間事件的處理都是同步的、原子的執行,不會出現搶佔或中斷當前事件的情況。服務執行過程中,就是在一個循環中不斷的處理文件事件和時間事件。如果時間事件沒有到期,
那麼會接着處理文件事件。[對於比較費時的時間事件,redis通常採用子線程或者子進程來執行]

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章