什么是 redis?它主要用來什么的?
Redis(Remote Dictionary Server)是一個開源的使用ANSI C編寫、支持網絡、可基于內存亦可持久化的日志型、Key-Value數據庫,并提供多種語言的API。
它支持數據結構非常多樣,包括字符串(string)、哈希(hash)、列表(list)、集合(sets)、有序集合(sorted sets)等。
Redis的主要特性和用途包括:
1、 性能高效:Redis能讀的速度是110000次/s,寫的速度是81000次/s,因此它經常被用作高速緩存。
2、 數據持久化:Redis可以定期將內存中的數據寫入磁盤或者把修改操作寫入追加的記錄文件,以保證數據的持久化。
3、 發布訂閱系統:Redis支持發布訂閱模式,可以用于構建實時消息系統。
4、 數據過期功能:Redis中的每個Key-Value對都可以設置過期時間,非常適合用于實現緩存功能。
5、 原子操作:所有Redis操作都是原子性的,意味著要么成功執行要么失敗完全不執行,這對于分布式系統來說是非常重要的。
6、 支持多種語言:Redis提供了多種語言的客戶端,如JAVA,C,C++,php,Python/ target=_blank class=infotextkey>Python,JavaScript等。
因此,Redis可以用于各種場景,如緩存系統、消息隊列系統、排行榜等等。
什么是熱 Key 問題,如何解決熱 key 問題
在Redis中,熱Key問題是指某一個或幾個Key被大量并發的讀寫,這樣會導致大量的請求集中在單個或少數幾個Redis節點上,而其他節點則閑置,造成資源的嚴重浪費,且可能會導致部分節點響應延遲,影響整個系統的性能。

解決熱Key問題的方法有很多種,下面是幾種常見的方法:
1、 數據分片:將熱Key的數據分片到多個Key中去,這樣就可以將訪問分散到多個節點上,降低單個節點的壓力。
2、 讀寫分離:對于讀多寫少的熱Key,可以使用Redis的主從復制功能,將讀操作分散到多個從節點上,減輕主節點的壓力。
3、 使用緩存過期策略:對于熱Key,可以設置一個合理的過期時間,避免長時間的熱點訪問。
4、 增加緩存層:在應用層和Redis之間增加一層緩存,可以是本地緩存或者是分布式緩存,比如使用Memcached或者是使用Guava Cache等。
5、 使用第三方解決方案:一些第三方中間件提供了熱Key的解決方案,例如使用一些Redis集群中間件,它們可以自動進行數據分片,有效地解決熱Key問題。
需要注意的是,解決熱Key問題沒有統一的解決方案,需要根據具體的業務場景和需求來選擇合適的方法。
Redis 為什么這么快?

Redis 的高速性能主要可以歸因于以下幾個因素:
1、 基于內存的存儲:Redis 是一個內存數據庫,所有的數據都存儲在內存中。內存數據庫的訪問速度遠超于傳統的磁盤存儲,這是 Redis 能提供高速數據訪問的關鍵原因。
2、 高效的數據結構:Redis 使用高效的數據結構,如哈希表和跳表,來存儲和查找數據,這使得數據的讀取和寫入都非常快速。
3、 簡單的單線程模型:Redis 的操作都是單線程的,它避免了常規數據庫需要進行大量的上下文切換和鎖競爭的開銷。盡管是單線程,但是由于 Redis 主要運行在內存中,并且使用了高效的數據結構,所以其性能非常高。
4、 優化的數據編碼:Redis 使用一種稱為 "RESP" 的簡單協議。這種協議使得服務器和客戶端的交互變得簡單快速。此外,Redis 使用了一種緊湊的存儲格式來存儲數據,從而減少了內存使用。
5、 支持管道化(Pipelining)操作:Redis 客戶端可以一次發送多個命令給服務器,而不需要等待每個命令的回復。這減少了網絡延遲,使得 Redis 可以提供更高的吞吐量。
6、 異步復制和快照:Redis 的數據持久化操作是異步進行的,這意味著它不會阻塞主線程的執行。
因此,Redis 的設計和實現使得它能夠提供非常高的性能和低延遲的數據訪問。
SDS 簡單動態字符串

