日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

面试必会系列 - 3.1 Redis知识点大汇总(数据类型,内存模型,持久化,缓存击穿,集群,一致性哈希等等)

發布時間:2024/2/28 数据库 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 面试必会系列 - 3.1 Redis知识点大汇总(数据类型,内存模型,持久化,缓存击穿,集群,一致性哈希等等) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

本文已收錄至 Github(MD-Notes),若博客中圖片模糊或打不開,可以來我的 Github 倉庫,包含了完整圖文:https://github.com/HanquanHq/MD-Notes,涵蓋了互聯網大廠面試必問的知識點,講解透徹,長期更新中,歡迎一起學習探討 ~

更多內容,可以訪問:

面試必會系列專欄:https://blog.csdn.net/sinat_42483341/category_10300357.html
操作系統系列專欄:https://blog.csdn.net/sinat_42483341/category_10519484.html


目錄

  • Redis
      • 常問問題
      • Redis 五種基本數據結構
        • 1、字符串 string
        • 2、列表 list
        • 3、字典 hash
        • 4、集合 set
        • 5、有序集合 zset
      • 內存模型
      • 數據類型
      • 使用
          • 在 Java 中的 API 使用
      • 持久化
        • RDB
        • AOF
      • 緩存常見問題
        • 緩存擊穿
        • 緩存穿透
        • 緩存雪崩
        • 緩存一致性(雙寫問題)
      • Redis 集群
        • AKF 拆分原則
          • 拆分之后,怎么知道數據存在哪臺機器了?去哪里取數據?
        • AKF 存在的問題:CAP定理
        • 集群一般使用奇數臺
        • Sentinel 哨兵
      • 一致性哈希
        • 普通的哈希算法存在的問題:
        • 一致性哈希算法的優點
        • hash環的偏斜
        • 虛擬節點


Redis

常問問題

采用單工作 worker 線程,6.x 版本有多 IO threads(Tomcat,Netty 也有 IO threads);

Redis 二進制安全:自身不會做編解碼,不會變化你的數據,你在客戶端要提供二進制的字節數組,不同客戶端之間要統一編碼。Zookeeper,HBase,Kafka 都是二進制安全的。

Redis 天生單線程,多 server 共同請求時,不需要加鎖。缺點:不能充分發揮服務器核數,所以進行了 IO/計算隔離,增加多個 IO 線程,worker 還是單線程。

聊:持久化 -> 同步 -> 集群 -> 中間件(分布式,ES…)讓對方覺得你的技術棧非常豐富

技術選型:Redis,memcached 怎么選擇?Redis 相比 memcached,計算向數據移動;支持模塊化,如布隆過濾器,也可以自己C++開發更多的數據類型

Redis 五種基本數據結構

string(字符串)、list(列表)、hash(字典)、set(集合) 和 zset(有序集合)

1、字符串 string

Redis 中的字符串是一種 動態字符串,這意味著使用者可以修改,它的底層實現有點類似于 Java 中的 ArrayList,有一個字符數組,從源碼的 sds.h/sdshdr 文件 中可以看到 Redis 底層對于字符串的定義 SDS,即 Simple Dynamic String 結構。

我們通常使用 SET 和 GET 來設置和獲取字符串值。

Redis 規定了字符串的長度不得超過 512 MB。

2、列表 list

相當于 Java 語言中的 LinkedList,注意它是鏈表而不是數組。這意味著 list 的插入和刪除操作非常快,時間復雜度為 O(1),但是索引定位很慢,時間復雜度為 O(n)。

基本操作

  • LPUSH 和 RPUSH 分別可以向 list 的左邊(頭部)和右邊(尾部)添加一個新元素;
  • LRANGE 命令可以從 list 中取出一定范圍的元素;
  • LINDEX 命令可以從 list 中取出指定下表的元素,相當于 Java 鏈表操作中的 get(int index) 操作;

3、字典 hash

Redis 中的字典相當于 Java 中的 HashMap,內部實現也差不多類似,都是通過 “數組 + 鏈表” 的鏈地址法來解決部分 哈希沖突,同時這樣的結構也吸收了兩種不同數據結構的優點。

