1. 程式人生 > >一流網際網路公司面試Redis都問些什麼

一流網際網路公司面試Redis都問些什麼

Reids的特點

Redis本質上是一個Key-Value型別的記憶體資料庫,很像memcached,整個資料庫統統載入在記憶體當中進行操作,定期通過非同步操作把資料庫資料flush到硬碟上進行儲存。因為是純記憶體操作,Redis的效能非常出色,每秒可以處理超過 10萬次讀寫操作,是已知效能最快的Key-Value DB。

Redis的出色之處不僅僅是效能,Redis最大的魅力是支援儲存多種資料結構,此外單個value的最大限制是1GB,不像 memcached只能儲存1MB的資料,因此Redis可以用來實現很多有用的功能,比方說用他的List來做FIFO雙向連結串列,實現一個輕量級的高性 能訊息佇列服務,用他的Set可以做高效能的tag系統等等。另外Redis也可以對存入的Key-Value設定expire時間,因此也可以被當作一 個功能加強版的memcached來用。

Redis的主要缺點是資料庫容量受到實體記憶體的限制,不能用作海量資料的高效能讀寫,因此Redis適合的場景主要侷限在較小資料量的高效能操作和運算上。

 

使用Redis有哪些好處?

(1) 速度快,因為資料存在記憶體中,類似於HashMap,HashMap的優勢就是查詢和操作的時間複雜度都是O(1)

(2) 支援豐富資料型別,支援string,list,set,sorted set,hash

(3) 支援事務,操作都是原子性,所謂的原子性就是對資料的更改要麼全部執行,要麼全部不執行

(4) 豐富的特性:可用於快取,訊息,按key設定過期時間,過期後將會自動刪除

 

Redis支援的資料型別

Redis通過Key-Value的單值不同型別來區分, 以下是支援的型別:

Strings

Lists

Sets 求交集、並集

Sorted Set 

hashes

 

為什麼redis需要把所有資料放到記憶體中?

Redis為了達到最快的讀寫速度將資料都讀到記憶體中,並通過非同步的方式將資料寫入磁碟。所以redis具有快速和資料持久化的特徵。如果不將資料放在記憶體中,磁碟I/O速度為嚴重影響redis的效能。在記憶體越來越便宜的今天,redis將會越來越受歡迎。

如果設定了最大使用的記憶體,則資料已有記錄數達到記憶體限值後不能繼續插入新值。

 

Redis是單程序單執行緒的

redis利用佇列技術將併發訪問變為序列訪問,消除了傳統資料庫序列控制的開銷

 

虛擬記憶體

當你的key很小而value很大時,使用VM的效果會比較好.因為這樣節約的記憶體比較大.

當你的key不小時,可以考慮使用一些非常方法將很大的key變成很大的value,比如你可以考慮將key,value組合成一個新的value.

vm-max-threads這個引數,可以設定訪問swap檔案的執行緒數,設定最好不要超過機器的核數,如果設定為0,那麼所有對swap檔案的操作都是序列的.可能會造成比較長時間的延遲,但是對資料完整性有很好的保證.

自己測試的時候發現用虛擬記憶體效能也不錯。如果資料量很大,可以考慮分散式或者其他資料庫

 

分散式

redis支援主從的模式。原則:Master會將資料同步到slave,而slave不會將資料同步到master。Slave啟動時會連線master來同步資料。

這是一個典型的分散式讀寫分離模型。我們可以利用master來插入資料,slave提供檢索服務。這樣可以有效減少單個機器的併發訪問數量

 

讀寫分離模型

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

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

                                                             

資料分片模型

為了解決讀寫分離模型的缺陷,可以將資料分片模型應用進來。

可以將每個節點看成都是獨立的master,然後通過業務實現資料分片。

結合上面兩種模型,可以將每個master設計成由一個master和多個slave組成的模型。

 

Redis的回收策略

    • volatile-lru:從已設定過期時間的資料集(server.db[i].expires)中挑選最近最少使用的資料淘汰
    • volatile-ttl:從已設定過期時間的資料集(server.db[i].expires)中挑選將要過期的資料淘汰
    • volatile-random:從已設定過期時間的資料集(server.db[i].expires)中任意選擇資料淘汰
    • allkeys-lru:從資料集(server.db[i].dict)中挑選最近最少使用的資料淘汰
    • allkeys-random:從資料集(server.db[i].dict)中任意選擇資料淘汰
    • no-enviction(驅逐):禁止驅逐資料

 

使用Redis有哪些好處?

(1) 速度快,因為資料存在記憶體中,類似於HashMap,HashMap的優勢就是查詢和操作的時間複雜度都是O(1)

(2) 支援豐富資料型別,支援string,list,set,sorted set,hash

(3) 支援事務,操作都是原子性,所謂的原子性就是對資料的更改要麼全部執行,要麼全部不執行

(4) 豐富的特性:可用於快取,訊息,按key設定過期時間,過期後將會自動刪除

 

redis相比memcached有哪些優勢?

(1) memcached所有的值均是簡單的字串,redis作為其替代者,支援更為豐富的資料型別

(2) redis的速度比memcached快很多

(3) redis可以持久化其資料

 

redis常見效能問題和解決方案:

(1) Master最好不要做任何持久化工作,如RDB記憶體快照和AOF日誌檔案

(2) 如果資料比較重要,某個Slave開啟AOF備份資料,策略設定為每秒同步一次

(3) 為了主從複製的速度和連線的穩定性,Master和Slave最好在同一個區域網內

(4) 儘量避免在壓力很大的主庫上增加從庫

(5) 主從複製不要用圖狀結構,用單向連結串列結構更為穩定,即:Master <- Slave1 <- Slave2 <- Slave3...

這樣的結構方便解決單點故障問題,實現Slave對Master的替換。如果Master掛了,可以立刻啟用Slave1做Master,其他不變。

 

 MySQL裡有2000w資料,redis中只存20w的資料,如何保證redis中的資料都是熱點資料

 相關知識:redis 記憶體資料集大小上升到一定大小的時候,就會施行資料淘汰策略。redis 提供 6種資料淘汰策略:

voltile-lru:從已設定過期時間的資料集(server.db[i].expires)中挑選最近最少使用的資料淘汰

volatile-ttl:從已設定過期時間的資料集(server.db[i].expires)中挑選將要過期的資料淘汰

