讀懂Redis這一篇真夠了

相信我,讀下去,沒有收穫你拿刀來砍我!

前言

首先科普一下CPU緩存,CPU緩存是指可以進行高速數據交換的存儲器,它先於內存與CPU交換數據,因此速率很快。緩存的工作原理是當CPU要讀取一個數據的時候,首先在CPU緩存中查找,找到就立即讀取並送給CPU處理;沒有找到,就從速率相對較慢的內存中讀取並送給CPU處理,同時把這個數據所在的數據塊調入緩存中,可以使得以後對整塊數據的讀取都從緩存中進行,不必再調用內存

為什麼要引入CPU緩存?在解釋之前必須先了解程序的執行過程,首先從硬盤執行程序,存放到內存,再給cpu運算與執行。由於內存和硬盤的速度相比cpu實在慢太多了,每執行一個程序cpu都要等待內存和硬盤,引入緩存技術便是為了解決此矛盾,緩存與cpu速度一致,cpu從緩存讀取數據比cpu在內存上讀取快得多,從而提升系統性能。目前主流級CPU都有一級和二級緩存,高端些的甚至有三級緩存

上面講述的是CPU緩存,讓你讀起下面的文章來好有個藥引子,如果讀不懂沒太大問題,繼續向下讀

重點來了!在開發中我們常常提到的緩存和上面的CPU緩存有異曲同工之妙,但是並不等同於CPU緩存,我們寫服務器程序時,使用緩存的目的無非就是減少數據庫訪問次數降低數據庫的壓力和提升程序的響應時間, 然而根據具體的使用場景又可以派生出無數種情況:

  • 比如說程序頻繁讀取數據庫, 但是查詢獲得的結果卻總是相同的,這部分相同的結果是不是可以放入緩存 ?
  • 獲得查詢結果要進行復雜的運算,非常消耗時間, 運算結果是不是可以放入緩存 ?
  • 有一些在網站每個頁面都需要使用的數據, 比如說用戶數據, 是不是可以放入緩存 ?

緩存有很多實現方式,谷歌的guava包的Cache、分佈式緩存redis,memcached、EHcache、自定義緩存(例如使用靜態Map實現)等。下面我們來講解最常用的redis,會有一些簡單的操作,還沒有下載,不會?別慌,點擊 download.redis.io/releases/下載,下載完成之後的步驟請移步我的redis教程系列;

Redis數據類型和內存原理

redis是一個基於C語言實現的高性能的key-value存儲系統,運行在內存中但是可以持久化到硬盤上,有著多樣的數據結構string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合),還有一些高級數據結構HyperLogLog、Geo、Pub/Sub。每種類型的存儲在底層都會存在不同的編碼格式(redisObject、SDS等)。

Redis到底強在哪裡?

  • 性能極高,Redis能讀的速度是110000次/s,寫的速度是81000次/s 。 -支持多種數據結構,如 string(字符串)、 list(雙向鏈表)、dict(hash表)、set(集合)、zset(排序set)、hyperloglog(基數估算)
  • 原子,Redis的所有操作都是原子性的,意思就是要麼成功執行要麼失敗完全不執行。單個操作是原子性的。多個操作也支持事務,即原子性,通過MULTI和EXEC指令包起來。
  • 支持AOF和RDB持久化操作,數據備份;
  • 豐富的特性,Redis還支持pub/sub, 通知, key 過期等等特性。

數據類型簡介

String(字符串)

Redis字符串是字節序列。Redis字符串是二進制安全的,這意味著他們有一個已知的長度沒有任何特殊字符終止,所以你可以存儲任何東西,512M為上限; 示例:

  • set key name存儲一個字符串對象value

Hash(hash表)

Redis的哈希是鍵值對的集合。 Redis的哈希值是字符串字段和字符串值之間的映射,因此它們被用來表示對象。

示例:

  • hset 存儲一個哈希鍵值對的集合(hset key field value)
  • hget 獲取一個哈希鍵的值(hget key field)

xiaoming是對於redis的存儲識別Hash的,而Hash真正存儲的是key(year、score),value(18、99)。

List(鏈表)

Redis的鏈表是簡單的字符串列表,排序插入順序。可以添加元素到Redis的列表的頭部或尾部,允許添加重複元素;

示例:

  • lpop key 從左邊移出一個元素,rpop key 從右邊移出一個元素
  • len key 返回鏈表中元素的個數 相當於關係型數據庫中 select count(*)
  • lrange key start end lrange命令將返回索引從start到stop之間的所有元素。Redis的列表起始索引為0。

