Redis基礎的問與答

1⃣️、爲什麼要用Redis

因爲傳統的關係型數據庫如MySQL已經不能適用所有的場景了,比如秒殺的庫存扣減,訪問流量高峯等等,這些都很容易使數據庫崩潰掉,所以引入了緩存中間件,目前比較常見的緩存中間件有Reids和Memcached,中和考慮它們的優缺點,最終選擇了Redis來作爲緩存中間件。

2⃣️、Redis有哪些數據庫結構?

常用的五種基本數據類型:String(字符串)、Hash(字典)、List(列表)、Set(集合)、Zset(SortedSet:有序集合)

  • String類型
    string是redis最基本的類型,你可以理解成與Memcached一模一樣的類型,一個key對應一個value。
    string類型是二進制安全的。意思是redis的string可以包含任何數據。比如jpg圖片或者序列化的對象 。
    string類型是Redis最基本的數據類型,一個鍵最大能存儲512MB。

    實例

    redis 127.0.0.1:6379> SET name "redis.net.cn"
    OK
    redis 127.0.0.1:6379> GET name
    "redis.net.cn"
    

    在以上實例中我們使用了 Redis 的 SET 和 GET 命令。鍵爲 name,對應的值爲redis.net.cn。

    注意:一個鍵最大能存儲512MB。

  • Hash類型
    Redis hash 是一個鍵值對集合。
    Redis hash是一個string類型的field和value的映射表,hash特別適合用於存儲對象。

    實例

    redis 127.0.0.1:6379> HMSET user:1 username redis.net.cn password redis.net.cn points 200
    OK
    redis 127.0.0.1:6379> HGETALL user:1
    1) "username"
    2) "redis.net.cn"
    3) "password"
    4) "redis.net.cn"
    5) "points"
    6) "200"
    redis 127.0.0.1:6379>
    

    以上實例中 hash 數據類型存儲了包含用戶腳本信息的用戶對象。 實例中我們使用了 Redis HMSET, HEGTALL 命令,user:1 爲鍵值。

    每個 hash 可以存儲 2^32 - 1^ 鍵值對(40多億)。

  • List類型
    Redis 列表是簡單的字符串列表,按照插入順序排序。你可以添加一個元素導列表的頭部(左邊)或者尾部(右邊)。

    實例

    redis 127.0.0.1:6379> lpush redis.net.cn redis
    (integer) 1
    redis 127.0.0.1:6379> lpush redis.net.cn mongodb
    (integer) 2
    redis 127.0.0.1:6379> lpush redis.net.cn rabitmq
    (integer) 3
    redis 127.0.0.1:6379> lrange redis.net.cn 0 10
    1) "rabitmq"
    2) "mongodb"
    3) "redis"
    redis 127.0.0.1:6379>
    

    列表最多可存儲 2^32 - 1^ 元素 (4294967295, 每個列表可存儲40多億)。

  • Set類型
    Redis的Set是string類型的無序集合。
    集合是通過哈希表實現的,所以添加,刪除,查找的複雜度都是O(1)。

    sadd 命令

    添加一個string元素到key對應的set集合中,成功返回1,如果元素已經在集合中返回0,key對應的set不存在返回錯誤。

    sadd key member
    

    實例

    redis 127.0.0.1:6379> sadd redis.net.cn redis
    (integer) 1
    redis 127.0.0.1:6379> sadd redis.net.cn mongodb
    (integer) 1
    redis 127.0.0.1:6379> sadd redis.net.cn rabitmq
    (integer) 1
    redis 127.0.0.1:6379> sadd redis.net.cn rabitmq
    (integer) 0
    redis 127.0.0.1:6379> smembers redis.net.cn
     
    1) "rabitmq"
    2) "mongodb"
    3) "redis"
    

    注意:以上實例中 rabitmq 添加了兩次,但根據集合內元素的唯一性,第二次插入的元素將被忽略。
    集合中最大的成員數爲 2^32 - 1^ (4294967295, 每個集合可存儲40多億個成員)。

  • Zset類型
    Redis zset 和 set 一樣也是string類型元素的集合,且不允許重複的成員。
    不同的是每個元素都會關聯一個double類型的分數。redis正是通過分數來爲集合中的成員進行從小到大的排序。
    zset的成員是唯一的,但分數(score)卻可以重複。
    zadd 命令
    添加元素到集合,元素在集合中存在則更新對應score

    zadd key score member 
    

    實例

    redis 127.0.0.1:6379> zadd redis.net.cn 0 redis
    (integer) 1
    redis 127.0.0.1:6379> zadd redis.net.cn 0 mongodb
    (integer) 1
    redis 127.0.0.1:6379> zadd redis.net.cn 0 rabitmq
    (integer) 1
    redis 127.0.0.1:6379> zadd redis.net.cn 0 rabitmq
    (integer) 0
    redis 127.0.0.1:6379> ZRANGEBYSCORE redis.net.cn 0 1000
     
    1) "redis"
    2) "mongodb"
    3) "rabitmq"
    