volatile-random:從已設定過期時間的資料集(server.db[i].expires)中任意選擇資料淘汰

allkeys-lru:從資料集(server.db[i].dict)中挑選最近最少使用的資料淘汰

allkeys-random:從資料集(server.db[i].dict)中任意選擇資料淘汰

no-enviction(驅逐):禁止驅逐資料

 

Memcache與Redis的區別都有哪些?

1)、儲存方式

Memecache把資料全部存在記憶體之中,斷電後會掛掉,資料不能超過記憶體大小。

Redis有部份存在硬碟上,這樣能保證資料的永續性。

2)、資料支援型別

Memcache對資料型別支援相對簡單。

Redis有複雜的資料型別。

3)、使用底層模型不同

它們之間底層實現方式 以及與客戶端之間通訊的應用協議不一樣。

Redis直接自己構建了VM 機制 ,因為一般的系統呼叫系統函式的話,會浪費一定的時間去移動和請求。

4),value大小

redis最大可以達到1GB,而memcache只有1MB

 

Redis 常見的效能問題都有哪些?如何解決?

1).Master寫記憶體快照,save命令排程rdbSave函式,會阻塞主執行緒的工作,當快照比較大時對效能影響是非常大的,會間斷性暫停服務,所以Master最好不要寫記憶體快照。

 

2).Master AOF持久化,如果不重寫AOF檔案,這個持久化方式對效能的影響是最小的,但是AOF檔案會不斷增大,AOF檔案過大會影響Master重啟的恢復速度。Master最好不要做任何持久化工作,包括記憶體快照和AOF日誌檔案,特別是不要啟用記憶體快照做持久化,如果資料比較關鍵,某個Slave開啟AOF備份資料,策略為每秒同步一次。

 

3).Master呼叫BGREWRITEAOF重寫AOF檔案,AOF在重寫的時候會佔大量的CPU和記憶體資源,導致服務load過高,出現短暫服務暫停現象。

4). Redis主從複製的效能問題,為了主從複製的速度和連線的穩定性,Slave和Master最好在同一個區域網內

(1) Master最好不要做任何持久化工作,如RDB記憶體快照和AOF日誌檔案

(2) 如果資料比較重要,某個Slave開啟AOF備份資料,策略設定為每秒同步一次

(3) 為了主從複製的速度和連線的穩定性,Master和Slave最好在同一個區域網內

(4) 儘量避免在壓力很大的主庫上增加從庫

(5) 主從複製不要用圖狀結構,用單向連結串列結構更為穩定,即:Master <- Slave1 <- Slave2 <- Slave3...

這樣的結構方便解決單點故障問題,實現Slave對Master的替換。如果Master掛了,可以立刻啟用Slave1做Master,其他不變。

 

redis 最適合的場景

Redis最適合所有資料in-momory的場景,雖然Redis也提供持久化功能,但實際更多的是一個disk-backed的功能,跟傳統意義上的持久化有比較大的差別,那麼可能大家就會有疑問,似乎Redis更像一個加強版的Memcached,那麼何時使用Memcached,何時使用Redis呢?

       如果簡單地比較Redis與Memcached的區別,大多數都會得到以下觀點:

     1 、Redis不僅僅支援簡單的k/v型別的資料,同時還提供list,set,zset,hash等資料結構的儲存。

     2 、Redis支援資料的備份,即master-slave模式的資料備份。

     3 、Redis支援資料的持久化,可以將記憶體中的資料保持在磁碟中,重啟的時候可以再次載入進行使用。

(1)、會話快取(Session Cache)

最常用的一種使用Redis的情景是會話快取(session cache)。用Redis快取會話比其他儲存(如Memcached)的優勢在於:Redis提供持久化。當維護一個不是嚴格要求一致性的快取時,如果使用者的購物車資訊全部丟失,大部分人都會不高興的,現在,他們還會這樣嗎?

幸運的是,隨著 Redis 這些年的改進,很容易找到怎麼恰當的使用Redis來快取會話的文件。甚至廣為人知的商業平臺Magento也提供Redis的外掛。

(2)、全頁快取(FPC)

除基本的會話token之外,Redis還提供很簡便的FPC平臺。回到一致性問題,即使重啟了Redis例項,因為有磁碟的持久化,使用者也不會看到頁面載入速度的下降,這是一個極大改進,類似PHP本地FPC。

再次以Magento為例,Magento提供一個外掛來使用Redis作為全頁快取後端

此外,對WordPress的使用者來說,Pantheon有一個非常好的外掛  wp-redis,這個外掛能幫助你以最快速度載入你曾瀏覽過的頁面。

(3)、佇列

Reids在記憶體儲存引擎領域的一大優點是提供 list 和 set 操作,這使得Redis能作為一個很好的訊息佇列平臺來使用。Redis作為佇列使用的操作,就類似於本地程式語言(如Python)對 list 的 push/pop 操作。

如果你快速的在Google中搜索“Redis queues”,你馬上就能找到大量的開源專案,這些專案的目的就是利用Redis建立非常好的後端工具,以滿足各種佇列需求。例如,Celery有一個後臺就是使用Redis作為broker,你可以從這裡去檢視。

(4),排行榜/計數器

Redis在記憶體中對數字進行遞增或遞減的操作實現的非常好。集合(Set)和有序集合(Sorted Set)也使得我們在執行這些操作的時候變的非常簡單,Redis只是正好提供了這兩種資料結構。所以,我們要從排序集合中獲取到排名最靠前的10個使用者–我們稱之為“user_scores”,我們只需要像下面一樣執行即可:

當然,這是假定你是根據你使用者的分數做遞增的排序。如果你想返回使用者及使用者的分數,你需要這樣執行:

ZRANGE user_scores 0 10 WITHSCORES

Agora Games就是一個很好的例子,用Ruby實現的,它的排行榜就是使用Redis來儲存資料的,你可以在這裡看到。

(5)、釋出/訂閱

最後(但肯定不是最不重要的)是Redis的釋出/訂閱功能。釋出/訂閱的使用場景確實非常多。我已看見人們在社交網路連線中使用,還可作為基於釋出/訂閱的指令碼觸發器,甚至用Redis的釋出/訂閱功能來建立聊天系統!(不,這是真的,你可以去核實)。

 

