Python面試彙總(三)——redis

Redis是什麼?

  1. 是一個完全開源免費的key-value內存數據庫
  2. 通常被認爲是一個數據結構服務器,主要是因爲其有着豐富的數據結構 strings、map、 list、sets、 sorted sets

redis支持的5種數據結構

https://blog.csdn.net/qq_36299025/article/details/92851603

Redis數據庫

​ 通常侷限點來說,Redis也以消息隊列的形式存在,作爲內嵌的List存在,滿足實時的高併發需求。在使用緩存的時候,redis比memcached具有更多的優勢,並且支持更多的數據類型,把redis當作一箇中間存儲系統,用來處理高併發的數據庫操作

redis特點

  • 持久化:將內存中的數據保存在磁盤中,重啓的時候可以再次加載進行使用
  • 支持多種數據類型:String, list, set, zset, hash
  • 數據備份:master(主) - slave(從) 模式的數據備份
  • 高性能:讀速度110000次/s,寫速度81000次/s
  • 原子性:所有操作都是原子性的

    Redis缺點

  • 是數據庫容量受到物理內存的限制,不能用作海量數據的高性能讀寫,因此Redis適合的場景主要侷限在較小數據量的高性能操作和運算上。
  • Redis較難支持在線擴容,在集羣容量達到上限時在線擴容會變得很複雜。爲避免這一問題,運維人員在系統上線時必須確保有足夠的空間,這對資源造成了很大的浪費。

什麼是Redis持久化?Redis有哪幾種持久化方式?優缺點是什麼?

持久化就是把內存的數據寫到磁盤中去,防止服務宕機了內存數據丟失。

Redis 提供了兩種持久化方式:RDB(默認) 和AOF 

RDB是Redis默認的持久化方式。按照一定的時間週期策略把內存的數據以快照的形式保存到硬盤的二進制文件。即Snapshot快照存儲,對應產生的數據文件爲dump.rdb,通過配置文件中的save參數來定義快照的週期。( 快照可以是其所表示的數據的一個副本,也可以是數據的一個複製品。)
AOF:Redis會將每一個收到的寫命令都通過Write函數追加到文件最後,類似於MySQL的binlog。當Redis重啓是會通過重新執行文件中保存的寫命令來在內存中重建整個數據庫的內容
當兩種方式同時開啓時,數據恢復Redis會優先選擇AOF恢復。

RDB

RDB的持久化方式是通過快照方式完成的,當符合某種規則時,會將內存中的數據全量生成一份副本存到硬盤行,這個過程稱爲“快照”。

快照執行原理

(1) Redis使用fork函數複製一份當前進程(父進程)的副本(子進程)

(2) 父進程繼續處理來自客戶端的請求,子進程開始將內存中的數據寫入硬盤中的臨時文件

(3) 當子進程寫完所有的數據後,用該臨時文件替換舊的RDB文件,至此,一次快照操作完成

(4) 注意:在執行fork的時候操作系統(類Unix操作系統)會使用寫時複製(copy-on-write)策略,即fork函數發生的一刻,父進程和子進程共享同一塊內存數據,當父進程需要修改其中的某片數據(如執行寫命令)時,操作系統會將該片數據複製一份以保證子進程不受影響,所以RDB文件存儲的是執行fork操作那一刻的內存數據。所以RDB方式理論上是會存在丟數據的情況的(fork之後修改的的那些沒有寫進RDB文件)

快照備份規則

(1) 根據配置規則進行自動快照:在配置文件redis/etc/redis.conf內,進行自定義快照條件。在快照條件缺省的條件下,快照會存放在簡介中介紹的dump.rdb文件中。自定義快照條件則是通過「save time(時長, 單位s) num(變化key的個數) 」對進行快照備份的頻率進行設置,例如「 save 900 1 」表示15分鐘內1個key發生變化,進行快照備份。每個快照條件單獨佔一行,條件之間是‘or '或的關係,滿足任何一個就進行自動快照備份

(2) 用戶執行SAVE,BGSAVE命令:除了讓Redis自動進行快照外,當我們需要重啓,遷移,備份Redis時,我們也可以手動執行SAVE或BGSAVE命令主動進行快照操作。

  • SAVE命令當執行SAVE命令時,Redis同步進行快照操作,期間會阻塞所有來自客戶端的請求,所以放數據庫數據較多時,應該避免使用該命令;
  • BGSAVE命令從命令名字就能看出來,這個命令與SAVE命令的區別就在於該命令的快照操作是在後臺異步進行的,進行快照操作的同時還能處理來自客戶端的請求。執行BGSAVE命令後Redis會馬上返回OK表示開始進行快照操作,如果想知道快照操作是否已經完成,可以使用LASTSAVE命令返回最近一次成功執行快照的時間,返回結果是一個Unix時間戳。