SDS,即Simple Dynamic String,簡單動態字符串,是Redis自主實現的一種動態字符串表示方法,被廣泛應用于Redis的各種數據結構中。與C語言中的字符串相比,SDS具有一些優點:
1、 動態性:SDS可以根據需要動態調整字符串的長度,避免了C語言字符串可能出現的緩沖區溢出問題。
2、 空間預分配:當SDS的字符串長度需要增長時,除了為字符串實際需要的長度外,SDS還會分配額外的未使用空間。這個策略減少了連續執行字符串增長操作所需的內存重分配次數。
3、 惰性空間釋放:當SDS的字符串長度縮短時,SDS并不立即使用內存重分配來回收縮短后多出來的字節,而是使用未使用空間來保存這些字節,待將來使用。
4、 二進制安全:C語言字符串由于以作為結束符,因此不能保存像圖片、音頻、視頻、壓縮文件等二進制數據。而SDS則可以保存任何二進制數據。
5、 API豐富:SDS提供了一系列API用于滿足各種字符串操作。
6、 獲取字符串長度的復雜度為O(1):與C語言字符串需要遍歷整個字符串才能知道字符串長度不同,SDS的len屬性使得獲取字符串長度的復雜度降到了O(1)。
因為以上的優點,SDS成為了Redis的主要字符串表示方式,大大提高了其效率和安全性。
合理的線程模型
合理的線程模型對于提高Redis的性能非常重要。Redis采用了單線程模型以及I/O多路復用技術。
I/O多路復用是一種允許單個線程同時監視多個文件描述符(通常是網絡套接字)的事件的技術。當某個文件描述符上的事件觸發時,它可以執行相應的操作,例如讀取或寫入數據。使用I/O多路復用技術,Redis可以在單線程中處理多個客戶端連接,并在任何時候處理活躍的連接。這樣可以避免線程同步和上下文切換的開銷,從而提高性能。
在Redis中,I/O多路復用主要通過以下幾種方法實現:
1、 select 2、 poll 3、 epoll(linux) 4、 kqueue(BSD,macOS) 5、 evport(Solaris)
根據操作系統和編譯時的可用性,Redis會選擇其中一種方法作為I/O多路復用的實現。
單線程模型指的是Redis在處理客戶端請求時僅使用一個線程。由于Redis主要運行在內存中,使用了高效的數據結構,因此單線程模型足以滿足大部分場景的性能需求。單線程模型避免了多線程模型中的鎖競爭、上下文切換等開銷,使得Redis能夠更高效地處理請求。
總之,Redis通過合理的線程模型和I/O多路復用技術,在單線程模型下實現了高性能的并發處理能力。
Redis 的過期策略
Redis 的過期策略主要包括三種方式:定時過期,惰性過期,定期過期。這些策略用于處理設置了過期時間的鍵值對。
1、 定時過期:當設置鍵的過期時間時,同時創建一個定時器,讓定時器在鍵的過期時間來臨時,立即執行對鍵的刪除操作。這種方式可以保證過期鍵會被及時清除,不會占用過多的內存空間。但是在大量使用過期鍵的情況下,會創建大量的定時器,消耗大量的CPU資源去處理定時器事件,可能會導致服務器變慢。
2、 惰性過期:只有當有命令對鍵進行訪問的時候,才會檢查該鍵是否過期,如果過期就刪除。這種方式CPU資源消耗最小,但是如果過期鍵如果長期沒有被訪問,那么這些過期鍵就會一直存在于內存中,造成內存浪費。
3、 定期過期:Redis每隔一段時間,程序就隨機測試一些設置了過期時間的鍵,然后剔除其中的過期鍵。這種方式是定時過期和惰性過期的折中方案,通過限制刪除過期鍵的操作次數,保證了刪除操作不會占用太多CPU時間,又可以有效地釋放過期鍵占用的內存。
Redis 實際上同時使用了惰性過期和定期過期兩種策略:當某個鍵被訪問時,如果該鍵已過期,那么會采用惰性過期策略立即刪除;而定期過期策略則會定時地在后臺刪除過期鍵,以減少內存的使用。至于定時過期策略,因為其在大規模使用時對CPU資源的消耗過大,Redis并未采用。
Redis 內存淘汰策略
當 Redis 的內存使用超過了最大內存限制時,我們需要一種機制來清理舊的數據,釋放內存空間。這種機制就是內存淘汰策略。下面是 Redis 提供的8種內存淘汰策略:
1、 noeviction:這是默認策略。當內存使用達到上限時,如果客戶端繼續執行導致內存增長的命令(比如 SET),Redis 會返回一個錯誤信息。這意味著 Redis 不會主動淘汰任何數據,只有顯示地通過命令(比如 DEL)刪除數據才會釋放內存。
2、 volatile-lru:在鍵設置了過期時間的數據集中,優先移除最少使用的鍵。
3、 allkeys-lru:在所有的數據集中,優先移除最少使用的鍵。不管鍵是否設置了過期時間。
4、 volatile-random:在鍵設置了過期時間的數據集中,隨機移除鍵。
5、 allkeys-random:在所有的數據集中,隨機移除鍵。不管鍵是否設置了過期時間。
6、 volatile-ttl:在鍵設置了過期時間的數據集中,有更早過期時間的鍵優先移除。
7、 volatile-lfu:在鍵設置了過期時間的數據集中,使用LFU(Least Frequently Used,最不頻繁使用)算法移除鍵。
8、 allkeys-lfu:在所有的數據集中,使用LFU算法移除鍵。不管鍵是否設置了過期時間。
需要注意的是,選擇什么樣的淘汰策略需要根據實際業務需求和場景決定,不同的策略對應用的影響也會不同。
Redis的應用場景-排行榜
下面是一個使用Java和Redis實現點贊排行榜的簡單示例。
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Tuple;
import java.util.Set;
public class RedisLikeLeaderboard {
private static final String LEADERBOARD_KEY = "like_leaderboard";
private Jedis jedis;
public RedisLikeLeaderboard(Jedis jedis) {
this.jedis = jedis;
}
// 點贊操作,每次+1
public void like(String post) {
jedis.zincrby(LEADERBOARD_KEY, 1, post);
}
// 獲取點贊數
public Double getLikes(String post) {
return jedis.zscore(LEADERBOARD_KEY, post);
}
// 獲取點贊排行榜前10的帖子
public Set<Tuple> getTopPosts() {
return jedis.zrevrangeWithScores(LEADERBOARD_KEY, 0, 9);
}
public static void mAIn(String[] args) {
Jedis jedis = new Jedis("localhost");
RedisLikeLeaderboard leaderboard = new RedisLikeLeaderboard(jedis);
for(int i=1; i<=20; i++){
for(int j=0; j<i; j++){
leaderboard.like("post" + i);
}
}
Set<Tuple> topPosts = leaderboard.getTopPosts();
for (Tuple post : topPosts) {
System.out.println(post.getElement() + ": " + post.getScore());
}
}
}
在這個例子中,我們使用Redis的有序集合(ZSET)來存儲帖子的點贊數。每次點贊,我們用zincrby方法增加相應帖子的分數。然后,我們使用zrevrangeWithScores方法獲取點贊數最多的前10個帖子。
在main函數中,我們模擬了20個帖子的點贊,點贊次數與帖子編號相同(如post1點贊1次,post2點贊2次,以此類推)。然后我們獲取并打印出點贊最多的前10個帖子。
Redis 持久化有哪幾種方式,怎么選?
Redis提供了兩種主要的數據持久化方法:RDB(快照)和AOF(追加文件)。
1、 RDB (Redis DataBase): RDB持久化方式是通過將某一時刻點的所有數據寫入一個dump.rdb的文件中實現的。、可以配置Redis在n秒內如果超過m個key被修改就自動做一次dump,這個就是快照。當Redis重啟的時候會通過載入dump.rdb文件恢復數據。
2、 AOF (Append Only File): AOF持久化方式記錄每次對數據庫的寫操作,當Redis重啟的時候會通過重新執行這些命令來達到恢復數據的目的。Redis的AOF持久化方式提供了不同的fsync策略,如每次寫操作都fsync(更安全)或者每秒fsync一次(可能丟失這1秒的數據,但性能好)。
在選擇持久化方式時,、需要考慮以下幾個因素:
1、 數據安全性:如果、非常關心數據的完整性,并且不能容忍數據的丟失,那么AOF是一個更好的選擇。因為AOF提供了每次寫操作都fsync的模式,盡管這會降低寫入性能。
2、 性能:如果、更關心性能,并且能容忍少量數據丟失,那么RDB可能更適合。RDB是通過快照來保存數據的,所以它不需要記錄每一次寫操作。
3、 復雜性:AOF文件可能會比RDB文件更大,并且恢復速度可能會比RDB慢,因為它需要重新執行所有的寫操作。但是AOF的文件通常比RDB更健壯。
4、 備份:RDB方式更適合用于全量備份。比如每天的凌晨2點,做一次全量備份。同時AOF在Redis意外宕機時數據恢復更完整。
5、 特殊需求:如果、需要實時備份,那么AOF的每秒fsync模式可能是一個好選擇。如果、需要最大化數據的安全性,、也可以同時開啟RDB和AOF。
最后,根據業務場景和需求,、可以選擇合適的持久化方式,也可以選擇同時使用RDB和AOF。
AOF 是執行完命令后才記錄日志的。為什么不先記錄日志再執行命令呢?
在 Redis 的設計中,AOF 持久化是在執行完命令后才記錄日志的。這種設計的一個主要原因是為了保證數據的一致性。
如果先記錄日志,然后再執行命令,可能會出現以下情況:日志已經記錄,但命令在執行過程中發生錯誤,導致命令沒有成功執行。這時,日志中的信息和實際的數據狀態就會不一致。
相反,如果是先執行命令,成功后再記錄日志,就可以確保日志中的信息和數據的實際狀態始終保持一致。如果命令執行失敗,那么日志也不會記錄這個命令,因此不會出現數據不一致的問題。
另外,還需要注意的是,Redis 的大部分操作都是在內存中完成的,速度非常快,所以即使是在執行完命令后才記錄日志,也不會造成明顯的性能影響。
總的來說,先執行命令再記錄日志,是為了保證數據的一致性,并且在實際操作中,這種方式的性能影響是可以接受的。
AOF 機制的三種寫回策略
Redis 的 AOF (Append Only File) 持久化機制有三種 fsync 策略,可以在 redis.conf 配置文件中設置:
1、 always:每次執行寫命令后,立即將數據同步到磁盤。這種方式的數據安全性最高,但是對性能的影響也最大,因為每次寫命令都需要進行磁盤 I/O 操作。
2、 everysec:每秒鐘將數據同步到磁盤一次。這種方式是默認的 fsync 策略,它在數據安全性和性能之間做了一個折衷。在最壞的情況下,如果系統發生故障,、可能會丟失最近一秒鐘的數據。
3、 no:讓操作系統決定何時進行數據同步。這種方式的性能最好,因為 Redis 不需要頻繁地進行磁盤 I/O 操作。但是,數據安全性最低,如果系統發生故障,可能會丟失更多的數據。
選擇哪種 fsync 策略,主要取決于、對數據安全性和性能的需求。如果對數據的完整性要求非常高,那么可以選擇 always 策略。如果對性能有較高的要求,可以選擇 no 策略。如果需要在數據安全性和性能之間做一個平衡,那么可以選擇 everysec 策略。
AOF 重寫機制是啥?AOF 重寫會阻塞嘛?
AOF 重寫(rewrite)是 Redis 提供的一種優化機制,其目的是減少 AOF 文件的大小。在 Redis 的運行過程中,由于每一次寫操作的命令都會被記錄到 AOF 文件,因此 AOF 文件的大小會隨著時間的推移而持續增長。如果不進行優化,AOF 文件可能會占用大量的磁盤空間,而且數據恢復的速度也會變慢。因此,Redis 提供了 AOF 重寫機制,以優化 AOF 文件。
AOF 重寫的過程是這樣的:Redis 會創建一個新的 AOF 文件,然后遍歷當前數據庫中的所有數據,對于每一條數據,Redis 會生成一個或多個寫命令,這些命令能夠重建當前的數據狀態,并將這些命令寫入新的 AOF 文件。這樣,新的 AOF 文件就包含了最小的、能夠重建當前數據狀態的命令集合。完成 AOF 重寫后,Redis 會將新的 AOF 文件替換掉舊的 AOF 文件。
至于 AOF 重寫是否會阻塞 Redis,這取決于、使用的 Redis 版本和配置。在 Redis 2.4 版本之前,AOF 重寫是一個阻塞操作,也就是說,在 AOF 重寫過程中,Redis 不會處理其他命令。從 Redis 2.4 版本開始,Redis 使用了一個子進程來進行 AOF 重寫,這樣主進程就可以在 AOF 重寫過程中繼續處理命令,因此 AOF 重寫不會阻塞 Redis。但是需要注意的是,當 AOF 重寫完成后,Redis 需要將新的 AOF 文件替換掉舊的 AOF 文件,這個替換過程是一個阻塞操作。
Redis有哪些優缺點
Redis具有以下優點:
1、 性能高:Redis所有的操作都是內存級別的,讀寫速度非常快。
2、 數據類型豐富:Redis不僅支持基本的鍵值類型,還提供list,set,zset,hash等數據結構。
3、 持久化:通過RDB和AOF兩種方式進行持久化,可以根據實際需求進行靈活配置。
4、 支持發布訂閱模式:Redis支持PUB/SUB模式,可以用于構建實時消息系統。
5、 支持事務:Redis支持事務,可以保證一系列命令的原子性執行。
6、 支持Lua腳本:Redis支持Lua腳本,可以使用Lua腳本來完成一些復雜的操作。
然而,Redis也有一些缺點:
1、 單線程:Redis的操作雖然都是基于內存的,但它是單線程的,CPU資源不能得到充分利用。
2、 內存限制:由于數據存儲在內存中,所以Redis對內存的需求比較大,如果數據量特別大,成本較高。
3、 數據安全:雖然Redis提供了持久化機制,但如果不合理配置,可能會有數據丟失的風險。
4、 不適合復雜的查詢:Redis雖然提供了豐富的數據類型,但不支持像SQL那樣復雜的查詢。
綜上,Redis非常適合需要高性能和豐富數據類型的場景,但如果需要處理大量數據或者進行復雜查詢,可能需要考慮其他解決方案。
Redis 類型介紹
以下是Redis支持的8種數據類型的簡要介紹:
1、 String:字符串類型是Redis最基礎的數據類型,一個鍵最大能存儲512MB。除了常規的get、set操作,、還可以進行一些像增加(incr)、減少(decr)、追加(append)等復雜操作。
2、 List:列表類型是Redis的雙向鏈表。、可以向列表的頭部(左邊)或尾部(右邊)添加一個或多個元素,或者從頭部或尾部獲取元素。列表最多可以包含 2^32 - 1 元素 (4294967295, 每個列表超過40億個元素)。
3、 Set:集合類型是一個無序且不重復的元素集合,集合中最大的成員數為 2^32 - 1 (4294967295, 每個集合可存儲40億個成員)。、可以添加、刪除、查找一個元素是否存在等。此外,Redis還提供了多個集合之間的運算操作,如并集(sunion),交集(sinter),差集(sdiff)等。
4、 Zset:有序集合類型也是一個不允許重復的元素集合,但每個元素都會關聯一個double類型的分數。Redis正是通過分數來為集合中的成員進行從小到大的排序。有序集合的成員是唯一的, 但分數(score)卻可以重復。
5、 Hash:哈希類型是一個鍵值對集合。哈希類型的數據非常適合用于存儲對象,每個哈希可以存儲 2^32 - 1 鍵值對(40億個)。
6、 Bitmap:位圖類型實際上就是一個以位為單位的數組,數組的每個單元只能存儲0和1。雖然位圖類型本質上是字符串類型,但是可以對位圖進行一些特殊操作,如設置某一位的值,獲取某一位的值等。
7、 HyperLogLog:HyperLogLog是用來做基數統計的一種數據結構,HyperLogLog只會根據輸入元素來計算基數,但不會儲存輸入元素本身,所以HyperLogLog占用的空間是固定的、并非常小。在Redis中,每個HyperLogLog鍵只需要花費12KB內存,就可以計算接近2^64個不同元素的基數。
8、 Geospatial:地理位置數據類型是Redis的zset類型的擴展,用于管理地理空間信息。、可以添加地理位置相關的經度、緯度、名稱,然后、可以基于地理位置進行一些操作,如計算兩個地點之間的距離,獲取某個范圍內的元素等。Geospatial類型使用的命令主要有GEOADD、GEODIST、GEOHASH、GEOPOS和GEORADIUS等。
這些數據類型提供了強大的功能,、可以根據應用需求選擇最適合的類型。例如,如果、需要保存一些具有相同屬性的復雜對象,哈希可能是一個好的選擇。如果你需要保存和排序一些值,那么有序集合可能是一個更好的選擇。
我們知道通過expire來設置key 的過期時間,那么對過期的數據怎么處理呢?
Redis中的數據過期處理主要依靠兩種策略:惰性過期和定時過期。
1、 惰性過期:當客戶端嘗試訪問一個key的時候,Redis會檢查這個key是否設置了過期時間,如果設置了并且已經過期,Redis會在此時立即刪除這個key。這就是所謂的惰性過期,只有在嘗試訪問一個key的時候才會判斷并進行過期處理。
2、 定時過期:由于只依賴惰性過期可能會導致很多過期的key沒有被及時清理,占用大量內存,所以Redis還會定期進行過期key的掃描清理。Redis會隨機抽取一些設置了過期時間的key,刪除其中已經過期的key。這個操作是在后臺定期進行的,是為了補充惰性過期可能帶來的不及時清理問題。
需要注意的是,Redis的這兩種過期策略旨在實現一個平衡,既能盡可能快地釋放過期key占用的內存,又不會因為頻繁的過期掃描影響Redis的性能。在實際使用中,過期的數據處理通常是Redis自動完成的,用戶無需關心。
另外,在Redis的內存數據集大小上升到達Redis最大內存限制時,它會根據設定的內存淘汰策略開始淘汰數據,其中也包括過期的數據。
Redis如何做內存優化?
Redis 是一個內存存儲系統,因此內存優化是非常重要的。以下是一些可以幫助優化 Redis 內存使用的技術:
1、 選擇適當的數據類型: Redis 提供了多種數據類型(如字符串,列表,集合,哈希表,有序集合等)。選擇最適合您數據特性的數據類型可以幫助優化內存使用。例如,如果您需要存儲大量小的字段集,哈希表可能是一個比字符串更好的選擇。
2、 使用哈希表存儲大量小對象: 如果應用程序使用了大量的小對象,一個有效的內存優化策略是使用 Redis 的哈希表來存儲這些對象。由于 Redis 的哈希表可以進行特殊的內存優化,所以當哈希表的字段數量較小,并且字段值的大小也較小時,使用哈希表可以節省內存。
3、 鍵名和鍵值壓縮: 如果您的鍵名或鍵值非常大,那么壓縮它們可能會節省大量內存。例如,你可以選擇更短的鍵名,或者將長的文本值壓縮為二進制數據。
4、 使用 EXPIRE 和 TTL: 對于不需要永久存儲的數據,可以使用 Redis 的 EXPIRE 命令為其設置一個過期時間。當數據過期后,Redis 會自動刪除它,從而釋放內存。此外,可以使用 TTL(Time-To-Live)命令檢查鍵的剩余生存時間。
5、 使用 LRU 緩存策略: 如果 Redis 實例內存已滿,你可以配置 Redis 以便在需要空間時刪除最近最少使用(LRU)的鍵。Redis 提供了幾種 LRU 緩存策略,包括:volatile-lru、allkeys-lru、volatile-random、allkeys-random、volatile-ttl 等。
6、 使用 Redis 的內存分析工具: Redis 提供了一些內存分析工具,如 MEMORY USAGE 命令,它可以幫助你了解每個鍵使用了多少內存。此外,還有 MEMORY DOCTOR 和 MEMORY STATS 等工具可以提供更詳細的內存使用信息。
7、 垂直擴展: 如果可能的話,增加服務器的 RAM 也是一個直接而有效的辦法。
以上所述的優化方法都有各自的適用場景,需要根據實際的業務需求和數據特性來選擇最適合的優化策略。
Redis事務的概念
Redis 的事務能讓你執行一組命令(一個事務),這組命令要么全部執行,要么都不執行,這種特性叫做原子性。
Redis 的事務提供了一種將多個命令打包然后按順序執行的方式,不會被其他客戶端發送的命令所中斷。這意味著它們提供了一種預防命令間互相干擾的機制。
以下是 Redis 事務的一些基本特性:
1、 批量操作: 你可以在一個事務中執行多個操作,所有的操作會一起被發送到 Redis 服務器并順序執行。
2、 原子性: Redis 事務是原子的,這意味著事務中的所有命令要么全部執行,要么都不執行。
3、 無隔離性: Redis 的事務不支持隔離性,這意味著在事務執行過程中,其他客戶端發送的命令可能會在事務中間的任何時刻被執行。
4、 無回滾: 如果事務中的某個命令失敗,那么該命令之后的所有命令仍會被執行。Redis 不支持事務回滾,所以開發者需要自己處理事務失敗的情況。
5、 樂觀鎖: Redis 提供了一個名為 WATCH 的命令,用于實現一種樂觀鎖機制。你可以通過 WATCH 命令監控一個或多個鍵,如果在事務執行之前這些鍵的值被其他客戶端改變,那么事務將被中斷。
要開始一個事務,你可以使用 MULTI 命令,然后輸入你想在事務中執行的所有命令,最后使用 EXEC 命令提交事務。如果你在事務中使用了 WATCH 命令,并且被監控的鍵的值被改變了,那么你需要使用 UNWATCH 命令取消監控,或者使用 DISCARD 命令取消事務。
這是一個簡單的 Redis 事務示例:
MULTI
SET book-name "Redis"
SET book-price "49.99"
EXEC
上述代碼會開始一個事務,然后在事務中執行兩個 SET 命令,最后提交事務。這兩個 SET 命令會被一起發送到 Redis 服務器并順序執行。
Redis事務MULTI、EXEC、DISCARD和WATCH
Redis 的事務功能是通過 MULTI、EXEC、DISCARD 和 WATCH 這四個命令來實現的。這些命令的功能如下:
1、 MULTI: 這個命令用于開始一個新的事務。一旦執行了 MULTI 命令,客戶端可以繼續向服務器發送任意多個命令,這些命令不會立即被執行,而是被放入一個隊列中。
2、 EXEC: 這個命令用于提交事務,也就是執行事務隊列中的所有命令。一旦 EXEC 命令被調用,所有隊列中的命令都會被順序執行。
3、 DISCARD: 這個命令用于取消事務,也就是清空事務隊列中的所有命令。如果在 MULTI 和 EXEC 之間執行了 DISCARD 命令,那么隊列中的命令都不會被執行。
4、 WATCH: 這個命令用于監控一個或多個鍵,如果在 EXEC 命令執行之前這些鍵的值被其他命令改變了,那么事務將被中斷。這種機制被稱為樂觀鎖。
下面是一個包含這四個命令的 Redis 事務示例:
WATCH book
MULTI
SET book-name "Redis"
SET book-price "49.99"
EXEC
在這個示例中,WATCH 命令用于監控名為 "book" 的鍵。如果這個鍵的值在事務執行之前被改變了,那么 EXEC 命令將不會執行,事務將被中斷。
什么是 RedLock
RedLock 是一種分布式鎖的實現算法,它是由 Redis 的創造者 Salvatore Sanfilippo 提出的。這種算法可以在沒有中心實例的情況下在不同的 Redis 實例之間實現一個分布式鎖。
基本思想是這樣的:假設你有 N 個 Redis 實例,你想要獲取一個鎖,那么你需要在超過 N/2 的實例上都獲取到鎖,鎖的有效期都設為鎖的租約時間。這樣,只有當你能夠在大多數實例上都獲取到鎖的時候,才算真正獲取到了鎖。
這種方式可以避免單點故障,并且在一定程度上保證了鎖的安全性,因為即使有少數實例出現了問題,只要大多數實例正常,就能保證鎖的正確性。
RedLock 算法的基本步驟如下:
1、 獲取當前時間,單位是毫秒。 2、 嘗試在所有的 Redis 實例上獲取鎖。在嘗試獲取鎖的時候,需要設置鎖的有效期。獲取鎖的操作需要使用 Redis 的 SET 命令,并且要帶有 NX 和 PX 選項,這樣可以保證只有當鎖不存在的時候才會設置成功。 3、 如果在超過 N/2 的實例上都獲取到了鎖,并且獲取鎖的總時間小于鎖的有效期,那么算是成功獲取到了鎖。 4、 如果獲取鎖失敗,那么需要在所有的 Redis 實例上釋放鎖,然后從步驟 1 重新嘗試獲取鎖。 5、 如果獲取鎖成功,那么鎖的真正有效期應該是原來設置的有效期減去獲取鎖的時間。
請注意,RedLock 算法并不是完全無法出錯。例如,由于網絡分區等問題,可能會導致同時存在多個有效的鎖。因此,盡管 RedLock 可以提供一種分布式鎖的解決方案,但在使用的時候還是需要根據實際情況謹慎考慮。
為什么要做Redis分區?
Redis 分區是一種將數據分布在多個 Redis 實例上的方法,可以有助于解決單個 Redis 實例的數據存儲和計算能力限制。以下是進行 Redis 分區的主要原因:
1、 擴大存儲容量: 單個 Redis 實例的存儲容量受限于單臺服務器的內存大小。通過分區,你可以將數據分布在多個 Redis 實例上,從而擴大整體的存儲容量。
2、 提高性能: Redis 的運算操作都是在單線程中進行的,如果所有操作都在一個實例上執行,可能會出現性能瓶頸。通過將數據和操作分布在多個 Redis 實例上,可以利用多臺服務器的計算能力,從而提高整體的性能。
3、 提高可用性: 如果所有數據都存儲在單個 Redis 實例上,那么這個實例如果出現故障,就會導致所有數據都無法訪問。通過分區,即使有一部分實例出現故障,也只會影響到部分數據,其他實例上的數據仍然可以正常訪問。
Redis 分區有多種實現方式,包括范圍分區(range partitioning)、哈希分區(hash partitioning)以及一致性哈希分區(consistent hashing partitioning)等。每種方式都有其優點和適用場景,需要根據具體需求選擇合適的方式進行分區。
Redis分區有什么缺點?
盡管 Redis 分區可以提高存儲容量和性能,但也有一些潛在的缺點:
1、 復雜性增加: 實現和維護分區需要額外的工作。你需要設計一個適合數據和查詢模式的分區策略,并且在數據增長或查詢模式改變時可能需要重新調整分區策略。
2、 不支持多鍵操作: 在 Redis 中,很多操作是基于多個鍵的,例如交集、并集等操作。如果這些鍵被分配到了不同的分區(Redis 實例)上,那么這些操作就無法直接執行了。你需要在客戶端進行額外的操作來合并結果,這會增加客戶端的復雜性和計算負擔。
3、 不支持數據遷移: 如果你需要增加或減少分區的數量,那么可能需要對數據進行重新分布。這通常需要停止服務,或者在數據遷移過程中處理復雜的數據一致性問題。
4、 不均衡的數據和負載分布: 如果分區策略不合理,或者數據的訪問模式有傾斜,那么可能會出現數據和負載分布不均衡的問題。這可能導致某些 Redis 實例的負載過高,而其他實例則空閑。
5、 故障恢復復雜性: 如果一個 Redis 實例出現故障,那么需要恢復的數據量可能會非常大,這會使得故障恢復時間變長。
所以,在決定是否使用 Redis 分區時,需要仔細考慮這些潛在的缺點,并根據應用的需求和限制進行權衡。
Redis分區和Redis Cluster有啥區別
Redis 分區和 Redis Cluster 都是為了解決單個 Redis 實例無法滿足大規模數據處理需求的問題。雖然它們的目標相同,但是實現方式和特性有所不同:
Redis 分區
- Redis 分區是一種將數據分布在多個 Redis 實例上的方法,可以手動實現或者使用客戶端庫(如:Twemproxy)來自動進行。
- 分區的策略(如鍵的哈希分區、范圍分區等)需要開發者自行設計和實現。
- 如果一臺機器宕機,那么這臺機器上的所有數據將不可用。恢復這部分數據需要額外的故障恢復機制。
- 對于跨節點的多鍵操作和事務支持較差。
Redis Cluster
- Redis Cluster 是 Redis 官方支持的分布式解決方案,無需額外的代理和工具,只需要配置好 Redis Cluster 就可以使用。
- Redis Cluster 自動進行數據分片,并且每個節點都存儲部分數據和整個集群的狀態信息,節點之間使用 gossip 協議進行信息交換。
- Redis Cluster 支持主從復制和故障轉移,一臺機器宕機后,其上的數據可以由其它機器接管。
- 支持跨節點的多鍵操作,但是這些鍵必須在同一臺機器上。
總結一下,Redis 分區是一種較為簡單和靈活的方式,可以根據具體需求選擇不同的分區策略,但是需要自行處理很多問題,如數據一致性、故障恢復等。而 Redis Cluster 是一種更為復雜但功能更全的方式,它提供了自動的數據分片、故障恢復等功能,但是配置和管理相對復雜一些。
Redis與Memcached的區別
Redis 和 Memcached 都是流行的開源內存數據存儲系統,主要用作緩存和會話存儲。雖然它們有一些共同點,但也有許多重要的區別。
1、 數據類型:
- Memcached 支持的數據類型相對簡單,主要是字符串。
- Redis 支持更豐富的數據類型,包括字符串、列表、集合、哈希表、有序集合等,這使得 Redis 可以用于更多的場景。
2、 持久化:
- Memcached 不支持持久化,數據只存儲在內存中,服務器重啟后數據會丟失。
- Redis 支持多種持久化策略,如快照(RDB)和日志(AOF),可以在需要的時候將數據持久化到磁盤,增強了數據的安全性。
3、 數據一致性:
- Memcached 不支持數據一致性保證,比如事務操作。
- Redis 支持事務,可以執行一組命令,確保這組命令在同一時間內執行,提供了一定程度的數據一致性。
4、 分布式支持:
- Memcached 不直接支持分布式,需要客戶端來實現數據分布。
- Redis 提供了 Redis Cluster,支持自動分片,可以將數據分布在多個節點上。
5、 性能:
- Memcached 和 Redis 的性能都非常高,但由于 Redis 的單線程模型,對于純粹的獲取和設置操作,Memcached 可能會有更好的性能。
- 但是,如果你需要執行復雜的操作,比如操作列表或集合,那么 Redis 的性能可能會更好。
6、 使用場景:
- Memcached 適合做簡單的 K-V 緩存。
- 由于 Redis 提供的功能更多,所以使用場景也更廣泛,除了緩存之外,還可以用于消息隊列、發布/訂閱系統、排行榜等。
根據應用的具體需求,你可能會選擇使用 Redis 或 Memcached,或者兩者都使用。
Redis回收進程如何工作的?使用的是什么算法?
Redis 使用的是一種名為 "LRU"(Least Recently Used,最近最少使用)的回收策略。當內存達到上限時,Redis 會根據這個策略刪除一些鍵,以釋放內存。
LRU 算法的基本思想是:當內存不足時,應該優先刪除最近最少使用的數據。這是基于一個假設,那就是如果一個數據最近被訪問過,那么在將來它可能還會被再次訪問。
但是,完全的 LRU 需要為每個鍵都保存一個時間戳,這會占用大量的內存。因此,Redis 實現了一種名為 "approximated LRU"(近似 LRU)的策略。這種策略不會為每個鍵都保存一個時間戳,而是只保存一部分鍵的時間戳,并且使用一種特殊的抽樣算法來決定哪個鍵應該被刪除。
在這個抽樣算法中,Redis 會隨機選擇一組鍵,然后從中選擇最近最少使用的鍵進行刪除。這種方法不如完全的 LRU 精確,但是它的內存開銷更小,而且在大多數情況下,它的效果與完全的 LRU 相近。
你可以通過 maxmemory-policy 配置項來設置 Redis 的回收策略,除了 LRU 外,Redis 還支持其他幾種回收策略,如 volatile-ttl(優先刪除生存時間(TTL)短的鍵)、noeviction(不刪除任何鍵,只返回錯誤)等。
為什么 Redis 6.0 之后改多線程呢?
Redis是一款高性能的鍵值對存儲系統,長期以來它都是單線程模型。在6.0版本之前,Redis的設計者Antirez(Salvatore Sanfilippo)一直堅持Redis的單線程設計,這是因為單線程模型更簡單,避免了多線程編程中的復雜問題如并發控制和同步等。此外,由于Redis主要在內存中操作,而CPU通常不是Redis的性能瓶頸,所以單線程也能滿足絕大部分場景的需求。
然而,隨著硬件性能的提升,多核CPU已經非常普遍,而單線程無法充分利用多核CPU的優勢。尤其是在處理大量網絡IO請求時,單線程可能會成為性能瓶頸。
Redis 6.0版本引入了多線程模型,這是為了更好地利用現代多核CPU的計算能力,從而提高Redis的性能。具體來說,Redis 6.0版本中的多線程主要用于處理客戶端的網絡請求,例如讀取請求、寫入響應等,而數據處理仍然是單線程的,這樣在保持核心操作簡單性的同時,又能充分利用多核CPU的優勢,提升性能。
需要注意的是,即使在Redis 6.0之后,多線程并不是默認啟用的,需要在配置文件中進行設置。此外,由于數據處理仍然是單線程的,所以并不是所有情況下啟用多線程都能帶來性能提升,具體還需要根據實際的業務場景進行判斷。
Redis 的 Hash 沖突怎么辦
Redis 使用哈希表作為其底層數據結構之一,哈希表中的沖突是無法避免的。當兩個或更多的鍵在進行哈希運算后產生相同的哈希值,這就稱為哈希沖突。在Redis中,解決哈希沖突的方法主要是使用鏈地址法(Separate Chaining)。
鏈地址法的思想是將所有哈希到同一位置的鍵存放到一個鏈表中。在哈希表中,每一個桶(Bucket)不再直接存儲數據,而是存儲一個鏈表的頭節點,所有哈希到這個桶的鍵都會被存放到這個鏈表中。
具體操作如下:
1、 當插入一個新的鍵值對時,Redis會首先計算鍵的哈希值,然后根據哈希值找到對應的桶,將新的鍵值對作為一個節點添加到桶對應的鏈表中。
2、 當查找一個鍵時,Redis同樣會計算這個鍵的哈希值,然后找到對應的桶,然后在桶對應的鏈表中進行線性查找。
3、 當刪除一個鍵時,操作同查找。
這種方法可以很好地解決哈希沖突的問題,但是如果鏈表過長,查找效率會降低。因此,Redis還會定期進行哈希表的rehash操作,即動態調整哈希表的大小,使得桶的數量保持在鍵的數量的一定范圍內,從而保證查找效率。
除了鏈地址法,還有一種開放定址法(Open Addressing)來解決哈希沖突,但Redis并未采用這種方式。開放定址法是在哈希表內部尋找空的位置存儲沖突的鍵,但這種方法可能會導致查找和刪除操作的效率降低,尤其是在哈希表填充度較高時。
在生成 RDB 期間,Redis 可以同時處理寫請求么?
在Redis中,RDB持久化是通過創建一個子進程,將當前進程的數據復制到子進程,然后由子進程將數據寫入磁盤來實現的。這種方式的優點是可以減少對主進程的IO操作,從而避免阻塞主進程的讀寫請求。
但是,由于Redis是單線程的,所以在創建子進程(也就是進行fork操作)的過程中,Redis必須暫停處理其他請求,因為fork操作需要復制整個進程的內存空間,這可能會消耗比較大的CPU和內存資源。如果數據集很大,這個操作可能會耗費較長的時間,從而導致Redis在這段時間內無法處理其他請求。
另外,即使在子進程寫RDB文件期間,主進程可以繼續處理讀寫請求,但是,對于寫請求來說,如果在子進程生成RDB文件期間,主進程中的數據發生了修改,那么這些修改是不會反映到RDB文件中的。這是因為RDB持久化是通過快照(Snapshot)來實現的,也就是說,它只會保存生成RDB文件時刻的數據狀態。
因此,雖然Redis在生成RDB文件期間可以處理寫請求,但這些寫請求可能不會被持久化。如果在生成RDB文件期間Redis崩潰,那么最新的修改可能會丟失。所以,雖然RDB持久化可以提供一定程度的數據安全保障,但如果需要更高程度的數據安全保障,可能需要結合其他的持久化策略,例如AOF(Append Only File)持久化。
Redis 底層,使用的什么協議?
是的,Redis 底層使用的是 RESP (Redis Serialization Protocol) 協議,這是一種簡單的文本協議。
RESP 協議設計的非常簡單,它的主要特點有:
1、 易于實現:RESP 協議非常簡單,這使得它很容易在任何編程語言中實現。
2、 人類可讀:盡管 RESP 協議設計為計算機之間的通信協議,但它仍然是人類可讀的,這使得調試變得更加容易。
3、 客戶端和服務器可以在不關閉連接的情況下發送任意數量的命令和獲取任意數量的回復,這使得 pipelining(管道化)和多路復用變得容易實現。
4、 RESP 協議能夠表示簡單的字符串、錯誤、整數、數組等數據類型。
一個簡單的 RESP 協議的例子如下:
請求: "GET key1"
RESP 形式: "*2rn$3rnGETrn$4rnkey1rn"
這里 "*" 表示數組,"2" 表示數組中的元素數量,"$" 表示字節字符串,"3" 和 "4" 分別表示接下來的字節字符串的長度。"rn" 是分隔符。
回復: "OK"
RESP 形式: "+OKrn"
"+" 表示簡單字符串,"OK" 是字符串的內容。
雖然 RESP 協議很簡單,但它完全滿足了 Redis 的需求,并且在實踐中表現得非常出色。
Redis 的跳躍表
跳躍表(Skip List)是一種數據結構,是為了克服有序鏈表查找效率低的問題而產生的。跳躍表對標的是平衡樹和二分查找,跳躍表中存儲的是有序的數據,并且跳躍表的查找、插入、刪除的時間復雜度都是O(log n)。
跳躍表的基本思想是這樣的:它是一個多層的鏈表,底層是原始鏈表,每一層都是下一層的一個子集,最高層的鏈表中只有兩個節點:正無窮和負無窮。在查找一個元素的時候,從最高層開始查找,直到找到一個區間,使得待查找元素在這個區間中,然后進入下一層繼續查找,直到找到元素或者查找失敗。
Redis中的跳躍表是一種改良版的跳躍表,它添加了一些額外的特性來滿足Redis的需求:
1、 每個節點包含了一個后退指針,使得跳躍表的遍歷可以是雙向的,即可以從左到右,也可以從右到左。
2、 在每個節點中,Redis存儲了該節點在原始鏈表中的排名,這樣可以快速計算出任意元素的排名。
3、 在每個節點中,Redis還存儲了該節點的高度,即包含該節點的層數。
4、 在Redis中,跳躍表的層數是1到32之間的一個隨機數,這個隨機數是按照冪次定律分布的,也就是說,高層的節點會越來越少。
在Redis中,跳躍表被用作有序集合(sorted set)的一種實現方式。在有序集合中,每一個元素既有一個值,又有一個分數,元素之間是按照分數進行排序的。在實現有序集合時,Redis會將元素的值存儲在跳躍表的節點中,將元素的分數用作節點的排序依據。
總的來說,跳躍表是一個非常高效的數據結構,它在保持數據有序的同時,又能提供高效的查找、插入、刪除操作,因此非常適合用于實現有序集合這樣的數據結構。
為什么 Redis Cluster 的 Hash Slot 是 16384?
Redis Cluster 使用了一種叫做哈希槽(Hash Slot)的技術來分配鍵到不同的節點。總共有 16384 個哈希槽,當需要放置一個鍵值對的時候,Redis 首先會計算鍵的 CRC16 值,然后對 16384 求余數,得到的結果就是應該放置鍵值對的哈希槽的編號。
那么,為什么選擇 16384 作為哈希槽的數量呢?
1、 平衡分布和管理復雜性:如果哈希槽的數量太小,那么數據在各個節點之間的分布可能不夠均勻,導致某些節點上的數據過多,某些節點上的數據過少。如果哈希槽的數量太大,那么管理哈希槽的復雜性就會增加,例如在添加、刪除節點,或者在進行故障恢復時,需要移動的哈希槽的數量就會增加。16384 是一個折中的選擇,既可以保證數據的均勻分布,又不會讓管理變得過于復雜。
2、 性能考慮:CRC16 算法可以產生 0 到 65535 之間的值,而 16384 正好是 65536(即2的16次方)的四分之一,這樣在計算哈希槽編號時,只需要進行一次快速的模運算就可以了。
3、 經驗選擇:實際上,這個值也是根據 Redis 的作者的經驗選擇的。在 Redis 的開發過程中,他可能試驗了不同的哈希槽數量,然后發現 16384 是一個在分布、性能和管理復雜性之間取得平衡的值。
所以,16384 是 Redis Cluster 中哈希槽數量的選擇,旨在實現在不同節點間的數據均勻分布,提升性能,以及易于管理。
集群搭建
首先,確保每臺服務器都已經安裝了Redis。如果沒有,可以按照以下步驟在Ubuntu系統中安裝Redis:
sudo apt update
sudo apt install redis-server
Redis Cluster是一個Redis的分布式解決方案,它包括了數據的分片、自動故障轉移等功能。
以下是搭建Redis Cluster的步驟:
步驟1: 配置Redis
在每臺服務器上,需要為Redis創建一個配置文件。例如,創建一個名為redis.conf的文件,并在文件中添加以下內容:
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
這里的cluster-enabled yes開啟集群模式,cluster-config-file nodes.conf是指定一個保存節點狀態的文件,Redis會自動維護這個文件,cluster-node-timeout 5000配置節點的超時時間,appendonly yes是開啟AOF持久化。
步驟2: 啟動Redis
使用配置文件啟動Redis。在每臺服務器上執行以下命令:
redis-server ./redis.conf
步驟3: 創建集群
Redis自帶一個叫做redis-cli的命令行工具,可以使用這個工具創建集群。
首先,需要登錄到其中一臺服務器上,然后執行以下命令:
redis-cli --cluster create 192.168.1.1:6379 192.168.1.2:6379 192.168.1.3:6379
這里的IP地址和端口號應該和Redis服務器匹配。這個命令會提示你是否接受它的分片方案,輸入yes接受即可。
現在Redis Cluster應該已經搭建好了。可以通過以下命令檢查集群狀態:
redis-cli --cluster check 192.168.1.1:6379
這應該會顯示出集群的信息,包括節點的狀態、分片的情況等。
這只是一個基本的設置步驟,你可能需要根據實際情況做出調整。例如,你可能需要配置防火墻以允許Redis的通信,或者在生產環境中你可能需要為Redis設置密碼等。
新加入節點
要將新的Redis節點(例如192.168.1.4)添加到現有的Redis集群中,你可以按照以下步驟進行:
步驟1: 安裝并配置Redis
在新的服務器上安裝Redis并配置。你可以參考我之前給出的安裝和配置Redis的步驟。
步驟2: 啟動Redis
在新的服務器上使用配置文件啟動Redis:
redis-server ./redis.conf
步驟3: 添加節點到集群
使用redis-cli工具將新的節點添加到集群。你需要首先登錄到現有集群的任何一個節點,然后執行以下命令:
redis-cli --cluster add-node 192.168.1.4:6379 192.168.1.1:6379
這個命令將新的節點(192.168.1.4)添加到集群中。這里的192.168.1.1應該是你現有集群中的一個節點。
步驟4: 重新分配哈希槽
添加新的節點之后,你需要重新分配哈希槽。你可以使用redis-cli的--cluster reshard命令來實現。下面的命令將會把100個哈希槽從192.168.1.1遷移到新的節點192.168.1.4:
redis-cli --cluster reshard 192.168.1.1:6379 --cluster-node-id [node-id-of-192.168.1.4] --cluster-yes --timeout 5000 --cluster-from [node-id-of-192.168.1.1] --cluster-slots 100
在執行這個命令時,你需要替換[node-id-of-192.168.1.4]和[node-id-of-192.168.1.1]為對應節點的實際ID。你可以通過以下命令獲取節點的ID:
redis-cli -h 192.168.1.1 -p 6379 cluster nodes
這個命令將顯示集群中所有節點的信息,包括節點的ID。
現在,新的節點應該已經成功加入到集群中,并且哈希槽也已經被重新分配。你可以通過以下命令檢查集群的狀態:
redis-cli --cluster check 192.168.1.1:6379
這應該會顯示出集群的信息,包括節點的狀態、分片的情況等。
同樣地,這只是一個基本的設置步驟,你可能需要根據實際情況做出調整。例如,你可能需要配置防火墻以允許Redis的通信,或者在生產環境中你可能需要為Redis設置密碼等。
Redis集群中,客戶端給一個 Redis 實例發送數據讀寫操作時,如果這個實例上并沒有相應的數據,會怎么樣呢?
在 Redis 集群中,客戶端如果給一個實例發送數據讀寫操作,但是這個實例并沒有相應的數據,那么這個實例會返回一個特殊的錯誤,告訴客戶端數據應該去哪個實例進行操作。這個特殊的錯誤有兩種,一種叫做 MOVED 重定向,一種叫做 ASK 重定向。
1、 MOVED 重定向:MOVED 是 Redis 集群用來處理數據遷移的一種方式。當一個 Redis 實例收到一個它無法處理的請求時(因為數據并不在這個節點上),它會返回 MOVED 錯誤,告訴客戶端正確的節點地址。MOVED 重定向是完全確定的,意味著客戶端在收到 MOVED 錯誤后,應該將這個鍵對應的槽的映射關系更改為 MOVED 提示的地址,之后對這個鍵的所有請求都應該發送到新的地址。