redis的併發競爭問題如何解決?

   Redis為單程序單執行緒模式,採用佇列模式將併發訪問變為序列訪問。Redis本身沒有鎖的概念,Redis對於多個客戶端連線並不存在競爭,但是在Jedis客戶端對Redis進行併發訪問時會發生連線超時、資料轉換錯誤、阻塞、客戶端關閉連線等問題,這些問題均是

由於客戶端連線混亂造成。對此有2種解決方法:

   1.客戶端角度,為保證每個客戶端間正常有序與Redis進行通訊,對連線進行池化,同時對客戶端讀寫Redis操作採用內部鎖synchronized。

   2.伺服器角度,利用setnx實現鎖。

   注:對於第一種,需要應用程式自己處理資源的同步,可以使用的方法比較通俗,可以使用synchronized也可以使用lock;第二種需要用到Redis的setnx命令,但是需要注意一些問題。

 

redis事物的瞭解CAS(check-and-set 操作實現樂觀鎖 )?

    和眾多其它資料庫一樣,Redis作為NoSQL資料庫也同樣提供了事務機制。在Redis中,MULTI/EXEC/DISCARD/WATCH這四個命令是我們實現事務的基石。相信對有關係型資料庫開發經驗的開發者而言這一概念並不陌生,即便如此,我們還是會簡要的列出

    Redis中

  事務的實現特徵:

    1). 在事務中的所有命令都將會被序列化的順序執行,事務執行期間,Redis不會再為其它客戶端的請求提供任何服務,從而保證了事物中的所有命令被原子的執行。

    2). 和關係型資料庫中的事務相比,在Redis事務中如果有某一條命令執行失敗,其後的命令仍然會被繼續執行。

    3). 我們可以通過MULTI命令開啟一個事務,有關係型資料庫開發經驗的人可以將其理解為"BEGIN TRANSACTION"語句。在該語句之後執行的命令都將被視為事務之內的操作,最後我們可以通過執行EXEC/DISCARD命令來提交/回滾該事務內的所有操作。這兩

      個Redis命令可被視為等同於關係型資料庫中的COMMIT/ROLLBACK語句。

    4). 在事務開啟之前,如果客戶端與伺服器之間出現通訊故障並導致網路斷開,其後所有待執行的語句都將不會被伺服器執行。然而如果網路中斷事件是發生在客戶端執行EXEC命令之後,那麼該事務中的所有命令都會被伺服器執行。

    5). 當使用Append-Only模式時,Redis會通過呼叫系統函式write將該事務內的所有寫操作在本次呼叫中全部寫入磁碟。然而如果在寫入的過程中出現系統崩潰,如電源故障導致的宕機,那麼此時也許只有部分資料被寫入到磁碟,而另外一部分資料卻已經丟失。

      Redis伺服器會在重新啟動時執行一系列必要的一致性檢測,一旦發現類似問題,就會立即退出並給出相應的錯誤提示。此時,我們就要充分利用Redis工具包中提供的redis-check-aof工具,該工具可以幫助我們定位到資料不一致的錯誤,並將已經寫入的部

      分資料進行回滾。修復之後我們就可以再次重新啟動Redis伺服器了。

 

WATCH命令和基於CAS的樂觀鎖: 

   在Redis的事務中,WATCH命令可用於提供CAS(check-and-set)功能。假設我們通過WATCH命令在事務執行之前監控了多個Keys,倘若在WATCH之後有任何Key的值發生了變化,EXEC命令執行的事務都將被放棄,同時返回Null multi-bulk應答以通知呼叫者事務

 執行失敗。例如,我們再次假設Redis中並未提供incr命令來完成鍵值的原子性遞增,如果要實現該功能,我們只能自行編寫相應的程式碼。其偽碼如下:

  val = GET mykey

  val = val + 1

  SET mykey $val

  以上程式碼只有在單連線的情況下才可以保證執行結果是正確的,因為如果在同一時刻有多個客戶端在同時執行該段程式碼,那麼就會出現多執行緒程式中經常出現的一種錯誤場景--競態爭用(race condition)。比如,客戶端A和B都在同一時刻讀取了mykey的原有值,假設該值為10,此後兩個客戶端又均將該值加一後set回Redis伺服器,這樣就會導致mykey的結果為11,而不是我們認為的12。為了解決類似的問題,我們需要藉助WATCH命令的幫助,見如下程式碼:

  WATCH mykey

  val = GET mykey

  val = val + 1

  MULTI

  SET mykey $val

  EXEC

  和此前程式碼不同的是,新程式碼在獲取mykey的值之前先通過WATCH命令監控了該鍵,此後又將set命令包圍在事務中,這樣就可以有效的保證每個連線在執行EXEC之前,如果當前連接獲取的mykey的值被其它連線的客戶端修改,那麼當前連線的EXEC命令將執行失敗。這樣呼叫者在判斷返回值後就可以獲悉val是否被重新設定成功。

 