(3) 執行FLUSHALL命令:當執行FLUSHALL命令時,Redis會清除數據庫中的所有數據。需要注意的是:不論清空數據庫的過程是否觸發了自動快照的條件,只要自動快照條件不爲空,Redis就會執行一次快照操作,當沒有定義自動快照條件時,執行FLUSHALL命令不會進行快照操作。

(4) 執行復制(replication)操作:當設置了主從模式時,Redis會在複製初始化時進行自動快照。

數據遷移

問題:有三個Redis,分別命名爲Redis1、Redis2、Redis3;現在希望將Redis1和Redis2中的數據遷移到Redis3中,同時不希望Reds3中rdb的數據丟失,那麼如何將三個rdb文件融合爲一個呢?

解決方式一:將Rdeis3先設置爲Redis1的從數據庫,進行數據同步;然後在將Redis3設置爲Redis2的從數據庫,進行數據同步。

解決方式二:(注意該方法適合用於臨時恢復和導出數據,且需要關閉AOF持久化功能)

(1) 複製RDB文件:將Redis1的dump1.rdb、Redis2的dump2.rdb複製到Redis3的data目錄下

(2) 獲取RDB文件大小:dump1.rdb大小 = 27441041、dump2.rdb大小 = 37、dump3.rd大小 = 570214520

(3) 按照文件合併順序截取文件:dump1.rdb爲文件頭,去掉尾部的9個字節:「dd bs=1 if=dump1.rdb of=dump.rdb count=27441032」

      dump2.rdb爲中間文件,去掉頭部的11個字節,去掉尾部的9個字節:「dd bs=1 if=dump2.rdb of=res2.rdb count=17」

      dump3.rdb爲尾部文件,去掉頭部的11個字節,去掉北部的8個字節:「dd bs=1 if=dump3.rdb of=res3.rdb count=570214501」

(4) 合併截取文件:cat res2.rdb >> dump.rdb

cat res3.rdb >> dump.rdb

(5) 驗證備份文件:執行命令「redis-check-rdb dump.rdb 」

(6) 修改配置文件:將redis.conf中的rdbchecksum設置爲no

(7) 重啓Redis3,所有備份數據在數據庫內生效

AOF

AOF的持久化方式是將Redsi執行的每一條命令追加寫到硬盤文件中,這一過程坑你會降低Redis的性能,但大部分情況這個影響是可以接收的,默認情況下,AOF的持計劃功能是關閉的,可以通過修改redis.conf文件的配置進行功能開啓,本分文件的存儲路徑與dump.rdb是一致的。

實現過程

(1) 概述:AOF以純文本的形式記錄Redis執行的寫命令,將Redis按照上面提到的配置打開AOF持久化功能,執行命令「set test ceshi」,打開appendonly.aof 查看持久化內容信息,其文本中記錄命令的規則這裏不展開細說,感興趣可以深入學習一下

(2) AOF文件重寫:AOF記錄用戶操作Redis的命令行,但如果每一條命令行都記錄,那appendonly.aof文件會變的特別大,所以Redis設計了AOF文件重寫機制。重寫機制就是重新生成一份AOF文件,新的AOF文件中一條記錄的操作只會有一次,而不像老文件中存在對同一數值進行多次操作的現象。重寫的實現過程與RDB備份快照的過程類似,也是fork一個進程,直接遍歷數據,寫入新的AOF臨時文件。在寫入新文件的過程中,所有的寫操作日誌還是會寫到原來老的AOF文件中,同時還會記錄在內存緩衝區中。當重完操作完成後,會將所有緩衝區中的日誌一次性寫入到臨時文件中。然後調用原子性的rename命令用新的 AOF文件取代老的AOF文件。

如下圖中的操作,重寫後的AOF文件只會記錄最後一條命令「set test ceshi3」

AOF文件重寫規則

(1) 根據配置規則進行自動重寫:在配置文件redis/etc/redis.conf內,進行自定義重寫條件。

  • auto-aof-rewrite-percentage:目前AOF文件的大小超出上一次重寫時AOF文件大小的百分之多少時進行重寫,如果沒有重寫過就以啓動時AOF文件的大小爲標準
  • auto-aof-rewrite-min-size:當AOF文件的大小超過設定的數字時進行重寫

