這篇文章跟大家分享一些關於redis快取的面試題。有一定的參考價值,有需要的朋友可以參考一下,希望對大家有幫助。
區別:
memcached可快取圖片和影片。 redis支援除k/v更多的資料結構;
redis可以使用虛擬內存,redis可持久化和aof災難恢復,redis透過主從支援資料備份;
3.redis可以做訊息佇列。
原因:memcached多執行緒模型引入了快取一致性和鎖,加鎖帶來了效能損耗。
主從複製實作:主節點將自己記憶體中的資料做一份快照,將快照發給從節點,從節點將資料還原到記憶體。之後再每次增加新資料的時候,主節點以類似mysql的二進位日誌方式將語句傳送給從節點,從節點拿到主節點傳送過來的語句進行重播。
分片方式:
客戶端分片
基於代理程式的分片
#Twemproxy
codis
#路由查詢分片
Redis-cluster體身提供了自動將資料分散到RedisCluster不同節點的能力,整個資料集合的某個資料子集儲存在哪個節點對使用者來說是透明的)
redis- cluster分片原理:Cluster中有一個16384長度的槽(虛槽),編號分別為0-16383。每個Master節點都會負責一部分的槽,當有某個key被映射到某個Master負責的槽,那麼這個Master負責為這個key提供服務,至於哪個Master節點負責哪個槽,可以由用戶指定,也可以初始化的時候自動生成,只有Master才擁有槽的所有權。 Master節點維護一個16384/8位元組的位元序列,Master節點用bit來標識對於某個槽自己是否擁有。例如編號為1的槽,Master只要判斷序列的第二位(索引從0開始)是不是為1即可。這種結構很容易新增或刪除節點。例如如果我想新新增個節點D,我需要從節點A、B、C中得部分槽到D上。
【相關推薦:Redis影片教學】
redis:
#線程Asetnx(上鎖的物件逾時時的時間戳tl),如果傳回true,獲得鎖定。
線程B用get獲取t1,與當前時間戳比較,判斷是是否超時,沒超時false,若超時執行第3步;
計算新的逾時時間t2,使用getset指令返回t3(該值可能其他執行緒已經修改過),如果t1==t3,獲得鎖,如果t1!=t3說明鎖被其他執行緒取得了。
取得鎖定後,處理完業務邏輯,再去判斷鎖是否逾時,如果沒逾時刪除鎖定,如果已逾時,不用處理(防止刪除其他執行緒的鎖定)。
zk:
客戶端對某個方法加鎖定時,在zk上的與該方法對應的指定節點的目錄下,產生一個唯一的瞬時有序節點node1;
客戶端取得該路徑下所有已經建立的子節點,如果發現自己建立的node1的序號是最小的,就認為這個客戶端獲得了鎖。
如果發現node1不是最小的,則監聽比自己建立節點序號小的最大的節點,進入等待。
取得鎖定後,處理完邏輯,刪除自己建立的node1即可。差別:zk效能差一些,開銷大,實作簡單。
RDB(RedisDataBase:在不同的時間點將redis的資料產生的快照同步到磁碟等媒體):記憶體到硬碟的快照,定期更新。缺點:耗時,耗性能(fork io操作),易遺失資料。
AOF(AppendOnlyFile:所有執行redis所執行過的指令都記錄下來,在下次redis重新啟動時,只需要執行指令就可以了):寫日誌。缺點:體積大,恢復速度慢。
bgsave做鏡像全量持久化,aof做增量持久化。因為bgsave會消耗比較長的時間,不夠即時,在停機的時候會導致大量的資料遺失,需要aof來配合,在redis實例重啟時,優先使用aof來恢復記憶體的狀態,如果沒有aof日誌,就會使用rdb檔來恢復。 Redis會定期做aof重寫,壓縮aof檔案日誌大小。 Redis4.0之後有了混合持久化的功能,將bgsave的全量和aof的增量做了融合處理,這樣既保證了恢復的效率又兼顧了資料的安全性。 bgsave的原理,fork和cow,fork是指redis透過創建子進程來進行bgsave操作,cow指的是copyonwrite,子進程創建後,父子進程共享資料段,父進程繼續提供讀寫服務,寫髒的頁面資料會逐漸和子進程分埯開來。
過期策略:
定時過期(一key一定時器),惰性過期:只有使用key時才判斷key是否已過期,過期則清除。定期過期:前兩者折中。
LRU:newLinkedHashMap
LRU演算法實現:
透過雙向鍊錶來實現,新資料插入到鍊錶頭部;
每當快取命中(即快取資料被存取),則將資料移到鍊錶頭部;
#當鍊錶滿的時候,將鍊錶尾部的資料丟棄。
LinkedHashMap:HashMap和雙向鍊錶合而為一即是LinkedHashMap。 HashMap是無序的,LinkedHashMap透過維護一個額外的雙向鍊錶保證了迭代順序。此迭代順序可以是插入順序(預設),也可以是訪問順序。
**快取穿透:**指查詢一個一定不存在的數據,如果從儲存層查不到數據則不寫入緩存,這將導致這個不存在的數據每次請求都要到DB去查詢,可能導致DB掛掉。
解決方案:
查詢傳回的資料為空,仍把這個空結果進行緩存,但過期時間會比較短;
布隆過濾器:將所有可能存在的資料哈希到一個足夠大的bitmap中,一個一定不存在的資料會被這個bitmap攔截掉,從而避免了對DB的查詢。
**快取擊穿:**對於設定了過期時間的key,快取在某個時間點過期的時候,恰好這時間點對這個Key有大量的並發請求過來,這些請求發現快取過期一般都會從後端DB載入資料並回設到緩存,這個時候大並發的請求可能會瞬間把DB壓垮。
解決方案:
使用互斥鎖:當快取失效時,不立即去Ioaddb,先使用如Redis的setnx去設定一個互斥鎖,當操作成功返回時再進行Ioaddb的操作並回設緩存,否則重試get快取的方法。
永遠不要過期:物理不過期,但邏輯過期(後台非同步執行緒去刷新)。快取雪崩:設定快取時採用了相同的過期時間,導致快取在某個時刻同時失效,請求全部轉送到DB,DB瞬時壓力過重雪崩。與快取擊穿的差別:雪崩是很多key,擊穿是某一個key快取。
解決方案:
將快取失效時間分散開,例如可以在原有的失效時間基礎上增加一個隨機值,例如1-5分鐘隨機,這樣每一個快取的過期時間的重複率就會降低,就很難引發集體失效的事件。
選擇redis的情況:
複雜資料結構,value的資料是哈希,列表,集合,有序集合等這種情況下,會選擇redis,因為memcache無法滿足這些資料結構,最典型的使用場景是,用戶訂單列表,用戶訊息,帖子評論等。
需要進行資料的持久化功能,但是注意,不要把redis當成資料庫使用,如果redis掛了,記憶體能夠快速恢復熱數據,不會將壓力瞬間壓在資料庫上,沒有cache預熱的過程。對於唯讀和資料一致性要求不高的場景可以採用持久化存儲
高可用,redis支持集群,可以實現主動複製,讀寫分離,而對於memcache如果想要實現高可用,需要進行二次開發。
儲存的內容比較大,memcache儲存的value最大為1M。
選擇memcache的場景:
純KV,資料量非常大的業務,使用memcache更合適,原因是:
memcache的記憶體分配採用的是預先分配記憶體池的管理方式,能夠省去記憶體分配的時間,redis是臨時申請空間,可能導致碎片化。
虛擬記憶體使用,memcache將所有的數據儲存在實體記憶體裡,redis有自己的vm機制,理論上能夠儲存比物理記憶體更多的數據,當數據超量時,引發swap,把冷資料刷新到磁碟上,從這點上,資料量大時,memcache更快
網路模型,memcache使用非阻塞的10復用模型,redis也是使用非阻塞的I。復用模型,但是redis也提供了一些非KV儲存以外的排序,聚合功能,複雜的CPU運算,會阻塞整個I0調度,從這點上由於redis提供的功能較多,memcache更快些
線程模型,memcache使用多線程,主線程監聽,worker子線程接受請求,執行讀寫,這個過程可能存在鎖定衝突。 redis使用的單線程,雖然無鎖衝突,但是難以利用多核心的特性提升吞吐量。
假設採用的主存分離,讀寫分離的資料庫,
如果一個執行緒A先刪除快取數據,然後將資料寫入到主庫當中,這個時候,主庫和從庫同步沒有完成,線程B從緩存當中讀取數據失敗,從從庫當中讀取到舊數據,然後更新到緩存,這個時候,緩存當中的就是舊的數據。
發生上述不一致的原因在於,主從庫資料不一致問題,加入了快取之後,主從不一致的時間被拉長了。
處理想法:從庫有資料更新之後,將快取當中的資料也同時進行更新,即當從庫發生了資料更新之後,向快取發出刪除,淘汰這段時間寫入的舊數據。
場景描述,對於主從庫,讀寫分離,如果主從庫更新同步有時差,就會導致主從庫資料的不一致
忽略這個資料不一致,在資料一致性要求不高的業務下,未必需要時時一致性
#強制讀主庫,使用一個高可用的主庫,資料庫讀寫都在主庫,增加一個緩存,提升資料讀取的效能。
選擇性讀主庫,添加一個緩存,用來記錄必須讀主庫的數據,將哪個庫,哪個表,哪個主鍵,作為緩存的key,設置緩存失效的時間為主從庫同步的時間,如果快取當中有這個數據,直接讀取主庫,如果快取當中沒有這個主鍵,就到對應的從庫中讀取。
master最好不要做持久化工作,如RDB記憶體快照與AOF日誌檔
如果資料比較重要,某個slave開啟AOF備份,策略設定成每秒同步一次
對方接著追問:如果這個redis正在為線上的業務提供服務,那使用keys指令會有什麼問題?
這時候你要回答redis關鍵的一個特性:redis的單線程的。 keys指令會導致執行緒阻塞一段時間,線上服務會停頓,直到指令執行完畢,服務才能恢復。這時候可以使用scan指令,scan指令可以無阻塞的提取出指定模式的key列表,但是會有一定的重複機率,在客戶端做一次去重就可以了,但是整體所花費的時間會比直接用keys指令長。
使用list類型保存資料訊息,rpush生產訊息,lpop消費訊息,當lpop沒有訊息時,可以sleep一段時間,然後再檢查有沒有訊息,如果不想sleep的話,可以使用blpop,在沒有訊息的時候,會一直阻塞,直到訊息的到來。 redis可以透過pub/sub主題訂閱模式實現一個生產者,多個消費者,當然也存在一定的缺點,當消費者下線時,生產的訊息會遺失。
使用sortedset,使用時間戳做score,訊息內容作為key,呼叫zadd來生產訊息,消費者使用zrangbyscore取得n秒之前的資料做輪詢處理。
Redis本質上是一個Key-Value類型的記憶體資料庫,很像memcached,整個資料庫統統載入在記憶體當中進行操作,定期透過非同步操作把資料庫資料flush到硬碟上進行儲存。
因為是純記憶體操作,Redis的效能非常出色,每秒鐘可以處理超過10萬次讀寫操作,是已知效能最快的Key-ValueDB。
Redis的出色之處不僅僅是效能,Redis最大的魅力是支援保存多種數據
結構,此外單一value的最大限制是1GB,不像memcached只能保存1MB的數據,因此Redis可以用來實現很多有用的功能。
比方說用他的List來做FIFO雙向鍊錶,實作一個輕量級的高效能訊息佇列服務,用他的Set可以做高效能的tag系統等等。
另外Redis也可以對存入的Key-Value設定expire時間,因此也可以被當作一個功能加強版的memcached來用。 Redis的主要缺點是資料庫容量受到實體記憶體的限制,不能用作海量資料的高效能讀寫,因此Redis適合的場景主要局限在較小資料量的高效能操作和運算上。
memcached所有的值都是簡單的字串,redis作為其替代者,支援更為豐富資料型別
Redis的速度比memcached快很多
redis可以持久化其資料
String、List、Set、SortedSet、hashes
記憶體。
Remote Dictionary Server
noeviction:傳回錯誤當記憶體限制達到並且客戶端嘗試執行會讓更多記憶體被使用的指令(大部分的寫入指令,但DEL和幾個例外)
allkeys-lru:嘗試回收最少使用的按鍵(LRU),使得新加入的資料有空間存放。
volatile-lru:嘗試回收最少使用的鍵(LRU),但僅限於在過期集合的鍵,使得新加入的資料有空間存放。
allkeys-random:回收隨機的按鍵使得新加入的資料有空間存放。
volatile-random:回收隨機的鍵使得新添加的資料有空間存放,但僅限於在過期集合的鍵。
volatile-ttl:回收在過期集合的鍵,並且優先回收存活時間(TTL)較短的鍵,使得新加入的資料有空間存放。
因為目前Linux版本已經相當穩定,而且使用者量很大,無需開發windows版本,反而會帶來相容性等問題。
512M
Redis為了達到最快的讀寫速度將資料都讀到記憶體中,並透過非同步的方式將資料寫入磁碟。
所以redis具有快速和資料持久化的特徵。如果不將資料放在記憶體中,磁碟I/O速度為嚴重影響redis的效能。
在記憶體越來越便宜的今天,redis將會越來越受歡迎。如果設定了最大使用的內存,則資料已有記錄數達到內存限值後不能繼續插入新值。
codis。
目前用的最多的叢集方案,基本和twemproxy一致的效果,但它支援在節點數量改變情況下,舊節點資料可恢復到新hash節點。
rediscluster3.0自帶的集群,特點在於他的分佈式演算法不是一致性hash,而是hash槽的概念,以及自身支持節點從節點設定。具體看官方文件介紹。
在業務碼層實現,起幾個毫無關聯的redis實例,在程式碼層,對key進行hash計算,然後去對應的redis實例操作資料。這種方式對hash層程式碼要求比較高,考慮部分包括,節點失效後的替代演算法方案,資料震盪後的自動腳本恢復,實例的監控,等等。
有A,B,C三個節點的集群,在沒有複製模型的情況下,如果節點B失敗了,那麼整個集群就會以為缺少5501-11000這個範圍的槽而不可用。
redis記憶體資料集大小上升到一定大小的時候,就會施加資料淘汰策略。
會話快取(SessionCache)
最常用的一種使用Redis的情境是會話快取(sessioncache)。用Redis快取會話比其他儲存(如Memcached)的優點在於:Redis提供持久化。當維護一個不是嚴格要求一致性的快取時,如果用戶的購物車資訊全部遺失,大部分人都會不高興的,現在,他們還會這樣嗎?
幸運的是,隨著Redis這些年的改進,很容易找到怎麼恰當的使用Redis來快取會話的文檔。甚至廣為人知的商業平台Magento也提供Redis的插件。
再次以Magento為例,Magento提供一個外掛程式來使用Redis作為全頁快取後端。
此外,對WordPress的用戶來說,Pantheon有一個非常好的插件wp-redis,這個外掛程式能幫助你以最快速度載入你曾經瀏覽過的頁面。
如果你快速的在Google中搜尋"Redisqueues",你馬上就能找到大量的開源項目,這些項目的目的就是利用Redis創建非常好的後端工具,以滿足各種隊列需求。例如,Celery有一個後台就是使用Redis當broker,你可以從這裡去查看。
排行榜/計數器
Redis在記憶體中對數字進行遞增或遞減的操作實現的非常好。集合(Set)和有序集合(SortedSet)也使得我們在執行這些運算的時候變的非常簡單,Redis只是剛好提供了這兩種資料結構。所以,我們要從排序集合中獲取到排名最靠前的10個用戶-我們稱之為“user_scores”,我們只需要像下面一樣執行即可:
當然,這是假定你是根據你使用者的分數做遞增的排序。如果你想回傳使用者及使用者的分數,你需要這樣執行:
ZRANGEuser_scores010WITHSCORES
AgoraGames就是一個很好的例子,用Ruby實現的,它的排行榜就是使用Redis來儲存資料的,你可以在這裡看到。
Redisson、Jedis、lettuce等等,官方推薦使用Redisson。
Redisson是一個高階的分散式協調Redis客服端,能幫助使用者在分散式環境中輕鬆實作一些Java的物件(Bloomfilter,BitSet,Set,SetMultimap,ScoredSortedSet,SortedSet,Map, ConcurrentMap,List,ListMultimap,Queue,BlockingQueue,Deque,BlockingDeque,Semaphore,Lock,ReadWriteLock,AtomicLong,CountDownLatch,Publish/Subscribe,HyperLogLog)。
Jedis是Redis的Java實現的客戶端,其API提供了比較全面的Redis指令的支援;
Redisson實作了分散式和可擴展的Java資料結構,和Jedis相比,功能較為簡單,不支援字串操作,不支援排序、事務、管道、分區等Redis特性。 Redisson的宗旨是促進使用者對Redis的關注分離,從而讓使用者能夠將精力更集中地放在處理業務邏輯上。
設定密碼:config set require pass 123456 授權密碼:auth123456
Redis集群沒有使用一致性hash,而是引入了哈希槽的概念,Redis集群有16384個哈希槽,每個key透過CRC16校驗後對16384取模來決定放置哪個槽,叢集的每個節點負責一部分hash槽。
為了使在部分節點失敗或大部分節點無法通訊的情況下叢集仍然可用,所以叢集使用了主從複製模型,每個節點都會有N-1個複製品.
Redis並不能保證資料的強一致性,這意味著這在實際中叢集在特定的條件下可能會遺失寫入操作。
非同步複製
16384個。
Redis叢集目前無法做資料庫選擇,預設在0資料庫。
ping
一次請求/回應伺服器能實現處理新的請求即使舊的請求還未被回應。這樣就可以將多個命令發送到伺服器,而不用等待回复,最後在一個步驟中讀取該答复。
這就是管道(pipelining),是一種幾十年來廣泛使用的技術。例如許多POP3協定已經實現支援這個功能,大大加快了從伺服器下載新郵件的過程。
交易是一個單獨的隔離操作:事務中的所有指令都會序列化、依序執行。事務在執行的過程中,不會被其他客戶端發送來的命令請求所打斷。事務是一個原子操作:事務中的指令要麼全部被執行,要麼全部都不執行。
MULTI、EXEC、DISCARD、WATCH
EXPIRE和PERSIST指令。
盡可能使用散列表(hashes),散列表(是說散列表裡面儲存的數少)使用的記憶體非常小,所以你應該盡可能的將你的資料模型抽像到一個散列表裡面。例如你的web系統中有一個用戶對象,不要為這個用戶的名稱,姓氏,郵箱,密碼設定單獨的key,而是應該把這個用戶的所有資訊儲存到一張散列表裡面。
一個客戶端運行了新的命令,新增了新的資料。
Redi檢查記憶體使用情況,如果大於maxmemory的限制,則根據設定好的策略進行回收。一個新的命令被執行,等等。
所以我們不斷地穿越記憶體限制的邊界,透過不斷達到邊界然後不斷地回收回到邊界以下。
如果一個指令的結果導致大量記憶體被使用(例如很大的集合的交集保存到一個新的鍵),不用多久記憶體限制就會被這個記憶體使用量超越。
更多程式相關知識,請造訪:程式設計影片! !
以上是總結分享一些關於redis快取的面試題的詳細內容。更多資訊請關注PHP中文網其他相關文章!