redis的快取失效策略和主鍵失效機制

  作為快取系統都要定期清理無效資料,就需要一個主鍵失效和淘汰策略.

  在Redis當中,有生存期的key被稱為volatile。在建立快取時,要為給定的key設定生存期,當key過期的時候(生存期為0),它可能會被刪除。

  1、影響生存時間的一些操作

  生存時間可以通過使用 DEL 命令來刪除整個 key 來移除,或者被 SET 和 GETSET 命令覆蓋原來的資料,也就是說,修改key對應的value和使用另外相同的key和value來覆蓋以後,當前資料的生存時間不同。

  比如說,對一個 key 執行INCR命令,對一個列表進行LPUSH命令,或者對一個雜湊表執行HSET命令,這類操作都不會修改 key 本身的生存時間。另一方面,如果使用RENAME對一個 key 進行改名,那麼改名後的 key的生存時間和改名前一樣。

  RENAME命令的另一種可能是,嘗試將一個帶生存時間的 key 改名成另一個帶生存時間的 another_key ,這時舊的 another_key (以及它的生存時間)會被刪除,然後舊的 key 會改名為 another_key ,因此,新的 another_key 的生存時間也和原本的 key 一樣。使用PERSIST命令可以在不刪除 key 的情況下,移除 key 的生存時間,讓 key 重新成為一個persistent key 。

  2、如何更新生存時間

  可以對一個已經帶有生存時間的 key 執行EXPIRE命令,新指定的生存時間會取代舊的生存時間。過期時間的精度已經被控制在1ms之內,主鍵失效的時間複雜度是O(1),

  EXPIRE和TTL命令搭配使用,TTL可以檢視key的當前生存時間。設定成功返回 1;當 key 不存在或者不能為 key 設定生存時間時,返回 0 。

  最大快取配置

  在 redis 中,允許使用者設定最大使用記憶體大小

  server.maxmemory

  預設為0,沒有指定最大快取,如果有新的資料新增,超過最大記憶體,則會使redis崩潰,所以一定要設定。redis 記憶體資料集大小上升到一定大小的時候,就會實行資料淘汰策略。

  redis 提供 6種資料淘汰策略:

  . volatile-lru:從已設定過期時間的資料集(server.db[i].expires)中挑選最近最少使用的資料淘汰(預設)

  . volatile-ttl:從已設定過期時間的資料集(server.db[i].expires)中挑選將要過期的資料淘汰

  . volatile-random:從已設定過期時間的資料集(server.db[i].expires)中任意選擇資料淘汰

  . allkeys-lru:從資料集(server.db[i].dict)中挑選最近最少使用的資料淘汰

  . allkeys-random:從資料集(server.db[i].dict)中任意選擇資料淘汰

  . no-enviction(驅逐):禁止驅逐資料

  注意這裡的6種機制,volatile和allkeys規定了是對已設定過期時間的資料集淘汰資料還是從全部資料集淘汰資料,後面的lru、ttl以及random是三種不同的淘汰策略,再加上一種no-enviction永不回收的策略。

  使用策略規則:

  1、如果資料呈現冪律分佈,也就是一部分資料訪問頻率高,一部分資料訪問頻率低,則使用allkeys-lru

  2、如果資料呈現平等分佈,也就是所有的資料訪問頻率都相同,則使用allkeys-random

  三種資料淘汰策略:

  ttl和random比較容易理解,實現也會比較簡單。主要是Lru最近最少使用淘汰策略,設計上會對key 按失效時間排序,然後取最先失效的key進行淘汰

 

使用過Redis分散式鎖麼,它是什麼回事?

先拿setnx來爭搶鎖,搶到之後,再用expire給鎖加一個過期時間防止鎖忘記了釋放。

這時候對方會告訴你說你回答得不錯,然後接著問如果在setnx之後執行expire之前程序意外crash或者要重啟維護了,那會怎麼樣?

這時候你要給予驚訝的反饋:唉,是喔,這個鎖就永遠得不到釋放了。緊接著你需要抓一抓自己得腦袋,故作思考片刻,好像接下來的結果是你主動思考出來的,然後回答:我記得set指令有非常複雜的引數,這個應該是可以同時把setnx和expire合成一條指令來用的!(事物)對方這時會顯露笑容,心裡開始默唸:摁,這小子還不錯。

 

假如Redis裡面有1億個key,其中有10w個key是以某個固定的已知的字首開頭的,如果將它們全部找出來?

使用keys指令可以掃出指定模式的key列表。

對方接著追問:如果這個redis正在給線上的業務提供服務,那使用keys指令會有什麼問題?

這個時候你要回答redis關鍵的一個特性:redis的單執行緒的。

keys指令會導致執行緒阻塞一段時間,線上服務會停頓,直到指令執行完畢,服務才能恢復。

這個時候可以使用scan指令,scan指令可以無阻塞的提取出指定模式的key列表,但是會有一定的重複概率,在客戶端做一次去重就可以了,但是整體所花費的時間會比直接用keys指令長。

 

使用過Redis做非同步佇列麼,你是怎麼用的?

一般使用list結構作為佇列,rpush生產訊息,lpop消費訊息。當lpop沒有訊息的時候,要適當sleep一會再重試。

如果對方追問可不可以不用sleep呢?list還有個指令叫blpop,在沒有訊息的時候,它會阻塞住直到訊息到來。

如果對方追問能不能生產一次消費多次呢?使用pub/sub主題訂閱者模式,可以實現1:N的訊息佇列。

如果對方追問pub/sub有什麼缺點?在消費者下線的情況下,生產的訊息會丟失,得使用專業的訊息佇列如rabbitmq等。

如果對方追問redis如何實現延時佇列?我估計現在你很想把面試官一棒打死如果你手上有一根棒球棍的話,怎麼問的這麼詳細。但是你很剋制,然後神態自若的回答道:使用sortedset,拿時間戳作為score,訊息內容作為key呼叫zadd來生產訊息,消費者用zrangebyscore指令獲取N秒之前的資料輪詢進行處理。

 

如果有大量的key需要設定同一時間過期,一般需要注意什麼?

如果大量的key過期時間設定的過於集中,到過期的那個時間點,redis可能會出現短暫的卡頓現象。一般需要在時間上加一個隨機值,使得過期時間分散一些。

 

Redis如何做持久化的?

bgsave做映象全量持久化,aof做增量持久化。因為bgsave會耗費較長時間,不夠實時,在停機的時候會導致大量丟失資料,所以需要aof來配合使用。在redis例項重啟時,會使用bgsave持久化檔案重新構建記憶體,再使用aof重放近期的操作指令來實現完整恢復重啟之前的狀態。

 

如果突然機器掉電會怎樣?

取決於aof日誌sync屬性的配置,如果不要求效能,在每條寫指令時都sync一下磁碟,就不會丟失資料。但是在高效能的要求下每次都sync是不現實的,一般都使用定時sync,比如1s1次,這個時候最多就會丟失1s的資料。

 

bgsave的原理是什麼?

你給出兩個詞彙就可以了,fork和cow。fork是指redis通過建立子程序來進行bgsave操作,cow指的是copy on write,子程序建立後,父子程序共享資料段,父程序繼續提供讀寫服務,寫髒的頁面資料會逐漸和子程序分離開來。

 

Pipeline有什麼好處,為什麼要用pipeline?

可以將多次IO往返的時間縮減為一次,前提是pipeline執行的指令之間沒有因果相關性。使用redis-benchmark進行壓測的時候可以發現影響redis的QPS峰值的一個重要因素是pipeline批次指令的數目。

 

Redis的同步機制瞭解麼?

