redis原理及應用
redis原理及應用
一、redis來源
二、資料型別
三、主流的應用場景
四、特性
五、補充
一、 redis來源
作者:Salvatore Sanfilippo (antirez),男,義大利人.
需求: 一個訪客資訊追蹤網站,網站可以通 過 JavaScript 指令碼,將訪客的 IP 地 址、所屬國家、閱覽器資訊、被訪問頁 面的地址等資料傳送給 LLOOGG. com 。 然後 LLOOGG.com 會將這些瀏覽數 據通過 web 頁面實時地展示給使用者, 並儲存起最新的 5 至 10,000 條瀏覽 記錄以便進行查閱。
redis解決方案
每當某個被追蹤的網站新增一條 瀏覽記錄時, LLOOGG.com 就會將這條新的瀏覽記錄推入 (push)到與該網站相對應的列表裡面,當列表的 長度超過使用者指定的最大長度時,程式每向 列表推入一條新的記錄,就需要從列表中彈出(pop)一條最舊的記錄。
現在已經被廣泛使用:
Twitter 使用 Redis 來儲存使用者時間線(user timeline)。
StackOverflow 使用 Redis 來進行快取和訊息分發。
Pinterest 使用 Redis 來構建關注模型(follow model)和興趣圖譜(interest graph)。
Flickr 使用 Redis 來構建佇列。
Github 使用 Redis 作為持久化的鍵值對資料庫,並使用 Resque 來實現訊息佇列。
新浪微博使用 Redis 來實現計數器、反向索引、排行榜、訊息 佇列,並儲存使用者關係。
知乎使用 Redis 來進行計數、快取、訊息分發和任務排程。
掌上醫訊使用redis來進行快取,分散式鎖等。
阿里雲、百度雲、Amazon、 RedisLab 等公司都提供了基於 Redis 的應用服務。
二、支援的資料型別
Redis 所有的資料結構都是以唯一的 key 字串作為名稱,然後通過這個唯一 key 值來獲取相應的 value 資料。不同型別的資料結構的差異就在於 value 的結構不一樣。
a.字串
常用命令 SET key value [NX|XX] 儲存值
SETNX key value 命令僅在鍵 key 不存在的情況下,才進行設定操作
SETXX key value 命令僅在鍵 key 存在的情況下,才進行設定操作
APPEND key value
STRLEN key
get key
SETRANGE key index value 根據索引設定值
GETRANGE key start end
字串結構使用非常廣泛,一個常見的用途就是快取使用者資訊。我們將使用者資訊結構體使用 JSON 序列化成字串,然後將序列化後的 字串塞進 Redis 來快取。同樣,取使用者資訊會經過一次反序列化的過程。
struct SDS<T> {
T capacity; // 陣列容量
T len; // 陣列長度
byte flags; // 特殊標識位,
byte[] content; // 陣列內內容
}
Redis 的字串是動態字串,是可以修改的字串,內部結構實現上類似於 Java 的 ArrayList,採用預分配冗餘空間的方式來減少記憶體的頻繁分配,如圖中所示,內部為當前字串實際分配的空間 capacity 一般要高於實際字串長度 len。當字串長度小於 1M 時,擴容都是加倍現有的空間,如果超過 1M,擴容時一次只會多擴 1M 的空間。需要注意的是字串最大長度為 512M。
儲存二進位制資料相關的命令
SETBIT key index value
GETBIT key index
BITCOUNT key [start] [end]
b 列表
Redis 的列表相當於 Java 語言裡面的 LinkedList,注意它是連結串列而不是陣列。這意味著 list 的插入和刪除操作非常快
常用的命令有 LPUSH key value [value ...]
LPOP key
RPUSH key value [value ...]
RPOP key
LLEN key
LINDEX key index
LRANGE key start stop
LSET key index value
LREM key count value
LTRIM key start stop
阻塞彈出
BLPOP key [key ...] timeout
Redis 的列表結構常用來做非同步佇列使用。將需要延後處理的任務結構體序列化成字串塞進 Redis 的列表,另一個執行緒從這個列表中輪詢資料進行處理
hash 字典
Redis 的字典相當於 Java 語言裡面的 HashMap,它是無序字典。內部實現結構上同 Java 的 HashMap 也是一致的
常用指令:HSET key field value
HGET key field
HEXISTS key field
HDEL key field [field ...]
HLEN key
hash 結構也可以用來儲存使用者資訊,不同於字串一次性需要全部序列化整個物件,hash 可以對使用者結構中的每個欄位單獨儲存。這樣當我們需要獲取使用者資訊時可以進行部分獲取。而以整個字串的形式去儲存使用者資訊的話就只能一次性全部讀取,這樣就會比較浪費網路流量...
集合(set)
Redis 的集合相當於 Java 語言裡面的 HashSet,它內部的鍵值對是無序的唯一的。它的內部實現相當於一個特殊的字典,字典中所有的 value 都是一個值NULL
。
使用者可以速地向集合新增元素,或者從集合裡面 刪除元素,也可以對多個集合進行集合運算操作,比 如計算並集、交集和差集。
SDIFF key [key ...] 計算所有給定集合的差集,並返回結果。
SDIFF key [key ...] 計算所有給定集合的差集,,並將結果儲存到 destkey 。
SINTER key [key ...] 計算所有給定集合的交集,並返回結果
SINTERSTORE destkey key [key ...] 計算所有給定集合的交集,並將結果儲存到 destkey
SUNION key [key ...] 計算所有給定集合的並集,並返回結果
SUNIONSTORE destkey key [key ...] 計算所有給定集合的並集,並將結果儲存到 destkey
使用集合實現共同關注功能
使用集合可以實現商品篩選功能
有序集合
有序集合和集合一樣,都可以包含任意數量的、各不相同的元素( element),不同於集合的是,有序集 合的每個元素都關聯著一個浮點數格式的分 值(score),並且有序集合會按照分 值,以從小到大的順序 來排列有序集合中的各個元素。 雖然有序集合中的每個元素都必 須是各不相同的,但元素的分 值並沒有這一限制,換句話來說,兩個不 同元素的分值可以是相同的。
ZADD key score element [[score element] [score element] ...]
ZREM key element [element ...]
ZSCORE key element 返回分值
ZINCRBY key increment element 新增分值 ZINCRBY fruits-price 1.5 西瓜 3.5
ZCARD key 返回有序集合包含的元素數量
ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count] 獲取指定分值範圍內的升序元素
ZCOUNT key min max 計算給定分值範圍內的元素數量
zset 可以用來存粉絲列表,value 值是粉絲的使用者 ID,score 是關注時間。我們可以對粉絲列表按關注時間進行排序
ZUNIONSTORE destkey numkeys key [key ...] 計算多個集合的並集
eg:水果的銷售情況:
HyperLogLog
需求:記錄網站每天獲得的獨立 IP 數量
集合方式:
SADD '2018.9.10::unique::ip' ip 加入IP
SCARD '20148:9:10:unique::ip' 計算獨立IP數量
集合實現的問題
使用字串來儲存每個 IPv4 地址最多需要耗費 15 位元組(格式為 'XXX.XXX.XXX.XXX' ,比如 '202.189.128.186')。下表給給出了不同使用者量使用記憶體的數量
隨著集合記錄的 IP 越來越多,消耗的記憶體也會越來越多。 另外如果要儲存 IPv6 地址的話,需要的記憶體還會更多一些。為了解決此類問題,Redis 提供了 HyperLogLog 資料結構就是用來解決這種統計問題的。
HyperLogLog 提供了兩個指令 pfadd 和 pfcount
pfadd key value 增加物件
pfcount key 獲取計數
HyperLogLog 是不精確的去重計數方案,雖然不精確但是也不是非常不精確,標準誤差是 0.81%,重點是省空間,總共佔用的記憶體是12k.關於該演算法的介紹https://blog.csdn.net/firenet1/article/details/77247649
發明這個演算法的牛人,名字叫Philippe Flajolet 。
pfmerge 用於將多個 pf 計數值累加在一起形成一個新的 pf 值。
比如獲取兩個網站的合併起來的獨立ip。
主流的應用場景
1、快取
2、分散式鎖
3、 訊息列隊
4、點陣圖
在我們平時開發過程中,會有一些 bool 型資料需要存取,比如使用者一年的簽到記錄,簽了是 1,沒簽是 0,要記錄 365 天。如果使用普通的 key/value,每個使用者要記錄 365 個,當用戶上億的時候,需要的儲存空間是驚人的。 為了解決這個問題,Redis 提供了點陣圖資料結構,這樣每天的簽到記錄只佔據一個位,365 天就是 365 個位,46 個位元組 (一個稍長一點的字串) 就可以完全容納下,這就大大節約了儲存空間。
5、HyperLogLog
6、geoHash 附近的人
- geoadd 為成員增加某個地理位置的座標
- geodist 獲取兩個成員之間的地理位置的距離,可以設定距離單位 m:米 ,km:千米,ft:英尺 ,mi:英里
- geohash 獲取某個地理位置的hash值
- geopos 獲取某個成員的地理位置的座標
- georadius 根據給定地理位置座標獲取指定範圍內的地理位置集合
- WITHCOORD:傳入WITHCOORD引數,則返回結果會帶上匹配位置的經緯度。
- WITHDIST:傳入WITHDIST引數,則返回結果會帶上匹配位置與給定地理位置的距離。
- ASC|DESC:預設結果是未排序的,傳入ASC為從近到遠排序,傳入DESC為從遠到近排序。
- WITHHASH:傳入WITHHASH引數,則返回結果會帶上匹配位置的hash值。
- COUNT count:傳入COUNT引數,可以返回指定數量的結果。
- georadiusbymember 根據給定成員獲取指定範圍內的地理位置集合,相對於georadius命令,使用起來比較方便.引數基本一樣
7、排行榜
8、關注點贊
四、特性
1、快
2、支援多種資料型別
3、過期鍵功能
4、支援持久化
5、管道
6、支援主從模式
7、sentinel高可用
8、Cluster叢集
快!!!記憶體儲存,不受IO到硬碟IO速度限制 速度極快!
採用了非阻塞 I/O 多路複用機制 極大增加訪問速度。
過期鍵功能
設定生存時間 EXPIRE 命令和 PEXPIRE 命令。 SETEX 命令 PSETEX 命令
設定過期時間 EXPIREAT 命令和 PEXPIREAT 命令。
檢視剩餘生存時間 TTL 命令和 PTTL 命令。
刪除生存時間或過期時間 PERSIST 命令。
應用:1、自動更新的快取
2、自動重新整理的排行榜:在有序集合中,通過給日排行榜設定生存時間,我們可以 讓 Redis 在每個新的一天開始時,自動刪除舊的排行榜。
過期刪除機制:
redis 會將每個設定了過期時間的 key 放入到一個獨立的字典中,以後會定時遍歷這個字典來刪除到期的 key。除了定時遍歷之外,它還會使用惰性策略來刪除過期的 key,所謂惰性策略就是在客戶端訪問這個 key 的時候,redis 對 key 的過期時間進行檢查,如果過期了就立即刪除。定時刪除是集中處理,惰性刪除是零散處理。
定時掃描:
Redis 預設會每秒進行十次過期掃描,過期掃描不會遍歷過期字典中所有的 key,而是採用了一種簡單的貪心策略。
1、從過期字典中隨機 20 個 key;
2、 刪除這 20 個 key 中已經過期的 key;
3、 如果過期的 key 比率超過 1/4,那就重複步驟 1;
注意:如果有大批量的 key 過期,要給過期時間設定一個隨機範圍,而不能全部在同一時間過期。否則可能會系統資源不足,造成卡頓。
支援持久化
Redis 的資料全部在記憶體裡,如果突然宕機,資料就會全部丟失,因此必須有一種機制來保證 Redis 的資料不會因為故障而丟失,這種機制就是 Redis 的持久化機制。
如果我們僅僅是將 Redis 用作快取的話,那麼這種資料丟失帶來的問題並不是非常大,我們只需要重 啟機器,然後再次將資料放到 快取裡面就可以了;但如果我 們將 Redis 用作資料庫的話,那麼這種資料 丟失就不能接受了。
Redis 的持久化機制有兩種:
第一種是快照 RDB,全量備份 記錄資料
那麼 Redis 伺服器在什麼時候才會建立 RDB 檔案呢?
在 Redis 伺服器建立 RDB 檔案的情況中,以下三種是最常 見的:
1. 伺服器執行客戶端傳送的 SAVE 命令; 手動 阻塞 快
2. 伺服器執行客戶端傳送的 BGSAVE 命令;手動 不阻塞 慢
3. 使用 save 配置選項設定的自動儲存條件被滿足,伺服器自動執行 BGSAVE 。自動 不阻塞
BGSAVE 命令不會㐀成伺服器阻塞的原因在於:
1. 當 Redis 伺服器接收到 BGSAVE 命令的時候,它不會自己來建立 RDB 檔案,而是通過 fork() 來生 成一個子程序,然後由子程序負責建立 RDB 檔案,而自己則繼續處理客戶端的命令請求;
2. 當子程序建立好 RDB 檔案並退出時,它會向父程序(也即是負責處理命令請求的 Redis 伺服器)發 送一個訊號,告知它 RDB 檔案已經建立完畢;
3. 最後 Redis 伺服器(父程序)接收子程序建立的 RDB 檔案,BGSAVE 執行完畢。
自動BGSAVE的條件
save 900 1
save 300 10
save 60 10000
表示“如果距離上一次建立 RDB 檔案已經過去了 900 秒,並且伺服器的所有資料庫總共已經發生了 不少於 1 次修改,那麼執行 BGSAVE 命令”。
RDB持久化策略的不足:
因 為建立 RDB 檔案需要將伺服器所有資料庫的資料都儲存起來, 這是一個非常耗費資源和時間的操作,所以伺服器需要隔一段時間才建立一個新的 RDB 檔案,也即 是說,建立 RDB 檔案的操作不能執行得過於頻繁,否則就會嚴重地影響伺服器的效能。 比如說,在 save 配置選項的預設設定下,即使有超過 10000 次修改操作發生,伺服器也至少會間隔 一分鐘才建立下一個 RDB 檔案: save 900 1 save 300 10 save 60 10000 如果在等待建立下一個 RDB 檔案的過程中,伺服器遭遇了意外停機,那麼用 戶將丟失最後一次建立 RDB 檔案之後,資料庫發生的所有修改。
第二種是 AOF 日誌 增量備份 記錄指令
AOF 持久化有一個巨大的優勢,那就是,使用者可以根據自己的需要對 AOF 持 久化進行調整,讓 Redis 在遭遇意外停機時不丟失任何資料,或者只丟失一秒鐘資料,這比 RDB 持 久化遭遇意外停機時,丟失的資料要少得多。
配置 相關配置
appendonly yes no#開啟AOF模式
appendfilename "appendonly.aof" #儲存資料的AOF檔名稱
appendfsync always everysec no
原理
AOF 持久化儲存資料庫資料的方法是:每當有修改資料 庫的命令被執行時,伺服器就會將被執行的命 令寫入到 AOF 檔案的末尾。下次服務啟動時還原。
AOF重寫-----給檔案瘦身
AOF 重寫的觸發
有兩種方法可以觸發 AOF 重寫:
1. 客戶端向伺服器傳送 BGREWRITEAOF 命令。
2. 通過設定配置選項來讓伺服器自動執行 BGREWRITEAOF 命令,它們分別是:
• auto-aof-rewrite-min-size ,觸發 AOF 重寫所需的最小體積:只有在 AOF 檔案的體積 大於等於 size 時,伺服器才會考慮是否需要進行 AOF 重寫。這個選項用於避免對體積過小的 AOF 檔案進行重寫。
• auto-aof-rewrite-percentage ,指定觸發重寫所需的 AOF 檔案體積百分比:當 AOF 檔案的體積大於 auto-aof-rewrite-min-size 指定的體積,並且超過上一次重寫之後的 AOF 檔案 體積的 percent% 時,就會觸發 AOF 重寫。(如果伺服器剛剛啟動不久,還沒有進行過 AOF 重 寫,那麼使用伺服器啟動時載入的 AOF 檔案的體積來作為基準值。)將這個值設定為 0 表示關 閉自動 AOF 重寫。
管道
在一般情況下, 使用者每執行一個 Redis 命令,客戶端與伺服器都需要進行一次通訊:客戶端會將命令 請求傳送給伺服器,而伺服器則會將執行命令所得的結果返回給客戶端。 當程式執行一些複雜的操作時, 客戶端可能需要執行多個命令, 並與伺服器進行多次通訊。
Redis 的流水線功能允許客戶端一次將多個命令請求傳送給伺服器, 並將被執行的多個命令請求的結 果在一個命令回覆中全部返回 給客戶端, 使用這個功能可以有效地減少客 戶端在執行多個命令時需要 與伺服器進行通訊的次數。
支援主從模式
為了分擔讀壓力,Redis支援主從複製,Redis的主從結構可以採用一主多從或者級聯結構,Redis主從複製可以根據是否是全量分為全量同步和增量同步。
Redis 的複製(replication)功能允許使用者根據一個 Redis 伺服器來建立任意多個該伺服器的複製品,其 中被複制的伺服器為主伺服器(master),而通過複製創建出來的伺服器複製品則為從伺服器(slave)。 主從伺服器兩者擁有相同的資料庫資料:只要主從伺服器之間的網路連線正常,主伺服器就會一直將 發生在自己身上的資料更新同步 給從伺服器,從而一直保證主從伺服器的資料相同。
Redis 提供了兩種方法來為某個主伺服器建立從伺服器:
1. 使用 SLAVEOF 命令,比如向一個伺服器傳送 SLAVEOF 127.0.0.1 6379 ,可以讓接收到該命令的伺服器變為 127.0.0.1:6379 的從伺服器。 在將一個伺服器設定成從伺服器之後,可以通過向它傳送 SLAVEOF no one 來讓它變回一個主 伺服器(資料庫已有的資料會被保留)。
2. 在啟動伺服器時,通過設定 slaveof 配置選項來讓伺服器成為指定 伺服器的從伺服器。
我配置的伺服器分佈:
6376 | 6377 | 6378 | 6379 |
master | slave | slave | slave |
配置並且啟動主節點:
port 6376
daemonize yes
logfile "6376.log"
dbfilename "dump_6376.rdb"
dir "/var/redis/data/"
啟動並重啟從節點
port 6377
daemonize yes
logfile "6377.log"
dbfilename "dump-6377.rdb"
dir "/var/redis/data/"
slaveof 127.0.0.1 6376 // 從屬主節點
檢視服務狀態,登陸客戶端,檢視主從關係 INFO replication
sentinel高可用
監視主從伺服器,並在主伺服器下線時自動進行故障轉移.
它負責持續監控主從節點的健康,當主節點掛掉時,自動選擇一個最優的從節點切換為主節點。客戶端來連線叢集時,會首先連線 sentinel,通過 sentinel 來查詢主節點的地址,然後再去連線主節點進行資料互動。當主節點發生故障時,客戶端會重新向 sentinel 要地址,sentinel 會將最新的主節點地址告訴客戶端。如此應用程式將無需重啟即可自動完成節點切換。比如上圖的主節點掛掉後,叢集將可能自動調整為下圖所示結構。
部署sentinel叢集
sentinel26379 | sentinel26380 | sentinel26381 |
修改sentinel.conf檔案
// Sentinel節點的埠
port 26379
dir /var/redis/data/
logfile "26379.log"
// 當前Sentinel節點監控 127.0.0.1:6379 這個主節點
// 2代表判斷主節點失敗至少需要2個Sentinel節點節點同意
// mymaster是主節點的別名
sentinel monitor mymaster 127.0.0.1 6376 2
//每個Sentinel節點都要定期PING命令來判斷Redis資料節點和其餘Sentinel節點是否可達,如果超過30000毫秒且沒有回覆,則判定不可達
sentinel down-after-milliseconds mymaster 30000
//故障轉移超時時間為180000毫秒
sentinel failover-timeout mymaster 180000
啟動sentinel節點
redis-sentinel sentinel.conf
確認登陸客戶端確認:
redis-cli -h 127.0.0.1 -p 26379 INFO Sentinel
測試
相關指令 sentinel masters
sentinel slaves mymaster
Redis Sentinel
的以下幾個功能。
- 監控:
Sentinel
節點會定期檢測Redis
資料節點和其餘Sentinel
節點是否可達。 - 通知:
Sentinel
節點會將故障轉移通知給應用方。 - 主節點故障轉移:實現從節點晉升為主節點並維護後續正確的主從關係。
- 配置提供者:在
Redis Sentinel
結構中,客戶端在初始化的時候連線的是Sentinel
節點集合,從中獲取主節點資訊。
訊息丟失
Redis 主從採用非同步複製,意味著當主節點掛掉時,從節點可能沒有收到全部的同步訊息,這部分未同步的訊息就丟失了。如果主從延遲特別大,那麼丟失的資料就可能會特別多。Sentinel 無法保證訊息完全不丟失,但是也儘可能保證訊息少丟失。它有兩個選項可以限制主從延遲過大。
min-slaves-to-write 1
min-slaves-max-lag 10
第一個引數表示主節點必須至少有一個從節點在進行正常複製,否 則就停止對外寫服務,喪失可用性。 何為正常複製,何為異常複製?這個就是由第二個引數控制的,它的單位是秒,表示如果 10s 沒有收到從節點的反饋,就意味著從節點同步不正常,要麼網路斷開了,要麼一直沒有給反饋。
Cluster叢集
------複製特性可以建立指定伺服器的複製品,這些複製品可以用於擴充套件系統處理讀請求的能力。
------Redis Sentinel 可以在複製特性的基礎上,通過監視主從伺服器並在主伺服器故障時執行自動故
障轉移來保證系統的可用性。
寫能力不足怎麼辦??????
分片技術
叢集使用分片來擴充套件資料庫的容量,並將命令請求的負載交給不同的節點來分擔。
叢集將整個資料庫分為 16384 個槽(slot),所有鍵都屬於這 16384 個槽的其中一個,計算鍵 key 屬於哪個槽的公式為 slot_number = crc16(key) % 16384 ,其中 crc16 為 16 位的迴圈冗餘校驗和函式。
叢集中的每個主節點都可以處理 0 個至 16384 個槽,當 16384 個槽都有某個節點在負責處理時,叢集進入上線狀態,並開始處理客戶端傳送的資料命令請求。
比如說,如果我們有三個主節點 7000 、 7001 和 7002 ,那麼我們可以:
- 將槽 0 至 5460 指派給節點 7000 負責處理;
- 將槽 5461 至 10922 指派給節點 7001 負責處理;
- 將槽 10923 至 16383 指派給節點 7002 負責處理;
這樣就可以將 16384 個槽平均地指派給三個節點負責處理。
請求轉向:
對於一個被指派了槽的主 節點來說,這個主節點只會處理屬於指派給自己的槽的命令請求。如果一個節點接收到了和自己處理的槽無關的命令請求,那麼節點會向客戶端返回一個轉向錯誤(redirection error),告訴客戶端,哪個節點才是負責處理這條命令的,之後客戶端需要根據錯誤中包含的地址和埠號重新向正確的 節點發送命令請求。
叢集搭建:
1、建立多個節點
2、為每個節點指派槽,並將多個節點連線起來,組成一個叢集
3、當叢集資料庫的 16384 個槽都有節點在處理時,叢集進入上線狀態。
五、補充
1、應用場景不同,配置不同。
快取:關閉aof化,rdb持久化視情況而定 ,開啟最大記憶體設定(否則為實體記憶體)
資料庫:開啟 aof ,開啟rdb 關閉最大記憶體設定