通常以上兩個配置是一起使用

(2) 用戶執行BGREWRITEAOF命令進行重寫:執行BGREWRITEAOF命令後,AOF文件中僅保留了「set test ceshi3」命令

同步硬盤

雖然每次執行更改數據庫的內容時,AOF都會記錄執行的命令,但是由於操作系統本身的硬盤緩存的緣故,AOF文件的內容並沒有真正地寫入硬盤,在默認情況下,操作系統會每隔30s將硬盤緩存中的數據同步到硬盤,但是爲了防止系統異常退出而導致丟數據的情況發生,我們還可以在Redis的配置文件中配置這個同步的頻率

RDB與AOF的優劣勢

二者選擇的標準,就是看系統是願意犧牲一些性能,換取更高的緩存一致性(AOF),還是願意寫操作頻繁的時候,不啓用備份來換取更高的性能,待手動運行save的時候,再做備份(RDB)

RDB的優勢:(1) 數據可移植性好:僅存在一個數據備份文件,對數據壓縮後可以輕鬆轉移到其他存儲介質上;(2) 性能最大化:RDB本分方式只需fork一個子進程,由子進程完成全部持久化工作,極大的避免了服務進程執行IO操作,當數據集很大時,其效率也高於AOF機制;

RDB的劣勢:(1) 在數據未持久化之前出現宕機現象,那麼未持久化的數據會丟失;(2) 當數據集較大時,fork子進程的備份工作會導致服務整體停止對外服務幾百毫秒,甚至1秒

AOF的優勢:(1) 數據安全性更高:寫Redis命令採用append的模式記錄,出現宕機,原有數據不會丟失,寫入了一半數據就出現了系統崩潰問題,不用擔心,在Redis下一次啓動之前,我們可以通過redis-check-aof工具來幫助我們解決數據一致性的問題;

AOF的劣勢:(1) 相同數據集,AOF文件大於RDB文件,且恢復速度慢於RDB;(2) 數據同步效率低於RDB

Redis主從複製原理

原理概述:一主多從 (1個master,多個slave) ;全量同步&增量同步。

一主多從:主數據庫進行讀寫操作,當發生寫操作時自動將數據同步到從數據庫;從數據庫一般只進行讀操作,並接收主數據庫同步過來的數據,一個主數據庫可以有多個從數據庫,但一個從數據庫只能有一個主數據庫。通過這種架構能夠很好的實現讀寫分離,從而提高服務器的負載能力。

全量同步:

    • 當數據庫啓動時,會向主數據庫發送sync命令
    • 主數據庫接收到sync命令會開始在後臺保存快照(執行rdb操作),並用緩存區記錄後續的所有寫操作
    • 當主服務器的快照保存完成後,redis會將快照文件發送給從數據庫
    • 從數據庫接收快照文件後,會丟棄所有的舊數據,載入收到的快照
    • 主服務器的快照發送完畢後,開始向從服務器發送緩衝區中的寫命令
    • 從服務器完成對快照對載入,開始接收命令請求,並執行來自主服務器緩衝區的寫命令

增量同步:

    • 從數據庫初始化工作完成後,主數據庫發生的寫操作同步到從數據庫到過程爲增量同步
    • 增量複製的過程主要是主服務器每執行一個寫命令就會向從服務器發送相同的寫命令,從服務器接收並執行收到的寫命令。

全量&增量同步選擇:

    • 主數據庫啓動創建鏈接時,採用全量同步
    • 全量同步結束後,採用增量同步
    • 如果需要,slave在任何時候都可以發起全量同步,redis的策略是首先嚐試增量同步,如果失敗,進行全量同步

redis主從配置:

(1)在slave的redis.conf中修改配置信息,ip : port對應master的ip : port,masterauth參數對應master的密碼,有則配置,沒有則不用關

(2)啓動slave:./bin/redis-server etc/redis.conf

(3)登錄master:./redis-cli -p 'port',輸入info,查看slave是否配置成功;

(4)登錄slave也可以查看:./redis-cli -p 'port',輸入info

(5)如果master掛了,需要將slave切換成master登錄slave:./redis-cli -p 'port',執行:SLAVEOF NO ONE,執行:info

(6)master修復後,可在slave上執行:SLAVEOF 127.0.0.1 'port'(SLAVEOF master_ip master_port),重新將其掛接在master上

(六) Redis的原子性

原子性概念:一個事務中的所有操作,要麼全部完成,要不全部不完成,不會結束在之間某個環節,Redis的原子性就是指其操作命令要麼執行,要麼不執行