Redis可以使用主從同步,從從同步。第一次同步時,主節點做一次bgsave,並同時將後續修改操作記錄到記憶體buffer,待完成後將rdb檔案全量同步到複製節點,複製節點接受完成後將rdb映象載入到記憶體。載入完成後,再通知主節點將期間修改的操作記錄同步到複製節點進行重放就完成了同步過程。

 

是否使用過Redis叢集,叢集的原理是什麼?

Redis Sentinal著眼於高可用,在master宕機時會自動將slave提升為master,繼續提供服務。

Redis Cluster著眼於擴充套件性,在單個redis記憶體不足時,使用Cluster進行分片儲存。

 

設定Redis最大佔用記憶體

Redis設定最大佔用記憶體,開啟redis配置檔案,找到如下段落,設定maxmemory引數,maxmemory是bytes位元組型別,注意轉換。修改如下所示:

# In short... if you have slaves attached it is suggested that you set a lower

# limit for maxmemory so that there is some free RAM on the system for slave

# output buffers (but this is not needed if the policy is 'noeviction').

#

# maxmemory <bytes>

maxmemory 268435456

本機伺服器redis配置檔案路徑:/etc/redis/6379.conf,由於本機自帶記憶體只有1G,一般推薦Redis設定記憶體為最大實體記憶體的四分之三,所以設定0.75G,換成byte是751619276.

可以在CentOS下輸入命令:find / -name redis查詢redis目錄:

[[email protected] ~]# find / -name redis

/usr/share/nginx/html/blog.tanteng.me/wp-content/cache/supercache/blog.tanteng.me/tag/redis

/etc/redis

/var/redis

Redis配置檔案一般在etc下的redis安裝目錄下。

Redis使用超過設定的最大值

如果Redis的使用超過了設定的最大值會怎樣?我們來改一改上面的配置,故意把最大值設為1個byte試試。

# output buffers (but this is not needed if the policy is 'noeviction').

#

# maxmemory <bytes>

maxmemory 1

開啟debug模式下的頁面,

提示錯誤:OOM command not allowed when used memory > ‘maxmemory’.

設定了maxmemory的選項,redis記憶體使用達到上限。可以通過設定LRU演算法來刪除部分key,釋放空間。預設是按照過期時間的,如果set時候沒有加上過期時間就會導致資料寫滿maxmemory。

如果不設定maxmemory或者設定為0,64位系統不限制記憶體,32位系統最多使用3GB記憶體。

 

Redis 的使用要注意什麼,講講持久化方式,記憶體設定,叢集的應用和優劣勢,淘汰策略等。

持久化方式:RDB時間點快照 AOF記錄伺服器執行的所有寫操作命令,並在伺服器啟動時,通過重新執行這些命令來還原資料集。 

記憶體設定 maxmemory used_memory 

虛擬記憶體: vm-enabled yes 

3.0採用Cluster方式, 

Redis叢集相對單機在功能上存在一些限制, 需要開發人員提前瞭解, 

在使用時做好規避。 限制如下: 

1) key批量操作支援有限。 如mset、 mget, 目前只支援具有相同slot值的key執 行批量操作。 對於對映為不同slot值的key由於執行mget、 mget等操作可能存在於多個節點上因此不被支援。 

2) key事務操作支援有限。 同理只支援多key在同一節點上的事務操作, 當多個key分佈在不同的節點上時無法使用事務功能。 

3) key作為資料分割槽的最小粒度, 因此不能將一個大的鍵值物件如hash、 list等對映到不同的節點。 

4) 不支援多資料庫空間。 單機下的Redis可以支援16個數據庫, 叢集模式下只能使用一個數據庫空間, 即db0。 

5) 複製結構只支援一層, 從節點只能複製主節點, 不支援巢狀樹狀複製結構。Redis Cluster是Redis的分散式解決方案, 在3.0版本正式推出, 有效地解決了Redis分散式方面的需求。 當遇到單機記憶體、 併發、 流量等瓶頸時, 可以採用Cluster架構方案達到負載均衡的目的。 之前, Redis分散式方案一般 

有兩種: 

·客戶端分割槽方案, 優點是分割槽邏輯可控,缺點是需要自己處理資料路由、高可用、故障轉移等問題。 

·代理方案, 優點是簡化客戶端分散式邏輯和升級維護便利, 缺點是加重架構部署複雜度和效能損耗。 

現在官方為我們提供了專有的叢集方案: Redis Cluster, 它非常優雅地解決了Redis叢集方面的問題, 因此理解應用好Redis Cluster將極大地解放我們使用分散式Redis的工作量, 同時它也是學習分散式儲存的絕佳案例。

LRU(近期最少使用演算法)TTL(超時演算法) 去除ttl最大的鍵值 

http://wiki.jikexueyuan.com/project/redis/data-elimination-mechanism.html 

http://www.infoq.com/cn/articles/tq-redis-memory-usage-optimization-storage 

http://www.redis.cn/topics/cluster-tutorial.html

 

redis2 和 redis3 的區別,redis3 內部通訊機制。

叢集方式的區別,3採用Cluster,2採用客戶端分割槽方案和代理方案 

通訊過程說明: 

1) 叢集中的每個節點都會單獨開闢一個TCP通道, 用於節點之間彼此 

通訊, 通訊埠號在基礎埠上加10000。 

2) 每個節點在固定週期內通過特定規則選擇幾個節點發送ping訊息。 

3) 接收到ping訊息的節點用pong訊息作為響應。

 

Memcache 的原理,哪些資料適合放在快取中。

基於libevent的事件處理 

內建記憶體儲存方式SLab Allocation機制 

並不單一的資料刪除機制 

基於客戶端的分散式系統

變化頻繁,具有不穩定性的資料,不需要實時入庫, (比如使用者線上 

狀態、線上人數..) 

入口網站的新聞等,覺得頁面靜態化仍不能滿足要求,可以放入 

到memcache中.(配合jquey的ajax請求)

 

redis 和 memcached 的記憶體管理的區別。

Memcached預設使用Slab Allocation機制管理記憶體,其主要思想是按照預先規定的大小,將分配的記憶體分割成特定長度的塊以儲存相應長度的key-value資料記錄,以完全解決記憶體碎片問題。 

Redis的記憶體管理主要通過原始碼中zmalloc.h和zmalloc.c兩個檔案來實現的。 

在Redis中,並不是所有的資料都一直儲存在記憶體中的。這是和Memcached相比一個最大的區別。 

