1. 程式人生 > 其它 >Redis設計與實現 讀書筆記

Redis設計與實現 讀書筆記

Redis單執行緒 採用多路 I/O 複用技術可以讓單個執行緒高效的處理多個連線請求(儘量減少網路IO的時間消耗) (1)為什麼不採用多程序或多執行緒處理? - 多執行緒處理可能涉及到鎖 - 多執行緒處理會涉及到執行緒切換而消耗CPU (2)單執行緒處理的缺點? - 無法發揮多核CPU效能,不過可以通過在單機開多個Redis例項來完善   Redis的高併發和快速原因
  • Redis是純記憶體資料庫,一般都是簡單的存取操作,執行緒佔用的時間很多,時間的花費主要集中在IO上,所以讀取速度快。
  • 再說一下IO,Redis使用的是非阻塞IO,IO多路複用,使用了單執行緒來輪詢描述符,將資料庫的開、關、讀、寫都轉換成了事件,減少了執行緒切換時上下文的切換和競爭。
  • Redis採用了單執行緒的模型,保證了每個操作的原子性,也減少了執行緒的上下文切換和競爭。
  • Redis全程使用hash結構,讀取速度快,還有一些特殊的資料結構,對資料儲存進行了優化,如壓縮表,對短資料進行壓縮儲存,再如,跳錶,使用有序的資料結構加快讀取的速度。
  • Redis採用自己實現的事件分離器,效率比較高,內部採用非阻塞的執行方式,吞吐能力比較大。
  Redis事務 redis 事務一次可以執行多條命令,伺服器在執行命令期間,不會去執行其他客戶端的命令請求。 事務中的多條命令被一次性發送給伺服器,而不是一條一條地傳送,這種方式被稱為流水線,它可以減少客戶端與伺服器之間的網路通訊次數從而提升效能。 Redis 最簡單的事務實現方式是使用 MULTI 和 EXEC 命令將事務操作包圍起來。
  • 批量操作在傳送 EXEC 命令前被放入佇列快取。
  • 收到 EXEC 命令後進入事務執行,事務中任意命令執行失敗,其餘命令依然被執行。也就是說 Redis 事務不保證原子性。
  • 在事務執行過程中,其他客戶端提交的命令請求不會插入到事務執行命令序列中。
  redis-cli記憶體統計命令 info memory 1、used_memory redis分配器分配的記憶體總量,包括使用的虛擬記憶體(swap) 2、used_memory_rss redis程序佔據作業系統的記憶體,包括程序執行本身需要的記憶體、記憶體碎片等,但是不包括虛擬記憶體。 PS:used_memory和used_memory_rss,前者是從Redis角度得到的量,後者是從作業系統角度得到的量。二者之所以有所不同,一方面是因為記憶體碎片和Redis程序執行需要佔用記憶體,使得前者可能比後者小,另一方面虛擬記憶體的存在,使得前者可能比後者大。 由於在實際應用中,Redis的資料量會比較大,此時程序執行佔用的記憶體與Redis資料量和記憶體碎片相比,都會小得多;因此used_memory_rss和used_memory的比例便成了衡量Redis記憶體碎片率的引數;這個引數就是mem_fragmentation_ratio。 3、mem_fragmentation_ratio 記憶體碎片比率,used_memory_rss/used_memory 一般大於1,且該值越大,記憶體碎片比例越大。如果小於1,說明redis使用了虛擬記憶體。 PS:一般來說,mem_fragmentation_ratio在1.03左右是比較健康的狀態(對於jemalloc來說);剛啟動的redis的mem_fragmentation_ratio值很大,是因為還沒有向Redis中存入資料,Redis程序本身執行的記憶體使得used_memory_rss 比used_memory大得多。 4、mem_allocator redis記憶體分配器,在編譯時指定。libc、jemalloc或者tcmalloc,預設是jemalloc。   redis記憶體劃分 1、資料 最主要的部分。在used_memory中統計 2、程序本身執行需要的記憶體 大約幾兆,在生產環境中一般忽略不計。這部分記憶體不是由jemalloc分配,因此不會統計在used_memory中 3、緩衝記憶體
  1. 客戶端緩衝區:儲存客戶端連線的輸入輸出緩衝
  2. 複製積壓緩衝區:用於部分複製功能
  3. AOF緩衝區:用於在進行AOF重寫時,儲存最近的寫入命令