3⃣️、如果有大量的key需要設置同一時間過期,一般需要注意什麼?

如果有大量的key過期時間設置的過於集中,到時期的那個時間點,redis可能會出現短暫的卡頓現象。嚴重的話會出現緩存雪崩,我們一般需要在時間上加一個隨機值,使得過期時間分散一些。

電商首頁經常會使用定時任務刷新緩存,可能大量的數據失效時間都十分集中,如果失效時間一樣,又剛好在失效的時間點大量用戶涌入,就有可能造成緩存雪崩

4⃣️、Redis分佈式鎖是什麼回事?

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

如果在setnx之後執行expire之前進程意外crash(崩潰)或者重啓維護了,那會怎麼樣?

set指令有一個參數,可以同時把setnx和expire合成一條指令來使用,保證了setnx和expire的原子性。

set key value [EX seconds] [PX milliseconds] [NX|XX]
EX seconds:設置失效時長,單位秒
PX milliseconds:設置失效時長,單位毫秒
NX:key不存在時設置value,成功返回OK,失敗返回(nil)
XX:key存在時設置value,成功返回OK,失敗返回(nil)

5⃣️、假如Redis裏面有1億個key,其中有10w個key是以某個固定的已知的前綴開頭的,如何將他們全部找出來?

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

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

重點:這裏面要get到redis關鍵的一個特性:redis是單線程的。

Redis是單線程的,keys指令會導致線程阻塞一段時間,線上服務會停頓,直到指令執行完畢,服務才能恢復。這個時候可以使用scan指令,scan指令可以無阻塞的提取出指定模式的key列表,但是會有一定重複概率,在客戶端做一次去重就可以了,但是整體所花費的時間會比直接用keys指令長。

不過,增量式迭代命令也不是沒有缺點的:舉個例子,使用SMEMBERS命令可以返回集合鍵當前包含的所有元素,但是對於SCAN這類增量式迭代命令來說,因爲在對鍵進行增量式迭代的過程中,鍵可能會被修改,所以增量式迭代命令只能對返回的元素提供有限的保證。

6⃣️、怎麼使用Redis做異步隊列?

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

可不可以不用sleep呢?

list還有個指令爲blpop,在沒有消息的時候,它會阻塞住直到消息到來。

能不能生產一次消費多次呢?

使用pub/sub主題訂閱者模式,可以實現1:N的消息隊列。

pub/sub有什麼缺點?

在消費者下線的情況下,生產的消息會丟失,得使用專業的消息隊列如RocketMQ等。

追問Redis如何實現延時隊列?

使用SortedSet,拿時間戳作爲score,消息內容爲key調用zadd來生產消息,消費者用zrangebyscore指令獲取N秒之前的數據輪詢進行處理。

7⃣️、Redis是怎麼持久化的?服務主從數據怎麼交互的?

RDB做鏡像全量持久化,AOF做增量持久化。因爲RDB會耗費較長的時間,不夠實時,在停機的時候會導致大量丟失數據,所以需要AOF來配合使用。在redis實例重啓時,會使用RDB持久化文件重新構建內存,再使用AOF重放近期操作指令來實現完整恢復重啓之前的狀態。

這裏很好理解,把RDB理解爲一整個表全量的數據,AOF理解爲每次操作的日誌就好了,服務器重啓的時候先把表的數據全部搞進去,但是他可能不完整,你再回放一下日誌,數據不就完整了嘛。不過Redis本身的機制是 AOF持久化開啓且存在AOF文件時,優先加載AOF文件;AOF關閉或者AOF文件不存在時,加載RDB文件;加載AOF/RDB文件城後,Redis啓動成功; AOF/RDB文件存在錯誤時,Redis啓動失敗並打印錯誤信息

如果突然機器斷電會怎麼樣?

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

RDB的原理是什麼?

forkcow。fork是指redis通過創建子進程來進行RDB操作,cow指的是copy on write,子進程創建後,父子進程共享數據段,父進程繼續提供讀寫服務,寫髒的頁面數據會逐漸和子進程分離開來。

8⃣️、Pipeline(管道)有什麼好處,爲什麼要用pipeline?

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

9⃣️、Reids的同步機制是什麼?

Redis可以使用主從同步,從從同步。第一次同步時,主節點做一次bgsave,並同時將後續修改操作記錄到內存buffer,待完成後將RDB文件全量同步到複製節點,複製節點接受完成後將RDB鏡像加載到內存。加載完成後,再通知主節點將期間修改的操作記錄同步到複製節點進行重放就完成了同步過程。後續的增量數據通過AOF日誌同步即可,有點類似數據庫的binlog。

🔟、Redis集羣的高可用怎麼保證,集羣的原理是什麼?

Redis Sentinal(哨兵模式)着眼於高可用,在master宕機時會自動將slave提升爲master,繼續提供服務。
Redis Cluster着眼於擴展性,在單個redis內存不足時,使用Cluster進行分片存儲。

發佈了14 篇原創文章 · 獲贊 20 · 訪問量 3342
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章