Set(集合)

Redis的集合是字符串的無序集合,不允許有重複。

示例:

  • sadd key value 添加一個string元素到,key對應的set集合中,成功返回1,如果元素已經在集合中返回0;
  • smembers key 返回key對應set的所有元素,結果是無序的;

SortedSet(有序集合)zset

Redis 有序集合和集合一樣也是string類型元素的集合,且不允許重複的成員。不同的是每個元素都會關聯一個double類型的分數。redis正是通過分數來為集合中的成員進行從小到大的排序。有序集合的成員是唯一的,但分數(score)卻可以重複。

示例:

  • zadd key score value 將一個或多個value及其socre加入到set中
  • zrange key start end 0和-1表示從索引為0的元素到最後一個元素(同LRANGE命令相似)
  • zrangebycore key start end 範圍內按照分數排序輸出
  • zremrangebyscore key start end 可用於範圍刪除操作

Redis內存模型

接下來我會從redis底層內存存儲到系統使用級別來簡單介紹redis,什麼,一聽內存就頭大?那你還想不想要走向人生巔峰迎娶白富美,想就乖乖的看、乖乖讀、乖乖學!

Redis通過info memory查詢內存使用情況,作為內存數據庫,在內存中存儲的主要是數據,redis內存主要劃分為幾部分:

  • 數據:作為數據庫,數據是最主要的部分;
  • 進程本身運行需要的內存:Redis主進程本身運行肯定需要佔用內存,如代碼、常量池等等;
  • 緩衝內存:緩衝內存包括客戶端緩衝區、複製積壓緩衝區、AOF緩衝區等;
  • 內存碎片:內存碎片是Redis在分配、回收物理內存過程中產生的。

Redis數據內存

上面圖是執行set hello world時所設計到的數據模型;

Redis是鍵值對數據庫,每個鍵值對都會有一個dictEntry,裡面存儲著指向Key和Value的指針;next指向下一個dictEntry,與本Key-Value無關。 Key和Value又都有相應的存儲結構,每種類型都有至少兩種內部編碼,這樣做的好處在於一方面接口與實現分離,當需要增加或改變內部編碼時,用戶使用不受影響,另一方面可以根據不同的應用場景切換內部編碼,提高效率。

但是,無論是哪種類型,redis都不會直接存儲,而是通過redisObject的對象進行存儲,redisObject對象很重要,Redis對象的類型、內部編碼、內存回收、共享對象等功能,都需要redisObject支持。

Redis中還有一種SDS結構也比較重要,SDS是簡單動態字符串(Simple Dynamic String)的縮寫。Redis沒有直接使用C字符串(即以空字符’\0’結尾的字符數組)作為默認的字符串表示,而是使用了SDS。至於它們的結構在這裡暫且不說,我會在下面的文章中詳細介紹。

事務和管道pipline

眾所周知,事務是指一個完整的動作,要麼全部執行,要麼什麼也沒有做,提起事務我們會首先想到事務的四大特性ACID:

A:原子性(Atomicity) 事務是數據庫的邏輯工作單位,事務中包括的諸操作要麼全做,要麼全不做。

C:一致性(Consistency) 事務執行的結果必須是使數據庫從一個一致性狀態變到另一個一致性狀態。一致性與原子性是密切相關的。

I:隔離性(Isolation) 一個事務的執行不能被其他事務干擾。

D:持續性/永久性(Durability) 一個事務一旦提交,它對數據庫中數據的改變就應該是永久性的。

Redis事務

簡單聊下redis中的事務,先介紹幾個redis指令,即MULTI、EXEC、DISCARD、WATCH、UNWATCH。這五個指令構成了redis事務處理的基礎。

<code>1、multi用來組裝提供事務;
2、exec執行所有事務塊內的命令。
3、discard取消事務,放棄執行事務塊內的所有命令。
4、watch監視一個(或多個) key ,如果在事務執行之前這個(或這些) key 被其他命令所改動,那麼事務將被打斷。
5、unwatch取消 watch 命令對所有 key 的監視。
複製代碼/<code>

Redis事務可以一次執行多個命令,會經歷三個階段:開始事務,命令入隊,執行事務。並且帶有以下三個重要的保證:

1、批量操作在發送 EXEC 命令前被放入隊列緩存。

2、收到 EXEC 命令後進入事務執行,事務中任意命令執行失敗,其餘的命令依然被執行。

3、在事務執行過程,其他客戶端提交的命令請求不會插入到事務執行命令序列中。