這部分記憶體由jemalloc分配,因此會統計在used_memory中 4、記憶體碎片 在分配、回收物理記憶體過程中產生的。   redis資料儲存細節 1、概述 資料儲存細節,涉及到記憶體分配器、簡單動態字串(SDS)、5種物件型別及內部編碼、RedisObject。
  1. redis是key-value資料庫,因此對每個鍵值對都會有一個dictEntry,儲存key和value的指標,next指向下一個dictEntry,與本鍵值對無關。
  2. key 不是直接以字串儲存,二十儲存在SDS結構中
  3. redisObject value的儲存資料結構,五種物件型別都是通過redisObject儲存
  4. jemalloc,記憶體分配器。dictEntry、redisObject和sds物件的記憶體都是通過記憶體分配器分配的。
2、記憶體分配器 Redis在編譯時便會指定記憶體分配器;記憶體分配器可以是 libc 、jemalloc或者tcmalloc,預設是jemalloc。 3、redisObject RedisObject物件非常重要,Redis物件的型別、內部編碼、記憶體回收、共享物件等功能,都需要RedisObject支援,下面將通過RedisObject的結構來說明它是如何起作用的。 typedef struct redisObject {   unsigned type:4;   unsigned encoding:4;   unsigned lru:REDIS_LRU_BITS; /* lru time (relative to server.lruclock) */   int refcount;   void *ptr; } robj;
  1. type 表示物件的型別,佔4個位元。目前包括REDIS_STRING(字串)、REDIS_LIST (列表)、REDIS_HASH(雜湊)、REDIS_SET(集合)、REDIS_ZSET(有序集合)。通過命令type key命令獲取資料的type
  2. encoding 表示物件的內部編碼,佔4個位元。5種物件型別對應的編碼方式以及轉換在後文介紹。
  3. lru 記錄物件最後一次被命令程式訪問的時間,佔據的位元數不同的版本有所不同(如4.0版本佔24位元,2.6版本佔22位元)。
通過對比lru時間與當前時間,可以計算某個物件的空轉時間;object idletime命令可以顯示該空轉時間(單位是秒)。object idletime命令的一個特殊之處在於它不改變物件的lru值。lru值除了通過object idletime命令列印之外,還與Redis的記憶體回收有關係:如果Redis打開了maxmemory選項,且記憶體回收演算法選擇的是volatile-lru或allkeys—lru,那麼當Redis記憶體佔用超過maxmemory指定的值時,Redis會優先選擇空轉時間最長的物件進行釋放。 4.refcount refcount記錄的是共享物件被引用的次數,型別為整型。refcount的作用,主要在於物件的引用計數和記憶體回收:
  • 當建立新物件時,refcount初始化為1;
  • 當有新程式使用該物件時,refcount加1;
  • 當物件不再被一個新程式使用時,refcount減1;
  • 當refcount變為0時,物件佔用的記憶體會被釋放
redis的共享物件目前僅支援整數值的字串物件。之所以如此,實際上是對記憶體和CPU的平衡:共享物件雖然會降低記憶體消耗,但是判斷兩個物件是否相等卻需要消耗額外的時間。 共享物件雖然只能是整數值的字串物件,但是五種型別都可能使用共享物件。 就目前實現來說,redis伺服器在初始化時,會建立10000個字串物件,值分別是0~9999的整數值;當redis需要使用這些值物件時,可以直接使用這些共享物件。10000這個數字可以通過調整引數REDIS_SHARED_INTEGERS的值進行改變。(4.0中是OBJ_SHARED_INTEGERS) 5. ptr ptr指標指向具體的資料,set hello world,ptr指向包含字串world的SDS物件地址。 6. 總結,redisObject的結構與物件型別、編碼、記憶體回收、共享物件都有關係;一個redisObject物件的大小為16位元組:4bit+4bit+24bit+4Byte+8Byte=16Byte。 4、SDS redis沒有直接使用C字串作為預設的字串表示,而是使用SDS(Simple Dynamic String)
  1. 結構