4、集合 set

Redis 的集合相當于 Java 語言中的 HashSet,它內部的鍵值對是無序、唯一的。它的內部實現相當于一個特殊的字典,字典中所有的 value 都是一個值 NULL。

5、有序集合 zset

這可能使 Redis 最具特色的一個數據結構了,它類似于 Java 中 SortedSetHashMap 的結合體,一方面它是一個 set,保證了內部 value 的唯一性,另一方面它可以為每個 value 賦予一個 score 值,用來代表排序的權重。

它的內部實現用的是一種叫做 「跳躍表」 的數據結構,由于比較復雜,所以在這里簡單提一下原理就好了:

想象你是一家創業公司的老板,剛開始只有幾個人,大家都平起平坐。后來隨著公司的發展,人數越來越多,團隊溝通成本逐漸增加,漸漸地引入了組長制,對團隊進行劃分,于是有一些人又是員工又有組長的身份

再后來,公司規模進一步擴大,公司需要再進入一個層級:部門。于是每個部門又會從組長中推舉一位選出部長。

跳躍表就類似于這樣的機制,最下面一層所有的元素都會串起來,都是員工,然后每隔幾個元素就會挑選出一個代表,再把這幾個代表使用另外一級指針串起來。然后再在這些代表里面挑出二級代表,再串起來。最終形成了一個金字塔的結構。

想一下你目前所在的地理位置:亞洲 > 中國 > 某省 > 某市 > …,就是這樣一個結構!

內存模型

  • epoll
    • Nginx 也用了 epoll
    • fd
    • 紅黑樹

  • 零拷貝

    • sendfile

    • mmap

      • epoll 源碼沒有用到 mmap,epoll 用的是紅黑樹和雙端鏈表,是 redis 用到了 mmap

      • 理解為:我們使用 epoll 的時候要注意用 mmap 輔助,來避免 read/write 多一次拷貝,而非系統內核幫你維護了 mmap

      • 編譯器將 epoll_create 編譯為了 int 80 中斷等一系列匯編指令,交給CPU就行了。C代碼中雖然是顯式調用 epoll_create ,但C翻譯成匯編的時候就翻譯成了相應的0x80中斷以及相關指令的

數據類型

  • string
    • bitmap:可以做布隆過濾器
  • list
  • hashmap
  • set
  • sorted_set(zsest)
    • 排序的實現:跳躍表

使用

  • 管道

    (printf "PING\r\nPING\r\nPING\r\n"; sleep 1) | nc localhost 6379
    • 只用了一個命令的開銷時間
    • 服務器將被迫回復一個隊列答復
  • 發布/訂閱 pubsub

    • 發送者 publish test1 hello
    • 訂閱者 subscribe test1
    • 場景:聊天室,啟動多個 redis 去接收訂閱消息
      • redis1:將消息實時推送給用戶
      • redis2:存入 zset,用來取某個時間窗口的歷史消息
      • redis3:啟一個 service ,將最新消息推給 kafka,然后異步存入db
  • 事務

    • 無論誰先開啟事務,exec命令先到達的client,事務先被執行
    • watch 某個 key,如果發生更改,事務不執行
  • 布隆過濾器

    • RedisBloom模塊
    • 小空間解決大量數據匹配,避免緩存穿透
    • 有一定的誤判率

    布隆過濾器升級版

    • Counting Bloom Filter
    • 布谷鳥過濾器
在 Java 中的 API 使用
  • 使用 Jedis 連接 Redis 客戶端
  • google 的布隆過濾器 BloomFilter,需要定義好預估數據量的大小和誤差率
    • 布隆過濾器的其他使用場景:爬蟲(防止重復爬取網頁),推薦系統(防止重復推送內容 )

持久化

默認開啟 RDB + AOF 混合使用。為什么做緩存還要持久化?這樣避免你重啟之后Redis空了,導致請求全部穿透到數據庫。

RDB

save 900 1 save 300 10 save 60 10000dbfilename dump.rdb dir /var/lib/redis/6379
  • 時點性
  • save,阻塞的,比如關機維護
  • bgsave,后臺的,fork子進程