在上面的例子中,我們看到了QUEUED的字樣,這表示我們在用MULTI組裝事務時,每一個命令都會進入到內存隊列中緩存起來,如果出現QUEUED則表示我們這個命令成功插入了緩存隊列,在將來執行EXEC時,這些被QUEUED的命令都會被組裝成一個事務來執行。

對於事務的執行來說,如果redis開啟了AOF持久化的話,那麼一旦事務被執行,事務中的命令便會通過write命令一次性寫入到磁盤中(下面會介紹持久化)。

在事務執行中,經常會遇到兩類問題,一是調用EXEC之前的問題,另一個是調用EXEC之後的問題。

調用EXEC之前的問題

“調用EXEC之前的錯誤”,有可能是由於語法有誤導致的,也可能時由於內存不足導致的。只要出現某個命令無法成功寫入緩衝隊列的情況,redis都會進行記錄,在客戶端調用EXEC時,redis會拒絕執行這一事務。(這時2.6.5版本之後的策略。在2.6.5之前的版本中,redis會忽略那些入隊失敗的命令,只執行那些入隊成功的命令)。

redis無情的拒絕了事務的執行,原因是“之前出現了錯誤”;(error) EXECABORT Transaction discarded because of previous errors。

調用EXEC之後的問題

對於“調用EXEC之後的錯誤”,redis則採取了完全不同的策略,即redis不會理睬這些錯誤,而是繼續向下執行事務中的其他命令。這是因為,對於應用層面的錯誤,並不是redis自身需要考慮和處理的問題,所以一個事務中如果某一條命令執行失敗,並不會影響接下來的其他命令的執行。

看到這裡可能很多人會提出問題:你前面不是說事務有一個特性是原子性,事務中的操作要麼全部執行,要不全部不執行。那上面的情況豈不是違背了原子性?是不是意味著redis不支持事務原子性?

解惑:redis事務不支持事務回滾機制,redis事務執行過程中,如果一個命令出現錯誤,那麼就返回錯誤,下面的命令還是會繼續執行下去。正是因為redis事務不支持事務回滾,如果事務出現了命令執行錯誤,只會返回當前命令的錯誤給客戶端,不會影響下面的命令的執行,所以很多人覺得和關係型數據庫(MySQL) 不一樣,而 MySQL 的事務是具有原子性的,所以大家都認為 Redis 事務不支持原子性。

其實,正常情況下,redis事務是支持原子性的,它也是要不所有命令執行成功,要不一個命令都不執行。看我們上面介紹的調用EXEC之前的錯誤的實例,在事務開始後,用戶可以輸入事務要執行的命令;在命令入事務隊列前,會對命令進行檢查,如果命令不存在或者是命令參數不對,則會返回錯誤可客戶端,並且修改客戶端狀態。當後面客戶端執行 EXEC 命令時,服務器就會直接拒絕執行此事務了。

Redis不支持事務回滾,但是它會檢查事務中的每一個命令是否錯誤(不支持檢查程序員個人的邏輯錯誤),如果有錯誤便不會執行事務,只有通過redis這一層的檢查才會開啟事務執行並且會全部執行(並不會保證全部執行成功),所以客觀來講redis事務是支持原子性的。

思考redis和mysql、oracle這種關係型數據庫事務的區別,首先redis定位是nosql非關係數據庫,而mysql、oracle這種是關係型數據庫。

在關係型數據庫中執行的sql查詢可以是相當複雜的,sql真正開始執行的時候才會進行檢查分析(有些情況可能會預編譯),沒有事務隊列這一概念,mysql數據庫不知道下一條sql是否正確,所以有必要支持事務回滾。但是在redis中,redis使用了事務隊列來將命令存儲起來並且會進行格式檢查,提前可以知道命令是否正確,所以如果只要有一個命令是錯誤的,那麼這個事務是不能執行的。

Redis 作者認為基本只會出現在開發環境的編程錯誤其實在生產環境基本是不可能出現的(例如對 String 類型的數據庫鍵執行 LPUSH 操作),所以他覺得沒必要為了這事務回滾機制而改變 Redis 追求簡單高效的設計主旨。

所以最後,其實 Redis 事務真正支持原子性的前提:開發者不要傻不拉幾的寫有邏輯問題的代碼!

Redis管道技術

Redis是一種基於客戶端-服務端模型以及請求/響應協議的TCP服務。這意味著通常情況下一個請求會遵循以下步驟:客戶端向服務端發送一個查詢請求,並監聽Socket返回,通常是以阻塞模式,等待服務端響應。服務端處理命令,並將結果返回給客戶端。