Redis操作原子性的原因:Redis是單線程的,線程是操作系統運算調度的最小單元

(七) Redis的事務性

MULTI, EXEC, DISCARD and WATCH 是Redis事務的基礎。用來顯式開啓並控制一個事務,它們允許在一個步驟中執行一組命令。並提供兩個重要的保證:

    事務中的所有命令都會被序列化並按順序執行。在執行Redis事務的過程中,不會出現由另一個客戶端發出的請求。這保證 命令隊列 作爲一個單獨的原子操作被執行。
    隊列中的命令要麼全部被處理,要麼全部被忽略。EXEC命令觸發事務中所有命令的執行,因此,當客戶端在事務上下文中失去與服務器的連接,
        如果發生在調用MULTI命令之前,則不執行任何commands;
        如果在此之前EXEC命令被調用,則所有的commands都被執行。

Redis爲什麼會這麼快

1、Redis是純內存操作,需要的時候需要我們手動持久化到硬盤中

2、Redis是單線程,從而避開了多線程中上下文頻繁切換的操作。

3、Redis數據結構簡單、對數據的操作也比較簡單

4、使用底層模型不同,它們之間底層實現方式以及與客戶端之間通信的應用協議不一樣,Redis直接自己構建了VM 機制 ,因爲一般的系統調用系統函數的話,會浪費一定的時間去移動和請求

5、使用多路I/O複用模型,非阻塞I/O

多路I/O複用: I/O 多路複用技術是爲了解決進程或線程阻塞到某個 I/O 系統調用而出現的技術,可以監視多個描述符,一旦某個描述符就緒(一般是讀就緒或者寫就緒,就是這個文件描述符進行讀寫操作之前),能夠通知程序進行相應的讀寫操作

redis的過期策略以及內存淘汰機制

問題:比如你redis只能存5G數據,可是你寫了10G,那會刪5G的數據。怎麼刪的,這個問題思考過麼?還有,你的數據已經設置了過期時間,但是時間到了,內存佔用率還是比較高,有思考過原因麼? 

策略:定期刪除 + 惰性刪除策略 + 內存淘汰機制

定期刪除策略的工作原理:定時刪除,用一個定時器來負責監視key,過期則自動刪除。雖然內存及時釋放,但是十分消耗CPU資源。在大併發請求下,CPU要將時間應用在處理請求,而不是刪除key,因此沒有采用這一策略。

定期刪除 + 惰性刪除策略的工作原理:定期刪除,redis默認每個100ms檢查,是否有過期的key,有過期key則刪除。需要說明的是,redis不是每個100ms將所有的key檢查一次,而是隨機抽取進行檢查(如果每隔100ms,全部key進行檢查,redis豈不是卡死)。因此,如果只採用定期刪除策略,會導致很多key到時間沒有刪除。於是,惰性刪除派上用場。也就是說在你獲取某個key的時候,redis會檢查一下,這個key如果設置了過期時間那麼是否過期了?如果過期了此時就會刪除。

內存淘汰機制:如果定時刪除沒有刪除key,用戶也沒有及時請求key,即惰性刪除也沒有生效,這樣redis的內存使用率會越來越高,那麼就需要使用內存淘汰機制。在redis/etc/redis.conf中會有內存淘汰機制的配置規則:

該配置爲redis內存淘汰策略的設定配置:

  • volatile-lru : 當內存不足以容納新寫入數據時,在設置了過期時間的鍵空間中,移除最近最少使用的key。這種情況一般是把redis既當緩存,又做持久化存儲的時候才用。
  • allkeys-lru : 當內存不足以容納新寫入數據時,在鍵空間中,移除最近最少使用的key。推薦使用。
  • volatile-random : 當內存不足以容納新寫入數據時,在設置了過期時間的鍵空間中,隨機移除某個key。
  • allkeys-random : 當內存不足以容納新寫入數據時,在鍵空間中,隨機移除某個key。應該也沒人用吧,你不刪最少使用Key,去隨機刪。
  • volatile-ttl : 當內存不足以容納新寫入數據時,在設置了過期時間的鍵空間中,有更早過期時間的key優先移除。
  • noeviction : 當內存不足以容納新寫入數據時,新寫入操作會報錯。應該沒人用吧。

同時有多個子系統去set一個key。這個時候要注意什麼呢?