AOF

appendonly yes appendfilename "appendonly.aof"auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 64mbappendfsync always appendfsync everysec appendfsync no
  • 將寫操作記錄到文件中
  • 丟失數據少
  • 可以和 RDB 同時開啟
    • 4.0 以前,重寫 AOF 時,刪除抵消的命令,合并重復的命令
    • 4.0 以后,重寫 AOF 時,將老的數據 RDB 到 AOF 文件中,再將增量數據以指令的方式 append 到 AOF 文件中

緩存常見問題

緩存擊穿

作為緩存,受到內存大小限制,可能:

  • key 超過了過期時間
  • key 被 LRU LFU 清掉了

因為 某些 key 不在 redis 里面了,大量并發來找這個 key 的時候,這時候客戶端去直接請求數據庫,這就是擊穿

這個問題怎么解決?
只要發現某個key不存在,就讓所有對這個key的請求去搶一把鎖。也就是說,
讓第一個找key的請求,執行一個setnx,類似于放一把鎖。只有獲得鎖的人才能去數據庫查,其他的請求讓它們失敗,sleep等待幾秒鐘之后,重新去 redis 取數據。

這會存在一個問題:
1、如果第一個拿到鎖的人掛了,別人也拿不到鎖,這樣就死鎖了。可以設置鎖的過期時間來避免這個問題。
2、由于我設置了過期時間,可能會發生這樣的情況:拿到鎖的人沒掛,但是可能由于網絡擁塞或者數據庫擁塞,鎖超時了,又有一個人拿到這個鎖,又去數據庫取,更加擁塞了。
針對這個問題,可以開啟多個線程,一個線程去庫里取數據,另一個線程去給鎖的超時時間延長。這樣會讓代碼邏輯變得復雜。
3、像上面這樣,你自己去實現分布式協調很麻煩。因此我們引入Zokeeper,這個以后再講~

緩存穿透

從業務接收查詢的,是你系統里面根本不存在的數據。這就是緩存穿透。

怎么解決?使用布隆過濾器

  • 你可以在客戶端中包含布隆過濾器的算法
  • 你可以在客戶端只包含算法,在redis中存放bitmap
  • 你可以直接在redis中集成布隆模塊:RedisBloom模塊

布隆過濾器的缺點

只能增加,不能刪除,如果你的業務刪除了數據庫中的某條數據,無法在布隆過濾器中刪除這個key

解決方式

你可以使用布谷鳥過濾器等其他支持刪除操作的過濾器,或者設置一個空 key

緩存雪崩

和擊穿有些類似,都是后面有數據的情況。和緩存擊穿不同的是,緩存擊穿指 并發查同一條數據,緩存雪崩是 不同數據都過期了,很多數據都查不到從而查數據庫。

  • 大量 key 同時失效,間接造成大量的訪問到達 DB

怎么解決?要考慮兩種情況:

1、每天都要更新數據的情況,例如每天零點要刷新緩存。這時候可以依賴擊穿的解決方案。或者在業務層加一個小延時:判斷如果是零點就延時,隨機sleep幾秒,這樣不會把流量一大波流量同時放過來。
對于能夠提前預知的時點數據,比如京東雙11的頁面樣式、圖片等,可以提前推到客戶端本地,到雙11零點的時候直接切換即可。

2、與時點性無關(并不需要在某個時間刷新緩存)的話,可以設置隨機過期時間。

緩存一致性(雙寫問題)

更新緩存的的Design Pattern有四種:Cache aside, Read through, Write through, Write behind caching

如何保證緩存與數據庫的雙寫一致性?https://www.jianshu.com/p/2936a5c65e6b

分布式系統里要么通過 2PC 或是 Paxos 協議保證一致性,要么就是拼命的降低并發時臟數據的概率

緩存系統適用的場景就是非強一致性的場景,所以它屬于CAP中的AP,BASE理論。

異構數據庫本來就沒辦法強一致,我們只是減少時間窗口,達到最終一致性。

還有別忘了設置過期時間,這是個兜底方案

Redis 集群