http://lib.csdn.net/article/redis/55323

(1)效能對比:由於Redis只使用單核,而Memcached可以使用多核,所以平均每一個核上Redis在儲存小資料時比Memcached效能更高。而在100k以上的資料中,Memcached效能要高於Redis,雖然Redis最近也在儲存大資料的效能上進行優化,但是比起Memcached,還是稍有遜色。

(2)記憶體使用效率對比:使用簡單的key-value儲存的話,Memcached的記憶體利用率更高,而如果Redis採用hash結構來做key-value儲存,由於其組合式的壓縮,其記憶體利用率會高於Memcached。

(3)Redis支援伺服器端的資料操作:Redis相比Memcached來說,擁有更多的資料結構和並支援更豐富的資料操作,通常在Memcached裡,你需要將資料拿到客戶端來進行類似的修改再set回去。這大大增加了網路IO的次數和資料體積。在Redis中,這些複雜的操作通常和一般的GET/SET一樣高效。所以,如果需要快取能夠支援更復雜的結構和操作,那麼Redis會是不錯的選擇。

 

Redis 的併發競爭問題如何解決,瞭解 Redis 事務的 CAS 操作嗎。

Redis為單程序單執行緒模式,採用佇列模式將併發訪問變為序列訪問。Redis本身沒有鎖的概念,Redis對於多個客戶端連線並不存在競爭,但是在Jedis客戶端對Redis進行併發訪問時會發生連線超時、資料轉換錯誤、阻塞、客戶端關閉連線等問題,這些問題均是由於客戶端連線混亂造成。對此有2種解決方法:

1.客戶端角度,為保證每個客戶端間正常有序與Redis進行通訊,對連線進行池化,同時對客戶端讀寫Redis操作採用內部鎖synchronized。

2.伺服器角度,利用setnx實現鎖。

MULTI,EXEC,DISCARD,WATCH 四個命令是 Redis 事務的四個基礎命令。其中:

MULTI,告訴 Redis 伺服器開啟一個事務。注意,只是開啟,而不是執行 

EXEC,告訴 Redis 開始執行事務 

DISCARD,告訴 Redis 取消事務 

WATCH,監視某一個鍵值對,它的作用是在事務執行之前如果監視的鍵值被修改,事務會被取消。 

可以利用watch實現cas樂觀鎖 

http://wiki.jikexueyuan.com/project/redis/transaction-mechanism.html 

http://www.jianshu.com/p/d777eb9f27df

 

Redis 的選舉演算法和流程是怎樣的

Raft採用心跳機制觸發Leader選舉。系統啟動後,全部節點初始化為Follower,term為0.節點如果收到了RequestVote或者AppendEntries,就會保持自己的Follower身份。如果一段時間內沒收到AppendEntries訊息直到選舉超時,說明在該節點的超時時間內還沒發現Leader,Follower就會轉換成Candidate,自己開始競選Leader。一旦轉化為Candidate,該節點立即開始下面幾件事情:

1、增加自己的term。 

2、啟動一個新的定時器。 

3、給自己投一票。 

4、向所有其他節點發送RequestVote,並等待其他節點的回覆。 

如果在這過程中收到了其他節點發送的AppendEntries,就說明已經有Leader產生,自己就轉換成Follower,選舉結束。

如果在計時器超時前,節點收到多數節點的同意投票,就轉換成Leader。同時向所有其他節點發送AppendEntries,告知自己成為了Leader。

每個節點在一個term內只能投一票,採取先到先得的策略,Candidate前面說到已經投給了自己,Follower會投給第一個收到RequestVote的節點。每個Follower有一個計時器,在計時器超時時仍然沒有接受到來自Leader的心跳RPC, 則自己轉換為Candidate, 開始請求投票,就是上面的的競選Leader步驟。

如果多個Candidate發起投票,每個Candidate都沒拿到多數的投票(Split Vote),那麼就會等到計時器超時後重新成為Candidate,重複前面競選Leader步驟。

Raft協議的定時器採取隨機超時時間,這是選舉Leader的關鍵。每個節點定時器的超時時間隨機設定,隨機選取配置時間的1倍到2倍之間。由於隨機配置,所以各個Follower同時轉成Candidate的時間一般不一樣,在同一個term內,先轉為Candidate的節點會先發起投票,從而獲得多數票。多個節點同時轉換為Candidate的可能性很小。即使幾個Candidate同時發起投票,在該term內有幾個節點獲得一樣高的票數,只是這個term無法選出Leader。由於各個節點定時器的超時時間隨機生成,那麼最先進入下一個term的節點,將更有機會成為Leader。連續多次發生在一個term內節點獲得一樣高票數在理論上機率很小,實際上可以認為完全不可能發生。一般1-2個term類,Leader就會被選出來。

Sentinel的選舉流程

Sentinel叢集正常執行的時候每個節點epoch相同,當需要故障轉移的時候會在叢集中選出Leader執行故障轉移操作。Sentinel採用了Raft協議實現了Sentinel間選舉Leader的演算法,不過也不完全跟論文描述的步驟一致。Sentinel叢集執行過程中故障轉移完成,所有Sentinel又會恢復平等。Leader僅僅是故障轉移操作出現的角色。

選舉流程

1、某個Sentinel認定master客觀下線的節點後,該Sentinel會先看看自己有沒有投過票,如果自己已經投過票給其他Sentinel了,在2倍故障轉移的超時時間自己就不會成為Leader。相當於它是一個Follower。 

2、如果該Sentinel還沒投過票,那麼它就成為Candidate。 

3、和Raft協議描述的一樣,成為Candidate,Sentinel需要完成幾件事情 

1)更新故障轉移狀態為start 

2)當前epoch加1,相當於進入一個新term,在Sentinel中epoch就是Raft協議中的term。 

3)更新自己的超時時間為當前時間隨機加上一段時間,隨機時間為1s內的隨機毫秒數。 

4)向其他節點發送is-master-down-by-addr命令請求投票。命令會帶上自己的epoch。 

5)給自己投一票,在Sentinel中,投票的方式是把自己master結構體裡的leader和leader_epoch改成投給的Sentinel和它的epoch。 

4、其他Sentinel會收到Candidate的is-master-down-by-addr命令。如果Sentinel當前epoch和Candidate傳給他的epoch一樣,說明他已經把自己master結構體裡的leader和leader_epoch改成其他Candidate,相當於把票投給了其他Candidate。投過票給別的Sentinel後,在當前epoch內自己就只能成為Follower。 