不推薦使用redis的事務機制。因爲我們的生產環境,基本都是redis集羣環境,做了數據分片操作。你一個事務中有涉及到多個key操作的時候,這多個key不一定都存儲在同一個redis-server上。因此,redis的事務機制,十分雞肋。
(1)如果對這個key操作,不要求順序: 準備一個分佈式鎖,大家去搶鎖,搶到鎖就做set操作即可
(2)如果對這個key操作,要求順序: 分佈式鎖+時間戳。 假設這會系統B先搶到鎖,將key1設置爲{valueB 3:05}。接下來系統A搶到鎖,發現自己的valueA的時間戳早於緩存中的時間戳,那就不做set操作了。以此類推。
(3) 利用隊列,將set方法變成串行訪問也可以

redis遇到高併發,如果保證讀寫key的一致性
對redis的操作都是具有原子性的,是線程安全的操作,你不用考慮併發問題,redis內部已經幫你處理好併發的問題了。

Redis主要有哪些功能?


1.哨兵(Sentinel)和複製(Replication)
Redis服務器毫無徵兆的罷工是個麻煩事,如何保證備份的機器是原始服務器的完整備份呢?這時候就需要哨兵和複製。

哨兵Sentinel可以管理多個Redis服務器,它提供了監控,提醒以及自動的故障轉移的功能,Replication則是負責讓一個Redis服務器可以配備多個備份的服務器。

Redis也是利用這兩個功能來保證Redis的高可用的。

2.事務
很多情況下我們需要一次執行不止一個命令,而且需要其同時成功或者失敗。redis對事務的支持也是源自於這部分需求,即支持一次性按順序執行多個命令的能力,並保證其原子性。

3.LUA腳本
在事務的基礎上,如果我們需要在服務端一次性的執行更復雜的操作(包含一些邏輯判斷),則lua就可以排上用場了。

4.持久化
redis的持久化指的是redis會把內存的中的數據寫入到硬盤中,在redis重新啓動的時候加載這些數據,從而最大限度的降低緩存丟失帶來的影響。

5.集羣(Cluster)
單臺服務器資源的總是有上限的,CPU資源和IO資源我們可以通過主從複製,進行讀寫分離,把一部分CPU和IO的壓力轉移到從服務器上,這也有點類似mysql數據庫的主從同步。

 Redis爲什麼是單線程的?

多線程處理會涉及到鎖,而且多線程處理會涉及到線程切換而消耗CPU。因爲CPU不是Redis的瓶頸,Redis的瓶頸最有可能是機器內存或者網絡帶寬。單線程無法發揮多核CPU性能,不過可以通過在單機開多個Redis實例來解決。

使用Redis的優勢?

1、速度快,因爲數據存在內存中,類似於HashMap,HashMap的優勢就是查找和操作的時間複雜度都是O(1)

2、支持豐富數據類型,支持string,list,set,sorted set,hash

3、支持事務,操作都是原子性,所謂的原子性就是對數據的更改要麼全部執行,要麼全部不執行

4、豐富的特性:可用於緩存,消息,按key設置過期時間,過期後將會自動刪除

Redis讀寫分離模型

通過增加Slave DB的數量,讀的性能可以線性增長。爲了避免Master DB的單點故障,集羣一般都會採用兩臺Master DB做雙機熱備,所以整個集羣的讀和寫的可用性都非常高。

讀寫分離架構的缺陷在於,不管是Master還是Slave,每個節點都必須保存完整的數據,如果在數據量很大的情況下,集羣的擴展能力還是受限於單個節點的存儲能力,而且對於Write-intensive類型的應用,讀寫分離架構並不適合。

Redis數據分片模型

爲了解決讀寫分離模型的缺陷,可以將數據分片模型應用進來。
可以將每個節點看成都是獨立的master,然後通過業務實現數據分片。
結合上面兩種模型,可以將每個master設計成由一個master和多個slave組成的模型。

 Redis常見性能問題和解決方案?

(1) Master最好不要做任何持久化工作,如RDB內存快照和AOF日誌文件
(2) 如果數據比較重要,某個Slave開啓AOF備份數據,策略設置爲每秒同步一次
(3) 爲了主從複製的速度和連接的穩定性,Master和Slave最好在同一個局域網內
(4) 儘量避免在壓力很大的主庫上增加從庫
(5) 主從複製不要用圖狀結構,用單向鏈表結構更爲穩定,即:Master <- Slave1 <- Slave2 <- Slave3...
這樣的結構方便解決單點故障問題,實現Slave對Master的替換。如果Master掛了,可以立刻啓用Slave1做Master,其他不變。

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