Redis 管道技術可以在服務端未響應時,客戶端可以繼續向服務端發送請求,並最終一次性讀取所有服務端的響應。形象點說明就是對於redis來說一般是同步模式來請求返回結果,而管道技術可以讓redis可以實現異步的訪問,客戶端不需要等待服務端的返回結果,可以持續的向服務端發送請求,等待最終把結果全部讀取。

持久化機制(RDB和AOF)

Redis持久化

數據持久化技術,也是Redis的一大特色。主要作用是數據的備份,將內存中的數據持久化到硬盤上,保證數不會因為服務的退出而造成丟失。Redis是內存數據庫,我們需要定期的將redis中的數據以某種形式(數據或者命令)存儲在硬盤上,當下次redis重啟時,利用持久化的技術可以實現數據的恢復。有時為了進行災難備份,我們也可以將持久化生成的數據文件拷貝到一個遠程位置。

和咱們在朋友圈看見好看的圖片一樣,得把它保存到手機,這樣下次才能找到它繼續用;而這裡的內存就相當於咱們的腦子,腦子經過多天的“打磨”忘卻了,而存起來更便於下次找到它!

Redis持久化分為RDB持久化和AOF持久化:前者將當前數據保存到硬盤,後者則是將每次執行的寫命令保存到硬盤(類似於MySQL的binlog);由於AOF持久化的實時性更好,即當進程意外退出時丟失的數據更少,因此AOF是目前主流的持久化方式,不過RDB持久化仍然有其用武之地。

RDB持久化

RDB持久化是將當前進程中的數據生成快照保存到硬盤(因此也稱作快照持久化),保存的文件是經過壓縮的二進制文件,後綴是rdb;當Redis重新啟動時,可以讀取快照文件恢復數據。RDB持久化的觸發分為手動觸發和自動觸發兩種。

優點:

1、體積小:相同的數據量rdb數據比aof的小,因為rdb是緊湊型文件;

2、恢復快:因為rdb是數據的快照,數據複製,不需要重新執行命令;

3、性能高:父進程在保存rdb時候只需要fork一個子進程,無需父進程的進行其他io操作,也保證了服務器的性能。

缺點:

1、故障丟失:因為rdb是全量的,我們一般是使用shell腳本實現30分鐘或者1小時或者每天對redis進行rdb備份,但是最少也要5分鐘進行一次的備份,所以當服務死掉後,最少也要丟失5分鐘的數據。

2、耐久性差:相對aof的異步策略來說,因為rdb的複製是全量的,即使是fork的子進程來進行備份,當數據量很大的時候對磁盤的消耗也是不可忽視的,尤其在訪問量高的時候,fork的時間會延長,導致cpu吃緊,耐久性相對較差。

AOF持久化

AOF持久化(即Append Only File持久化),是將Redis執行的每次寫命令記錄到單獨的日誌文件中(有點像MySQL的binlog);當Redis重啟時再次執行AOF文件中的命令來恢復數據。

它的出現是為了彌補RDB的不足(數據的不一致性),所以它採用日誌的形式來記錄每個寫操作,並追加到文件中。我們可以設置不同的 fsync 策略,比如無 fsync ,每秒鐘一次 fsync ,或者每次執行寫入命令時 fsync 。

AOF 的默認策略為每秒鐘 fsync 一次,在這種配置下,Redis 仍然可以保持良好的性能,並且就算髮生故障停機,也最多隻會丟失一秒鐘的數據( fsync 會在後臺線程執行,所以主線程可以繼續努力地處理命令請求)。

優點:

1、數據保證:我們可以設置不同的fsync策略,一般默認是everysec,也可以設置每次寫入追加,服務宕機最多丟失一秒數據

2、文件重寫:當aof文件大小到達一定程度的時候,後臺會自動的去執行aof重寫,此過程不會影響主進程,重寫完成後,新的寫入將會寫到新的aof中,舊的就會被刪除掉。

缺點:

1、性能相對較差:恢復數據需要重新執行命令,性能較RDB低;

2.體積相對更大:儘管是將aof文件重寫了,依然大;

3.恢復速度更慢;

主從複製(讀寫分離)

上面介紹的持久化側重解決的是redis數據的單機備份問題(從內存到硬盤),而主從複製則側重解決的是數據的多機熱備份。先來說下熱備份,就是保證服務正常不間斷運行,通過一臺服務器對另一臺服務器進行實時數據複製,且保障兩邊數據的一致性。我們將在線的備份稱為熱備份,而相對的,將脫機數據備份稱為冷備份。冷備份是在系統不運作時,定時的將數據備份至備份服務器或存儲。