2、 ASK 重定向:ASK 也是 Redis 集群的一種重定向方式,但它只是暫時的。在數據遷移過程中,如果一個請求命中了正在遷移的鍵,即使這個鍵暫時還在舊的節點上,新的節點也會返回 ASK 錯誤,讓客戶端臨時性地將請求重定向到新的節點。區別于 MOVED,ASK 只影響這一次的請求,客戶端不需要更改槽的映射關系。

對于 MOVED 和 ASK 重定向,客戶端通常會根據返回的重定向地址,重新發送請求到正確的節點上。而對于一些智能的客戶端,它們可能會記住這個重定向,對于以后相同的請求,直接發送到正確的節點上,從而減少重定向帶來的開銷。
在 Redis cluster 模式下,節點對請求的處理過程
Redis Cluster 模式下,節點對請求的處理過程:
1、 客戶端請求攜帶的 Redis key 通過哈希函數計算得到一個哈希值,根據這個哈希值映射到特定的哈希槽中,檢查當前節點是否負責此哈希槽。
2、 如果此哈希槽不是由當前節點負責,該節點會返回 MOVED 錯誤,告訴客戶端應該重定向到哪個節點(給出 IP 地址和端口號)。
3、 如果哈希槽確實由當前節點負責,那么節點會檢查 key 是否存在。如果 key 存在,則返回該 key 對應的結果。
4、 如果 key 不存在,此時需要檢查當前哈希槽是否處于遷移狀態。在 Redis Cluster 中,為了平衡負載,可能會將一些哈希槽從一個節點遷移到另一個節點。
5、 如果哈希槽正在被遷出(MIGRATING)到另一個節點,且客戶端請求的 key 正在這個哈希槽中,那么節點會返回 ASK 錯誤,告訴客戶端應該“臨時”重定向到目標節點。
6、 如果哈希槽沒有正在被遷出,那么需要檢查哈希槽是否正在被導入(IMPORTING)。
7、 如果哈希槽正在被導入,且客戶端請求的 key 存在于這個哈希槽中,那么在客戶端發送 ASKING 命令后,節點會直接處理這個請求。否則,節點將返回 MOVED 錯誤,告訴客戶端應該重定向到哪個節點。
這個過程是為了確保在集群調整(比如重分布哈希槽)的過程中,客戶端的請求可以被正確地路由到正確的節點。同時,MOVED 和 ASK 錯誤也幫助客戶端更新它們對集群節點和哈希槽責任分布的理解。
Redis 集群實現高可用性的具體方式
Redis 集群是一種實現分布式數據庫的方式,它通過將數據存儲在多個節點上,可以大大提高數據庫的吞吐量和可用性。具體來說,Redis 集群通過實現故障轉移和分片等機制,提高了數據庫的高可用性和擴展性。
以下是 Redis 集群實現高可用性的具體方式:
1、 故障轉移:當 Redis 集群中的一個主節點出現故障時,集群會自動將這個節點的從節點提升為新的主節點,以保證服務的正常進行。這個過程被稱為故障轉移。
2、 主觀下線和客觀下線:這是 Redis 集群故障檢測的兩個重要環節。當一個節點認為另一個節點不可達時,它會主觀地將這個節點標記為下線。如果集群中超過半數的節點都將一個節點標記為下線,那么這個節點就會被客觀地視為下線,并觸發故障轉移。
3、 Ping/Pong 消息:在 Redis 集群中,節點會定期向其他節點發送 Ping 消息,如果在一定時間內沒有收到回應,就會認為這個節點已經下線。這是 Redis 集群發現節點故障的一種方式。
4、 數據分片:Redis 集群通過將數據分布在多個節點上,可以提高數據庫的容量和吞吐量。如果一個節點出現故障,只會影響到存儲在該節點上的一部分數據,而其他節點可以繼續提供服務。
通過以上機制,Redis 集群可以在節點出現故障時,快速進行故障轉移,保證集群對外的服務不受影響。同時,通過數據分片,也可以提高集群的擴展性和性能。
主觀下線和客觀下線
Redis 集群中的主觀下線(Subjective Down)和客觀下線(Objective Down)是集群中用于節點故障檢測和處理的兩個重要概念。
主觀下線(Subjective Down):
主觀下線是某個節點對于其他節點的個體觀察和判斷。當一個節點 A 發現另一個節點 B 無法響應其消息時,節點 A 就會主觀地認為節點 B 下線了。這種判斷是基于節點 A 的視角,可能由于網絡延遲或者短暫的網絡問題導致,因此并不一定代表節點 B 真的出現了故障。
客觀下線(Objective Down):
當一個節點被主觀判斷為下線后,這個信息會被傳播到集群中的其他節點。當超過半數的節點也認為節點 B 下線時,節點 B 就被客觀地認為已經下線。這種判斷是基于集群的整體視角,而不只是單個節點的觀察。一旦一個節點被客觀地判斷為下線,集群就會開始進行故障恢復的操作,例如觸發故障轉移。
這兩種下線狀態可以幫助 Redis 集群更準確地識別和處理節點故障。主觀下線可以快速發現可能的節點問題,而客觀下線則可以防止因為個別節點的錯誤判斷而對整個集群產生影響。只有當一個節點被集群中的大部分節點認為已經下線,才會觸發故障恢復的操作,從而避免不必要的故障轉移。