struct sdshdr { int len; int free; char buf[]; }; buf表示位元組陣列,用來儲存字串;len表示buf已使用的長度,free表示buf未使用的長度。 2. SDS與C字串比較 SDS增加了free和len欄位,帶來了如下的好處:
  • 獲取字串長度:SDS是O(1),C字串是O(n)。
  • 緩衝區溢位:使用C字串的API時,如果字串長度增加(如strcat操作)而忘記重新分配記憶體,很容易造成緩衝區的溢位;而SDS由於記錄了長度,相應的API在可能造成緩衝區溢位時會自動重新分配記憶體,杜絕了緩衝區溢位。
  • 修改字串時記憶體的重分配:對於C字串,如果要修改字串,必須要重新分配記憶體(先釋放再申請),因為如果沒有重新分配,字串長度增大時會造成記憶體緩衝區溢位,字串長度減小時會造成記憶體洩露。而對於SDS,由於可以記錄len和free,因此解除了字串長度和空間陣列長度之間的關聯,可以在此基礎上進行優化——空間預分配策略(即分配記憶體時比實際需要的多)使得字串長度增大時重新分配記憶體的概率大大減小;惰性空間釋放策略使得字串長度減小時重新分配記憶體的概率大大減小。
  • 存取二進位制資料:SDS可以,C字串不可以。因為C字串以空字元作為字串結束的標識,而對於一些二進位制檔案(如圖片等),內容可能包括空字串,因此C字串無法正確存取;而SDS以字串長度len來作為字串結束標識,因此沒有這個問題。
  Redis的物件型別與內部編碼 Redis各種物件型別支援的內部編碼如下圖所示(圖中版本為Redis 3.0):

 

 

1、字串 字串長度不能超過512MB,redis中最基礎的型別。鍵和其他型別的元素都是字串。 內部編碼應用場景:
  1. int:8個位元組的長整型。字串值是整型時,這個值使用long整型表示。
  2. embstr:<=39位元組的字串。embstr與raw都使用RedisObject和SDS儲存資料。區別在於:embstr的使用只分配一次記憶體空間(因此RedisObject和SDS是連續的),而raw需要分配兩次記憶體空間(分別為RedisObject和SDS分配空間)。因此與raw相比,embstr的好處在於建立時少分配一次空間、刪除時少釋放一次空間、物件的所有資料連在一起,尋找方便。而embstr的壞處也很明顯:如果字串的長度增加需要重新分配記憶體時,整個RedisObject和SDS都需要重新分配空間,因此Redis中的embstr實現為只讀。
  3. raw:大於39個位元組的字串
embstr和raw進行區分的長度是39是因為RedisObject的長度是16位元組,SDS的長度是9+字串長度.因此當字串長度是39時,embstr的長度正好是16+9+39=64,jemalloc正好可以分配64位元組的記憶體單元。 當int資料不再是整數,或大小超過了long的範圍時,自動轉化為raw。 而對於embstr,由於其實現是隻讀的,因此在對embstr物件進行修改時,都會先轉化為raw再進行修改,因此,只要是修改embstr物件,修改後的物件一定是raw的,無論是否達到了39個位元組。 2、列表 一個列表可以儲存2^32-1個元素,redis列表支援兩端插入和彈出,並可以獲得指定位置的元素,可以充當陣列、佇列、棧等。 內部編碼:壓縮列表ziplist、雙端連結串列linkedlist 雙端連結串列:同時儲存表頭指標和表尾指標,連結串列中儲存了列表的長度。 壓縮列表:為了節約記憶體而開發,由一系列特殊編碼的連續記憶體塊組成的順序型資料結構。進行修改和增刪操作複雜度較高。 壓縮列表的使用條件需要同時滿足以下兩個:1 列表元素個數小於512、列表中所有字串物件都不足64位元組。 只能由壓縮列表轉為雙端連結串列,反向則不可能。 3、雜湊 內部編碼:壓縮列表ziplist、雜湊表hashtable 壓縮列表同列表中的壓縮列表 雜湊表:一個dict結構、兩個dictht結構、一個dictEntry指標陣列(稱為bucket)和多個dictEntry結構組成。

 

 

 