主從複製,是指將一臺Redis服務器的數據,複製到其他的Redis服務器。前者稱為主節點(master),後者稱為從節點(slave);數據的複製是單向的,只能由主節點到從節點。默認情況下,每臺Redis服務器都是主節點;且一個主節點可以有多個從節點(或沒有從節點),但一個從節點只能有一個主節點。

主從複製的作用:

  • 數據冗餘:主從複製實現了數據的熱備份,可以提供遠程備份、數據冗餘,也可以作為服務的冗餘。
  • 負載均衡:在主從複製的基礎上,配合讀寫分離,可以由主節點提供寫服務,由從節點提供讀服務(即寫Redis數據時應用連接主節點,讀Redis數據時應用連接從節點),分擔服務器負載;尤其是在寫少讀多的場景下,通過多個從節點分擔讀負載,可以大大提高Redis服務器的併發量。

主從複製的原理:

主從複製大概分為三個階段:連接建立、數據同步、命令傳播;

(1)連接建立階段主要作用是在主從節點之間建立連接,為數據同步做好準備;

(2)建立連接好之後便可以進行數據同步,也是從階段數據的初始化,數據同步階段是主從複製最核心的階段,根據主從節點當前狀態的不同,可以分為全量複製和部分複製。全量複製顧名思義就是把主節點數據全部複製到從節點中進行備份數據,全量複製在主節點數據量較大時效率太低。於是在Redis2.8引入了部分複製,用於處理網絡中斷時的數據複製,主從節點會自動判斷當前狀態適合全量複製還是部分複製;

(3)數據同步階段完成後,主從節點進入命令傳播階段;在這個階段主節點將自己執行的寫命令發送給從節點,從節點接收命令並執行,從而保證主從節點數據的一致性。在命令傳播階段,除了發送寫命令,主從節點還維持著心跳機制:PING和REPLCONF ACK,心跳機制對於主從複製的超時判斷、數據安全等有作用。

詳細原理請移步至Redis教程主從複製篇!

哨兵機制

提起哨兵,大家想到的是什麼呢?

我們上面說到持久化是為了解決單機redis的數據存儲問題,主從複製可以實現數據冗餘,側重於解決數據的多機熱備份。但是主從複製中存在著一個問題就是故障恢復無法自動化,而redis中的哨兵機制,基於Redis主從複製,主要作用便是解決主節點故障恢復的自動化問題,進一步提高系統的高可用性。但是哨兵機制也存在一定的缺陷,就是寫操作無法負載均衡,存儲能力受到單機限制。

Redis Sentinel,即Redis哨兵,在Redis 2.8版本開始引入。哨兵的核心功能是主節點的自動故障轉移。下面是Redis官方文檔對於哨兵功能的描述:

監控(Monitoring):哨兵會不斷地檢查主節點和從節點是否運作正常。 自動故障轉移(Automatic failover):當主節點不能正常工作時,哨兵會開始自動故障轉移操作,它會將失效主節點的其中一個從節點升級為新的主節點,並讓其他從節點改為複製新的主節點。 配置提供者(Configuration provider):客戶端在初始化時,通過連接哨兵來獲得當前Redis服務的主節點地址。 通知(Notification):哨兵可以將故障轉移的結果發送給客戶端。

Redis哨兵機制的架構

它由兩部分組成,哨兵節點和數據節點:

1、哨兵節點:哨兵系統由一個或多個哨兵節點組成,哨兵節點是特殊的redis節點,不存儲數據。 2、數據節點:主節點和從節點都是數據節點。哨兵系統中的主從節點和普通的主從節點沒有什麼區別,故障發現和轉移是由哨兵來控制和完成的,哨兵的本質也是redis節點,只不過不會存儲數據。每一個哨兵節點只需要配置監控主節點(可以配置監控多個主節點),便可以自動發現其他的哨兵節點和從節點。

哨兵機制原理

  1. 定時任務:每個哨兵節點維護了3個定時任務。

通過向主從節點發送info命令獲取最新的主從結構

通過發佈訂閱功能獲取其他哨兵節點的信息;