5、Candidate會不斷的統計自己的票數,直到他發現認同他成為Leader的票數超過一半而且超過它配置的quorum(quorum可以參考《redis sentinel設計與實現》)。Sentinel比Raft協議增加了quorum,這樣一個Sentinel能否當選Leader還取決於它配置的quorum。 

6、如果在一個選舉時間內,Candidate沒有獲得超過一半且超過它配置的quorum的票數,自己的這次選舉就失敗了。 

7、如果在一個epoch內,沒有一個Candidate獲得更多的票數。那麼等待超過2倍故障轉移的超時時間後,Candidate增加epoch重新投票。 

8、如果某個Candidate獲得超過一半且超過它配置的quorum的票數,那麼它就成為了Leader。 

9、與Raft協議不同,Leader並不會把自己成為Leader的訊息發給其他Sentinel。其他Sentinel等待Leader從slave選出master後,檢測到新的master正常工作後,就會去掉客觀下線的標識,從而不需要進入故障轉移流程。 

http://weizijun.cn/2015/04/30/Raft%E5%8D%8F%E8%AE%AE%E5%AE%9E%E6%88%98%E4%B9%8BRedis%20Sentinel%E7%9A%84%E9%80%89%E4%B8%BELeader%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/

 

redis 的持久化的機制,aof 和 rdb 的區別。

RDB 定時快照方式(snapshot): 定時備份,可能會丟失資料 

AOF 基於語句追加方式 只追加寫操作 

AOF 持久化和 RDB 持久化的最主要區別在於,前者記錄了資料的變更,而後者是儲存了資料本身

 

redis 的叢集怎麼同步的資料的。

redis replication redis-migrate-tool等方式

 

用過哪些命令?

官網命令:http://doc.redisfans.com

 

Redis有哪幾種資料淘汰策略?

noeviction:返回錯誤當記憶體限制達到並且客戶端嘗試執行會讓更多記憶體被使用的命令(大部分的寫入指令,但DEL和幾個例外)

allkeys-lru: 嘗試回收最少使用的鍵(LRU),使得新新增的資料有空間存放。

volatile-lru: 嘗試回收最少使用的鍵(LRU),但僅限於在過期集合的鍵,使得新新增的資料有空間存放。

allkeys-random: 回收隨機的鍵使得新新增的資料有空間存放。

volatile-random: 回收隨機的鍵使得新新增的資料有空間存放,但僅限於在過期集合的鍵。

volatile-ttl: 回收在過期集合的鍵,並且優先回收存活時間(TTL)較短的鍵,使得新新增的資料有空間存放。

 

Redis官方為什麼不提供Windows版本?

因為目前Linux版本已經相當穩定,而且使用者量很大,無需開發windows版本,反而會帶來相容性等問題。

 

為什麼Redis需要把所有資料放到記憶體中?

Redis為了達到最快的讀寫速度將資料都讀到記憶體中,並通過非同步的方式將資料寫入磁碟。所以redis具有快速和資料持久化的特徵。如果不將資料放在記憶體中,磁碟I/O速度為嚴重影響redis的效能。在記憶體越來越便宜的今天,redis將會越來越受歡迎。

如果設定了最大使用的記憶體,則資料已有記錄數達到記憶體限值後不能繼續插入新值。

 

Redis叢集方案應該怎麼做?都有哪些方案?

twemproxy:(中間代理)

大概概念是,它類似於一個代理方式,使用方法和普通redis無任何區別,設定好它下屬的多個redis例項後,使用時在本需要連線redis的地方改為連線twemproxy,它會以一個代理的身份接收請求並使用一致性hash演算法,將請求轉接到具體redis,將結果再返回twemproxy。使用方式簡便(相對redis只需修改連線埠),對舊專案擴充套件的首選。 問題:twemproxy自身單埠例項的壓力,使用一致性hash後,對redis節點數量改變時候的計算值的改變,資料無法自動移動到新的節點。

codis:(中間代理)

目前用的最多的叢集方案,基本和twemproxy一致的效果,但它支援在 節點數量改變情況下,舊節點資料可恢復到新hash節點。

redis cluster3.0自帶的叢集:(路由轉發)

特點在於他的分散式演算法不是一致性hash,而是hash槽的概念,以及自身支援節點設定從節點。具體看官方文件介紹。

在業務程式碼層實現,起幾個毫無關聯的redis例項,在程式碼層,對key 進行hash計算,然後去對應的redis例項操作資料。 這種方式對hash層程式碼要求比較高,考慮部分包括,節點失效後的替代演算法方案,資料震盪後的自動指令碼恢復,例項的監控,等等。

 

Redis叢集方案什麼情況下會導致整個叢集不可用?

有A,B,C三個節點的叢集,在沒有複製模型的情況下,如果節點B失敗了,那麼整個叢集就會以為缺少5501-11000這個範圍的槽而不可用。

 

Redis支援的Java客戶端都有哪些?官方推薦用哪個?

Redisson、Jedis、lettuce等等,官方推薦使用Redisson。

 

Redis如何設定密碼及驗證密碼?

設定密碼:config set requirepass 123456

授權密碼:auth 123456

 

說說Redis雜湊槽的概念?

Redis叢集沒有使用一致性hash,而是引入了雜湊槽的概念,Redis叢集有16384個雜湊槽,每個key通過CRC16校驗後對16384取模來決定放置哪個槽,叢集的每個節點負責一部分hash槽。

 

 

Redis叢集會有寫操作丟失嗎?為什麼?

Redis並不能保證資料的強一致性,這意味這在實際中叢集在特定的條件下可能會丟失寫操作。

 

Redis叢集最大節點個數是多少?

16384個。

 

Redis叢集如何選擇資料庫?

Redis叢集目前無法做資料庫選擇,預設在0資料庫。

 

Redis中的管道有什麼用?

一次請求/響應伺服器能實現處理新的請求即使舊的請求還未被響應。這樣就可以將多個命令傳送到伺服器,而不用等待回覆,最後在一個步驟中讀取該答覆。

這就是管道(pipelining),是一種幾十年來廣泛使用的技術。例如許多POP3協議已經實現支援這個功能,大大加快了從伺服器下載新郵件的過程。

 