dictEntry dictEntry結構用於儲存鍵值對,結構定義如下: typedef struct dictEntry{ void *key; union{ void *val; uint64_tu64; int64_ts64; }v; struct dictEntry *next; }dictEntry; 其中,各個屬性的功能如下:
  • key:鍵值對中的鍵;
  • val:鍵值對中的值,使用union(即共用體)實現,儲存的內容既可能是一個指向值的指標,也可能是64位整型,或無符號64位整型;
  • next:指向下一個dictEntry,用於解決雜湊衝突問題
  • 在64位系統中,一個dictEntry物件佔24位元組(key/val/next各佔8位元組)。
bucket bucket是一個數組,陣列的每個元素都是指向dictEntry結構的指標。Redis中bucket陣列的大小計算規則如下:大於dictEntry的、最小的2^n。例如,如果有1000個dictEntry,那麼bucket大小為1024;如果有1500個dictEntry,則bucket大小為2048。 dictht dictht結構如下: typedef struct dictht{ dictEntry **table; unsigned long size; unsigned long sizemask; unsigned long used; }dictht; 其中,各個屬性的功能說明如下:
  • table屬性是一個指標,指向bucket;
  • size屬性記錄了雜湊表的大小,即bucket的大小;
  • used記錄了已使用的dictEntry的數量;
  • sizemask屬性的值總是為size-1,這個屬性和雜湊值一起決定一個鍵在table中儲存的位置。
dict 一般來說,通過使用dictht和dictEntry結構,便可以實現普通雜湊表的功能;但是Redis的實現中,在dictht結構的上層,還有一個dict結構。下面說明dict結構的定義及作用。 dict結構如下: typedef struct dict{ dictType *type; void *privdata; dictht ht[2]; int trehashidx; } dict; 其中,type屬性和privdata屬性是為了適應不同型別的鍵值對,用於建立多型字典。 ht屬性和trehashidx屬性則用於rehash,即當雜湊表需要擴充套件或收縮時使用。 ht是一個包含兩個項的陣列,每項都指向一個dictht結構,這也是Redis的雜湊會有1個dict、2個dictht結構的原因。通常情況下,所有的資料都是存在放dict的ht[0]中,ht[1]只在rehash的時候使用。dict進行rehash操作的時候,將ht[0]中的所有資料rehash到ht[1]中。然後將ht[1]賦值給ht[0],並清空ht[1]。 因此,Redis中的雜湊之所以在dictht和dictEntry結構之外還有一個dict結構,一方面是為了適應不同型別的鍵值對,另一方面是為了rehash。 使用壓縮列表條件同列表型別中一樣,hash元素個數小於512、所有鍵值對的鍵和值字串長度都小於64位元組。 有一個條件不滿足則從壓縮列表轉換為雜湊表,反向則不行。 4、集合 集合與列表不同的兩點:元素無序、元素不重複。一個集合最多可以儲存2^32-1個元素。支援多個集合取交集、並集和差集。 內部編碼:整數集合intset、雜湊表hashtable 使用雜湊表的時候,值全部是null,相當於只使用了鍵。 整數集合的結構定義: typedef struct intset{ uint32_t encoding; uint32_t length; int8_t contents[]; } intset; 其中,encoding代表contents中儲存內容的型別,雖然contents(儲存集合中的元素)是int8_t型別,但實際上其儲存的值是int16_t、int32_t或int64_t,具體的型別便是由encoding決定的。length表示元素個數。 使用整數集合需同時滿足兩個條件:集合元素個數小於512、所有元素都是整數值。 有一個不滿足則轉換為雜湊表,反向則不行。 5、有序集合 與集合不同的是,有序集合中元素是有順序的。與列表使用索引下標作為排序依據不同,有序集合為每個元素設定一個分數(score)作為排序依據。 內部編碼:壓縮列表ziplist、跳躍表skiplist 跳躍表是一種有序資料結構,通過在每個節點中維持多個指向其它節點的指標,從而達到快速訪問節點的目的。 除了跳躍表,實現有序資料結構的另一種典型實現是平衡樹;大多數情況下,跳躍表的效率可以和平衡樹媲美,且跳躍表實現比平衡樹簡單很多,因此Redis中選用跳躍表代替平衡樹。 跳躍表支援平均O(logN)、最壞O(N)的複雜點進行節點查詢,並支援順序操作。Redis的跳躍表實現由zskiplist和zskiplistNode兩個結構組成:前者用於儲存跳躍表資訊(如頭結點、尾節點、長度等),後者用於表示跳躍表節點。 使用壓縮列表需要滿足兩個條件:元素個數小於128、所有成員長度都不足64位元組 如果有一個條件不滿足則使用跳躍表,反向轉換不行。   Redis淘汰策略

 

 

  Redis持久化 RDB:快照形式是直接把記憶體中的資料儲存到一個 dump 的檔案中,定時儲存,儲存策略。 AOF:把所有的對 Redis 的伺服器進行修改的命令都存到一個檔案裡,命令的集合。Redis 預設是快照 RDB 的持久化方式。 Redis重啟後,優先使用AOF檔案來還原資料集,AOF儲存的資料集通常比RDB更完整。 RDB原理:redis在需要做持久化的時候,fork一個子程序,子程序將資料寫到磁碟的一個臨時RDB檔案中,在完成寫臨時檔案後,將原來的RDB替換掉,這樣的好處是可以copy-on-write。適合用於備份。 AOF原理:將每一個寫命令通過write函式追加到appendonly.aof檔案中,配置如下: appendfsync yes appendfsync always #每次有資料修改發生時都會寫入AOF檔案。 appendfsync everysec #每秒鐘同步一次,該策略為AOF的預設策略。 AOF 可以做到全程持久化,只需要在配置中開啟 appendonly yes。這樣 Redis 每執行一個修改資料的命令,都會把它新增到 AOF 檔案中,當 Redis 重啟時,將會讀取 AOF 檔案進行重放,恢復到 Redis 關閉前的最後時刻。   主從複製 主負責寫操作,從節點提供讀操作。 過程:
  • 從節點執行 slaveof [masterIP] [masterPort],儲存主節點資訊。
  • 從節點中的定時任務發現主節點資訊,建立和主節點的 Socket 連線。
  • 從節點發送 Ping 訊號,主節點返回 Pong,兩邊能互相通訊。
  • 連線建立後,主節點將所有資料傳送給從節點(資料同步)。
  • 主節點把當前的資料同步給從節點後,便完成了複製的建立過程。接下來,主節點就會持續的把寫命令傳送給從節點,保證主從資料一致性。
資料同步過程: redis2.8之前使用sync [runId] [offset]同步命令,redis 2.8之後使用psync [runid] [offset]命令。 sync僅支援全量複製,psync支援全量和部分複製。 runid 每個redis節點啟動都會生成唯一的uuid offset 主節點和從節點都各自維護自己的主從複製偏移量offset,當主節點有寫入命令時,offset=offset+命令的位元組長度。從節點在收到主節點發送的命令後,也會增加自己的offset,並把自己的offset傳送給主節點。這樣主節點同事儲存自己的offset和從節點的offset,通過對比offset來判斷主從節點資料是否一致。 repl_backlog_size 儲存在主節點上的一個固定長度的先進先出佇列,預設大小是1MB。

 

 

上面是 Psync 的執行流程,從節點發送 psync[runId][offset] 命令,主節點有三種響應:
  • FULLRESYNC:第一次連線,進行全量複製
  • CONTINUE:進行部分複製
  • ERR:不支援 psync 命令,進行全量複製