通過向其他節點發送ping命令進行心跳檢測,判斷是否下線。

  1. 主觀下線:在心跳檢測的定時任務中,如果其他節點超過一定時間沒有回覆,哨兵節點就會將其進行主觀下線。顧名思義,主觀下線的意思是一個哨兵節點“主觀地”判斷下線;
  2. 客觀下線:哨兵節點在對主節點進行主觀下線後,會通過命令詢問其他哨兵節點該主節點的狀態;如果判斷主節點下線的哨兵數量達到一定數值,則對該主節點進行客觀下線。

需要特別注意的是,客觀下線是主節點才有的概念;如果從節點和哨兵節點發生故障,被哨兵主觀下線後,不會再有後續的客觀下線和故障轉移操作。

  1. 選舉領導者哨兵節點:當主節點被判斷客觀下線以後,各個哨兵節點會進行協商,選舉出一個領導者哨兵節點,並由該領導者節點對其進行故障轉移操作。

監視該主節點的所有哨兵都有可能被選為領導者,選舉使用的算法是Raft算法;Raft算法的基本思路是先到先得:即在一輪選舉中,哨兵A向B發送成為領導者的申請,如果B沒有同意過其他哨兵,則會同意A成為領導者。選舉的具體過程這裡不做詳細描述,一般來說,哨兵選擇的過程很快,誰先完成客觀下線,一般就能成為領導者。

  1. 故障轉移:選舉出的領導者哨兵,開始進行故障轉移操作,該操作大體可以分為3個步驟:

(1)在從節點中選擇新的主節點:選擇的原則是,首先過濾掉不健康的從節點;然後選擇優先級最高的從節點(由slave-priority指定);如果優先級無法區分,則選擇複製偏移量最大的從節點;如果仍無法區分,則選擇runid最小的從節點。

(2)更新主從狀態:通過slaveof no one命令,讓選出來的從節點成為主節點;並通過slaveof命令讓其他節點成為其從節點。

(3)將已經下線的主節點設置為新的主節點的從節點,當原主節點重新上線後,它會成為新的主節點的從節點。

集群機制

前面的哨兵機制存在缺陷,寫操作無法負載均衡,存儲能力受到單機限制。而集群就是為了解決這些問題而誕生的,它是redis3.0開始引入的分佈式存儲方案,集群由多個節點(Node)組成,Redis的數據分佈在這些節點中。集群中的節點分為主節點和從節點:只有主節點負責讀寫請求和集群信息的維護;從節點只進行主節點數據和狀態信息的複製。注意區分和哨兵機制中的主從節點,哨兵中的只有主節點負責寫請求,而從節點負責讀請求。

集群的主要作用可以歸納為兩點:數據分區和高可用;這兩點也正是解決上述的兩個問題,數據分區是為了解決存儲能力受到單機限制,而高可用則是為了解決寫操作無法負載均衡以及實現故障恢復自動化。

數據分區存儲

數據分區有順序分區,哈希分區等,而哈希分區具有天然的隨機性,集群使用的分區方案便是哈希分區的一種。

衡量數據分區方法好壞的標準有很多,其中比較重要的兩個因素是

  • 數據分佈是否均勻
  • 增加或刪減節點對數據分佈的影響。

由於哈希的隨機性,哈希分區基本可以保證數據分佈均勻,因此在比較哈希分區方案時,重點要看增減節點對數據分佈的影響。

哈希分區又可分為哈希取餘分區、一致性哈希分區、帶虛擬節點的一致性哈希分區,接下來簡單介紹下。

哈希取餘分區

哈希取餘分區思路非常簡單:計算key的hash值,然後對節點數量進行取餘,從而決定數據映射到哪個節點上。該方案最大的問題是,當新增或刪減節點時,節點數量發生變化,系統中所有的數據都需要重新計算映射關係,引發大規模數據遷移。

一致性哈希

一致性哈希:將整個哈希值空間組織成一個虛擬的圓環,範圍為0-2^32-1;對於每個數據,根據key計算hash值,確定數據在環上的位置,然後從此位置沿環順時針行走,找到的第一臺服務器就是其應該映射到的服務器。

與哈希取餘分區相比,一致性哈希分區將增減節點的影響限制在相鄰節點。以上圖為例,如果在node1和node2之間增加node5,則只有node2中的一部分數據會遷移到node5;如果去掉node2,則原node2中的數據只會遷移到node4中,只有node4會受影響。

一致性哈希分區的主要問題在於,當節點數量較少時,增加或刪減節點,對單個節點的影響可能很大,造成數據的嚴重不平衡。還是以上圖為例,如果去掉node2,node4中的數據由總數據的1/4左右變為1/2左右,與其他節點相比負載過高。


分享到:


相關文章: