學到這裏終於對Redis有個初步的瞭解,今天來看看面試中常常遇到的有關Redis的面試題面試
爲何要用Redis
Redis 和 Memcached 的區別
Redis有哪些數據結構?
什麼是Redis持久化?Redis有哪幾種持久化方式?優缺點是什麼?
使用過Redis分佈式鎖麼,它是怎麼實現的?
什麼是緩存穿透?如何避免?
什麼是緩存雪崩?如何避免?
如何保證緩存與數據庫雙寫時的數據一致性?
緩存分爲本地緩存
和分佈式緩存
。以 Java 爲例,使用自帶的 map 或者 guava 實現的是本地緩存,最主要的特色是輕量以及快速,生命週期隨着 jvm 的銷燬而結束,而且在多實例的狀況下,每一個實例都須要各自保存一份緩存,緩存不具備一致性。redis
使用Redis或Memcached之類的稱爲分佈式緩存,在多實例的狀況下,各實例共用一份緩存數據,緩存具備一致性。缺點是須要保持 redis 或 memcached服務的高可用,整個程序架構上較爲複雜。數據庫
String字符串
格式: set key value
Redis的String能夠包含任何數據。好比jpg圖片或者序列化的對象 。
String類型是Redis最基本的數據類型,一個鍵最大能存儲512MB。緩存
Hash(哈希)
格式: hmset name key1 value1 key2 value2
Redis hash 是一個鍵值對集合。
Redis hash是一個String類型的field和value的映射表,相似於Java中的Map<String,Map<field,value>>
,hash特別適合用於存儲對象。服務器
List(列表)
Redis 列表是簡單的字符串列表,按照插入順序排序。你能夠添加一個元素到列表的頭部(左邊)或者尾部(右邊)
格式: lpush name value
在 key 對應 list 的頭部添加字符串元素
格式: rpush name value
在 key 對應 list 的尾部添加字符串元素網絡
Set(集合)
格式: sadd name value
Redis的Set是String類型的無序集合。
集合是經過哈希表實現的,因此添加,刪除,查找的複雜度都是O(1)。數據結構
Zset(sorted set:有序集合)
格式: zadd name score value
Redis zset 和 set 同樣也是String類型元素的集合,且不容許重複的成員。
不一樣的是每一個元素都會關聯一個double
類型的分數。redis正是經過分數來爲集合中的成員進行從小到大的排序。
zset的成員是惟一的,但分數(score)卻能夠重複。多線程
什麼是Redis持久化?
持久化就是把內存的數據寫到磁盤中去,防止服務宕機了內存數據丟失。架構
Redis有哪幾種持久化方式?
Redis 提供了兩種持久化方式:RDB(默認) 和AOF併發
優缺點是什麼?
RDB和AOF並不互斥,它倆能夠同時使用。
RDB的優勢:載入時恢復數據快、文件體積小。
RDB的缺點:會必定程度上丟失數據(由於系統一旦在定時持久化以前出現宕機現象,此前沒有來得及寫入磁盤的數據都將丟失。)
AOF的優勢:丟失數據少(默認配置只丟失1秒的數據)。
AOF的缺點:恢復數據相對較慢,文件體積大
若是Redis服務器同時開啓了RDB和AOF持久化,服務器會優先使用AOF文件來還原數據
(由於AOF更新頻率比RDB更新頻率要高,還原的數據更完善)
Redis 4.0 對於持久化機制的優化
Redis 4.0 開始支持 RDB 和 AOF 的混合持久化(默認關閉,能夠經過配置項aof-use-rdb-preamble
開啓)。
若是把混合持久化打開,AOF 重寫的時候就直接把 RDB 的內容寫到 AOF 文件開頭。這樣作的好處是能夠結合 RDB 和 AOF 的優勢, 快速加載同時避免丟失過多的數據。固然缺點也是有的, AOF 裏面的 RDB 部分是壓縮格式再也不是 AOF 格式,可讀性較差。
先拿setnx來爭搶鎖,搶到以後,再用expire給鎖加一個過時時間防止鎖忘記了釋放。
若是在setnx以後執行expire以前進程意外crash或者要重啓維護了,那會怎麼樣
?set指令可使用很是複雜的參數同時把setnx和expire合成一條指令
來用的!這樣就保證了原子性!
通常使用list結構做爲隊列,rpush生產消息,lpop消費消息。當lpop沒有消息的時候,要適當sleep一會再重試。
可不能夠不用sleep呢?
list還有個指令叫blpop,在沒有消息的時候,它會阻塞住直到消息到來。
能不能生產一次消費屢次呢?
使用pub/sub主題訂閱者模式,能夠實現1:N的消息隊列。
pub/sub有什麼缺點?
在消費者下線的狀況下,生產的消息會丟失,得使用專業的消息隊列如rabbitmq等。
redis如何實現延時隊列?
使用sortedset,拿時間戳做爲score,消息內容做爲key調用zadd來生產消息,消費者用zrangebyscore指令獲取N秒以前的數據輪詢進行處理。
什麼是緩存穿透?
通常的緩存系統,都是按照key去緩存查詢,若是不存在對應的value,就到數據庫去查找。一些惡意的請求會故意查詢不存在的key,致使數據庫接收到大量無效請求,就會對服務器形成很大的壓力。這就叫作緩存穿透。
如何避免
什麼是緩存雪崩?
大量緩存在同一時間集中過時或者Redis掛掉,都會致使緩存同一時間大面積的失效,因此,後面的請求都會落到數據庫上,形成數據庫短期內承受大量請求而崩掉。
如何避免
對於大量緩存在同一時間集中過時,能夠這樣解決:
對於「Redis掛掉了,請求所有走數據庫」這種狀況,咱們能夠有如下的思路:
緩存與數據庫雙寫一致性
這裏不一致指的是:數據庫的數據跟緩存的數據不一致
從理論上說,只要咱們設置了鍵的過時時間,咱們就能保證緩存和數據庫的數據最終是一致的。
除了設置過時時間,咱們還須要作更多的措施來儘可能避免數據庫與緩存處於不一致的狀況發生。
通常來講,執行更新操做時,咱們會有兩種選擇:
爲何是刪除緩存而不是更新緩存?
通常咱們都是採起刪除緩存緩存策略的,由於刪除緩存比更新緩存要簡單得多,頻繁更新緩存的話會消耗必定性能。
舉個例子,一個緩存涉及的表的字段,在 1 分鐘內就修改了 20 次,或者是 100 次,那麼緩存更新 20 次、100 次;可是這個緩存在 1 分鐘內只被讀取了 1 次,有大量的冷數據。實際上,若是你只是刪除緩存的話,那麼在 1 分鐘內,這個緩存不過就從新計算一次而已,開銷大幅度下降。用到緩存纔去算緩存,這也體現了懶加載的思想。
先更新數據庫,再刪除緩存
若是在高併發的場景下,出現數據庫與緩存數據不一致的機率特別低,也不是沒有:
要達成上述狀況,仍是說一句機率特別低:
由於這個條件須要發生在讀緩存時緩存失效,並且併發着有一個寫操做。而實際上數據庫的寫操做會比讀操做慢得多,並且還要鎖表,而讀操做必需在寫操做前進入數據庫操做,而又要晚於寫操做更新緩存,全部的這些條件都具有的機率基本並不大。
先刪除緩存,再更新數據庫
咱們在併發場景下分析一下,就知道仍是有問題的了:
因此也會致使數據庫和緩存不一致的問題。
併發下解決數據庫與緩存不一致的思路
將刪除緩存、修改數據庫、讀取緩存等的操做積壓到隊列裏邊,實現串行化。
若是你的系統不是嚴格要求緩存+數據庫必須一致性的話,緩存能夠稍微的跟數據庫偶爾有不一致的狀況,最好不要作這個方案,串行化以後,就會致使系統的吞吐量會大幅度的下降,用比正常狀況下多幾倍的機器去支撐線上的一個請求。