全量複製的流程。主要有以下幾步:
  • 從節點發送 psync ? -1 命令(因為第一次傳送,不知道主節點的 runId,所以為?,因為是第一次複製,所以 offset=-1)。
  • 主節點發現從節點是第一次複製,返回 FULLRESYNC {runId} {offset},runId 是主節點的 runId,offset 是主節點目前的 offset。
  • 從節點接收主節點資訊後,儲存到 info 中。
  • 主節點在傳送 FULLRESYNC 後,啟動 bgsave 命令,生成 RDB 檔案(資料持久化)。
  • 主節點發送 RDB 檔案給從節點。到從節點載入資料完成這段期間主節點的寫命令放入緩衝區。
  • 從節點清理自己的資料庫資料。
  • 從節點載入 RDB 檔案,將資料儲存到自己的資料庫中。如果從節點開啟了 AOF,從節點會非同步重寫 AOF 檔案。
關於部分複製有以下幾點說明: ①部分複製主要是 Redis 針對全量複製的過高開銷做出的一種優化措施,使用 psync[runId][offset] 命令實現。 當從節點正在複製主節點時,如果出現網路閃斷或者命令丟失等異常情況時,從節點會向主節點要求補發丟失的命令資料,主節點的複製積壓緩衝區將這部分資料直接傳送給從節點。 這樣就可以保持主從節點複製的一致性。補發的這部分資料一般遠遠小於全量資料。 ②主從連線中斷期間主節點依然響應命令,但因複製連線中斷命令無法傳送給從節點,不過主節點內的複製積壓緩衝區依然可以儲存最近一段時間的寫命令資料。 ③當主從連線恢復後,由於從節點之前儲存了自身已複製的偏移量和主節點的執行 ID。因此會把它們當做 psync 引數傳送給主節點,要求進行部分複製。 ④主節點接收到 psync 命令後首先核對引數 runId 是否與自身一致,如果一致,說明之前複製的是當前主節點。 之後根據引數 offset 在複製積壓緩衝區中查詢,如果 offset 之後的資料存在,則對從節點發送+COUTINUE 命令,表示可以進行部分複製。因為緩衝區大小固定,若發生緩衝溢位,則進行全量複製。 ⑤主節點根據偏移量把複製積壓緩衝區裡的資料傳送給從節點,保證主從複製進入正常狀態。   主從複製存在的問題:
  • 一旦主節點宕機,從節點晉升為主節點,同時需要修改應用方的主節點地址,還需要命令所有從節點去複製新的主節點,整個過程需要人工干預。
  • 主節點的寫能力受到單機的限制。
  • 主節點的儲存能力受到單機的限制。
  • 原生複製的弊端在早期的版本中也會比較突出,比如:Redis 複製中斷後,從節點會發起 psync。此時如果同步不成功,則會進行全量同步,主庫執行全量備份的同時,可能會造成毫秒或秒級的卡頓。
  哨兵

 

 