單機、單節點、單實例存在的問題

  • 單點故障(主從、主備,一變多,多機數據存的是一樣的)
  • 容量有限
  • 單機壓力過大(分片集群,sharding 分片,不同分片存儲的是不一樣的數據)

單機單點問題的解決方式:AKF

AKF 拆分原則

  • X軸(主從復制):可以是Redis實例的副本,數據庫的副本等
    • 讀寫分離,增加備用性,解決單點故障 問題
    • 全量鏡像,不能解決容量有限 的問題
  • Y軸(按業務拆分):數據分開存儲,客戶端按照業務指定查詢哪個庫
    • 解決容量有限 的問題
  • Z軸(哈希取模):在按照不同業務拆分的前提下,將同一個業務將數據再拆分,存到不同的庫中
    • 數據量足夠小,更易發揮單機性能
拆分之后,怎么知道數據存在哪臺機器了?去哪里取數據?

可以在客戶端自己實現;可以用代理的中間件;

AKF 存在的問題:CAP定理

  • 一致性(Consistency):分布式系統中的所有數據備份,在同一時刻是否同樣的值
  • 可用性(Availability):集群中一部分節點故障后,集群整體是否還能響應客戶端的讀寫請求
  • 分區容忍性(Partition tolerance):是否能夠容忍發生網絡分區,對外表現出數據的不一致
    • 例如,向注冊中心集群注冊50臺tomcat,有的僅成功注冊了40臺,不影響使用,可容忍

集群一般使用奇數臺

當存活節點過半的時候,就可以解決容錯問題,正常提供服務

為什么是奇數臺?你看,3個節點4個節點都最多允許1個節點掛掉,但是:

  • 4臺服務器成本高于3臺服務器成本
  • 不管你是4節點還是3個節點,都只允許掛1個,風險是一樣的

Sentinel 哨兵

  • 監控
  • 提醒
  • 自動故障遷移
    • 流言協議:主服務器是否下線
    • 投票協議:重新選主,故障遷移

一致性哈希

由于redis是單點,但是項目中不可避免的會使用多臺Redis緩存服務器,那么怎么把緩存的Key均勻的映射到多臺Redis服務器上,且隨著緩存服務器的增加或減少時做到最小化的減少緩存Key的命中率呢?這樣就需要我們自己實現分布式。http://www.zsythink.net/archives/1182

普通的哈希算法存在的問題:

當緩存服務器數量發生變化時,會引起緩存的雪崩,大量緩存同一時間失效。

當緩存服務器數量發生變化時,幾乎所有緩存的位置都會發生改變。怎樣才能盡量減少受影響的緩存呢?

hash(服務器A的IP地址) % 2^32 hash(服務器B的IP地址) % 2^32 hash(服務器C的IP地址) % 2^32

一致性哈希算法的優點

假設服務器B出現了故障,我們現在需要將服務器B移除,那么,我們將上圖中的服務器B從hash環上移除即可。圖片4仍然會被緩存到服務器C中,圖片1與圖片2仍然會被緩存到服務器A中,這與服務器B移除之前并沒有任何區別。

hash環的偏斜

如果服務器被映射成下圖中的模樣,那么被緩存的對象很有可能大部分集中緩存在某一臺服務器上。

我們應該怎樣防止hash環的偏斜呢?一致性hash算法中使用"虛擬節點"解決了這個問題

虛擬節點

“虛擬節點"是"實際節點”(實際的物理服務器)在hash環上的復制品,一個實際節點可以對應多個虛擬節點。

從上圖可以看出,A、B、C三臺服務器分別虛擬出了一個虛擬節點,當然,如果你需要,也可以虛擬出更多的虛擬節點。引入虛擬節點的概念后,緩存的分布就均衡多了。如果你還不放心,可以虛擬出更多的虛擬節點,以便減小hash環偏斜所帶來的影響,虛擬節點越多,hash環上的節點就越多,緩存被均勻分布的概率就越大。

總結

以上是生活随笔為你收集整理的面试必会系列 - 3.1 Redis知识点大汇总(数据类型,内存模型,持久化,缓存击穿,集群,一致性哈希等等)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。