怎麼理解Redis事務?

事務是一個單獨的隔離操作:事務中的所有命令都會序列化、按順序地執行。事務在執行的過程中,不會被其他客戶端傳送來的命令請求所打斷。

事務是一個原子操作:事務中的命令要麼全部被執行,要麼全部都不執行。

 

Redis事務相關的命令有哪幾個?

MULTI、EXEC、DISCARD、WATCH

 

Redis key的過期時間和永久有效分別怎麼設定?

EXPIRE和PERSIST命令。

 

Redis如何做記憶體優化?

儘可能使用散列表(hashes),散列表(是說散列表裡面儲存的數少)使用的記憶體非常小,所以你應該儘可能的將你的資料模型抽象到一個散列表裡面。比如你的web系統中有一個使用者物件,不要為這個使用者的名稱,姓氏,郵箱,密碼設定單獨的key,而是應該把這個使用者的所有資訊儲存到一張散列表裡面.

 

Redis回收程序如何工作的?

  1. 一個客戶端運行了新的命令,添加了新的資料。
  2. Redi檢查記憶體使用情況,如果大於maxmemory的限制, 則根據設定好的策略進行回收。
  3. 一個新的命令被執行,等等。
  4. 所以我們不斷地穿越記憶體限制的邊界,通過不斷達到邊界然後不斷地回收回到邊界以下。

如果一個命令的結果導致大量記憶體被使用(例如很大的集合的交集儲存到一個新的鍵),不用多久記憶體限制就會被這個記憶體使用量超越。

 

Redis分割槽有什麼缺點?

  • 涉及多個key的操作通常不會被支援。例如你不能對兩個集合求交集,因為他們可能被儲存到不同的Redis例項(實際上這種情況也有辦法,但是不能直接使用交集指令)。
  • 同時操作多個key,則不能使用Redis事務.
  • 分割槽使用的粒度是key,不能使用一個非常長的排序key儲存一個數據集(The partitioning granularity is the key, so it is not possible to shard a dataset with a single huge key like a very big sorted set).
  • 當使用分割槽的時候,資料處理會非常複雜,例如為了備份你必須從不同的Redis例項和主機同時收集RDB / AOF檔案。
  • 分割槽時動態擴容或縮容可能非常複雜。Redis叢集在執行時增加或者刪除Redis節點,能做到最大程度對使用者透明地資料再平衡,但其他一些客戶端分割槽或者代理分割槽方法則不支援這種特性。然而,有一種預分片的技術也可以較好的解決這個問題。

 

Redis持久化資料和快取怎麼做擴容?

  • 如果Redis被當做快取使用,使用一致性雜湊實現動態擴容縮容。
  • 如果Redis被當做一個持久化儲存使用,必須使用固定的keys-to-nodes對映關係,節點的數量一旦確定不能變化。否則的話(即Redis節點需要動態變化的情況),必須使用可以在執行時進行資料再平衡的一套系統,而當前只有Redis叢集可以做到這樣。

 

分散式Redis是前期做還是後期規模上來了再做好?為什麼?

既然Redis是如此的輕量(單例項只使用1M記憶體),為防止以後的擴容,最好的辦法就是一開始就啟動較多例項。即便你只有一臺伺服器,你也可以一開始就讓Redis以分散式的方式執行,使用分割槽,在同一臺伺服器上啟動多個例項。

一開始就多設定幾個Redis例項,例如32或者64個例項,對大多數使用者來說這操作起來可能比較麻煩,但是從長久來看做這點犧牲是值得的。

這樣的話,當你的資料不斷增長,需要更多的Redis伺服器時,你需要做的就是僅僅將Redis例項從一臺服務遷移到另外一臺伺服器而已(而不用考慮重新分割槽的問題)。一旦你添加了另一臺伺服器,你需要將你一半的Redis例項從第一臺機器遷移到第二臺機器。

 

Twemproxy是什麼?

Twemproxy是Twitter維護的(快取)代理系統,代理Memcached的ASCII協議和Redis協議。它是單執行緒程式,使用c語言編寫,執行起來非常快。它是採用Apache 2.0 license的開源軟體。 Twemproxy支援自動分割槽,如果其代理的其中一個Redis節點不可用時,會自動將該節點排除(這將改變原來的keys-instances的對映關係,所以你應該僅在把Redis當快取時使用Twemproxy)。 Twemproxy本身不存在單點問題,因為你可以啟動多個Twemproxy例項,然後讓你的客戶端去連線任意一個Twemproxy例項。 Twemproxy是Redis客戶端和伺服器端的一箇中間層,由它來處理分割槽功能應該不算複雜,並且應該算比較可靠的。

 

支援一致性雜湊的客戶端有哪些?

Redis-rb、Predis等。

 

檢視Redis使用情況及狀態資訊用什麼命令?

info

 

Redis的記憶體用完了會發生什麼?

如果達到設定的上限,Redis的寫命令會返回錯誤資訊(但是讀命令還可以正常返回。)或者你可以將Redis當快取來使用配置淘汰機制,當Redis達到記憶體上限時會沖刷掉舊的內容。

 

Redis是單執行緒的,如何提高多核CPU的利用率?

可以在同一個伺服器部署多個Redis的例項,並把他們當作不同的伺服器來使用,在某些時候,無論如何一個伺服器是不夠的, 所以,如果你想使用多個CPU,你可以考慮一下分片(shard)。

 

一個Redis例項最多能存放多少的keys?List、Set、Sorted Set他們最多能存放多少元素?

理論上Redis可以處理多達232的keys,並且在實際中進行了測試,每個例項至少存放了2億5千萬的keys。我們正在測試一些較大的值。

任何list、set、和sorted set都可以放232個元素。

換句話說,Redis的儲存極限是系統中的可用記憶體值。

 

修改配置不重啟Redis會實時生效嗎?

針對執行例項,有許多配置選項可以通過 CONFIG SET 命令進行修改,而無需執行任何形式的重啟。 從 Redis 2.2 開始,可以從 AOF 切換到 RDB 的快照永續性或其他方式而不需要重啟 Redis。檢索 ‘CONFIG GET *’ 命令獲取更多資訊。

但偶爾重新啟動是必須的,如為升級 Redis 程式到新的版本,或者當你需要修改某些目前 CONFIG 命令還不支援的配置引數的時候。