Redis Sentinel(哨兵)主要功能包括主節點存活檢測、主從執行情況檢測、自動故障轉移、主從切換。 哨兵系統可以執行以下四個任務:
  • 監控:不斷檢查主伺服器和從伺服器是否正常執行。
  • 通知:當被監控的某個 Redis 伺服器出現問題,Sentinel 通過 API 指令碼向管理員或者其他應用程式發出通知。
  • 自動故障轉移:當主節點不能正常工作時,Sentinel 會開始一次自動的故障轉移操作,它會將與失效主節點是主從關係的其中一個從節點升級為新的主節點,並且將其他的從節點指向新的主節點,這樣人工干預就可以免了。
  • 配置提供者:在 Redis Sentinel 模式下,客戶端應用在初始化時連線的是 Sentinel 節點集合,從中獲取主節點的資訊。
  哨兵工作原理: ①每個 Sentinel 節點都需要定期執行以下任務:每個 Sentinel 以每秒一次的頻率,向它所知的主伺服器、從伺服器以及其他的 Sentinel 例項傳送一個 PING 命令 ②如果一個例項距離最後一次有效回覆 PING 命令的時間超過 down-after-milliseconds 所指定的值,那麼這個例項會被 Sentinel 標記為主觀下線 ③如果一個主伺服器被標記為主觀下線,那麼正在監視這個伺服器的所有 Sentinel 節點,要以每秒一次的頻率確認主伺服器的確進入了主觀下線狀態 ④如果一個主伺服器被標記為主觀下線,並且有足夠數量的 Sentinel(至少要達到配置檔案指定的數量)在指定的時間範圍內同意這一判斷,那麼這個主伺服器被標記為客觀下線 ⑤一般情況下,每個 Sentinel 會以每 10 秒一次的頻率向它已知的所有主伺服器和從伺服器傳送 INFO 命令。 當一個主伺服器被標記為客觀下線時,Sentinel 向下線主伺服器的所有從伺服器傳送 INFO 命令的頻率,會從 10 秒一次改為每秒一次。 ⑥Sentinel 和其他 Sentinel 協商客觀下線的主節點的狀態,如果處於 SDOWN 狀態,則投票自動選出新的主節點,將剩餘從節點指向新的主節點進行資料複製。 ⑦當沒有足夠數量的 Sentinel 同意主伺服器下線時,主伺服器的客觀下線狀態就會被移除。 當主伺服器重新向 Sentinel 的 PING 命令返回有效回覆時,主伺服器的主觀下線狀態就會被移除。   PS: 1 Redis Sentinal著眼於高可用,在master宕機時會自動將slave提升為master,繼續提供服務。 2 Redis Cluster著眼於擴充套件性,在單個redis記憶體不足時,使用Cluster進行分片儲存。   Redis資料分片機制 Redis Cluster 採用虛擬雜湊槽分割槽,所有的鍵根據雜湊函式對映到 0 ~ 16383 整數槽內,每個key通過CRC16校驗後對16384取模來決定放置哪個槽(Slot),每一個節點負責維護一部分槽以及槽所對映的鍵值資料。 計算公式:slot = CRC16(key) & 16383。 使用雜湊槽的好處就在於可以方便的新增或移除節點。
  1. 當需要增加節點時,只需要把其他節點的某些雜湊槽挪到新節點就可以了;
  2. 當需要移除節點時,只需要把移除節點上的雜湊槽挪到其他節點就行了。
 

 

 

Pipeline redis客戶端執行一條命令的過程: 1 傳送命令 2 命令排隊 3 命令執行 4 返回結果 1-4的過程時間被稱為RTT 往返時間 redis提供批量操作命令mget、mset,可以有效地節約RTT(redis往返時間)。但是大部分命令都不支援批量操作。Pipeline能改善這個問題。 Pipeline能夠將命令進行組裝,一次RTT傳輸給Redis,再按這組命令的執行結果按順序返回給客戶端。 PS:pipeline不是原子操作,實際是多個命令   Bitmaps 本節將每個獨立使用者是否訪問過網站存放在Bitmaps中,將訪問的使用者記做1,沒有訪問的使用者記做0,用偏移量作為使用者的id。 setbit key offset value getbit key offset 統計bitcount key [startoffset] [endoffset] 運算bitop [and|or|not|xor] 目標key 運算key1 運算key2...   GEO 1 新增位置經緯度 geoadd key 經度 緯度 地址資訊 [經度 緯度 地址資訊...] 2 獲取地理位置資訊 geopos key 地址資訊 [地址資訊...] 3 獲取兩個地理位置的距離 geolist key 地址資訊1 地址資訊2 單位(m:米,km:公里,mi:英里,ft:尺) 4 獲取指定位置範圍內的地理資訊位置集合 georadius key longitude latitude radiusm|km|ft|mi [withcoord] [withdist] [withhash] [COUNT count] [asc|desc] [store key] [storedist key] georadiusbymember key member radiusm|km|ft|mi [withcoord] [withdist] [withhash] [COUNT count] [asc|desc] [store key] [storedist key] georadius和georadiusbymember兩個命令的作用是一樣的,都是以一個地 理位置為中心算出指定半徑內的其他地理資訊位置,不同的是georadius命令 的中心位置給出了具體的經緯度,georadiusbymember只需給出成員即可。 5 獲取geohash redis使用geohash將二維經緯度轉換為一維字串,字串越長表示位置更精準 6 刪除地址資訊 zrem key 地址資訊