大数据系列——Redis理论
?
概述
Remote Dictionary Server(Redis) 是一個由 Salvatore Sanfilippo寫的 key-value存儲系統,是跨平臺的非關系型數據庫,也屬于一種nosql數據庫,通常被稱為數據結構服務器。
Redis 是一個開源的使用 ANSI C 語言編寫、遵守 BSD 協議、支持網絡、可基于內存、分布式、可選持久性的鍵值對(Key-Value)存儲數據庫,并提供多種語言的 API。
Redis 與其他 key - value 緩存產品有以下特點:
Redis支持數據的持久化,可以將內存中的數據保存在磁盤中,重啟的時候可以再次加載進行使用。
Redis不僅僅支持簡單的key-value類型的數據,同時還提供list,set,zset,hash等數據結構的存儲。
Redis支持數據的備份,即master-slave模式的數據備份。
性能極高 – Redis能讀的速度是110000次/s,寫的速度是81000次/s 。
原子性 – Redis的所有操作都是原子性的,同時Redis還支持對幾個操作全并后的原子性執行。
豐富的特性 – Redis還支持 publish/subscribe, 通知, 設置key有效期等等特性。
下面從如下幾個方面介紹下其相關理論:
目錄
概述
架構
核心知識點:
部署方式:
優缺點分析
常見應用場景:
調優經驗:
API應用:
架構
核心知識點:
1、數據類型
1)、string(字符串)
string 是 redis 最基本的類型,你可以理解成與 Memcached 一模一樣的類型,一個 key 對應一個 value。
string 類型是二進制安全的。意思是 redis 的 string 可以包含任何數據。比如jpg圖片或者序列化的對象。
string 類型是 Redis 最基本的數據類型,string 類型的值最大能存儲 512MB。
2)、list(雙向列表)
Redis列表是簡單的字符串列表,按照插入順序排序。你可以添加一個元素到列表的頭部(左邊)或者尾部(右邊)
一個列表最多可以包含 232 - 1 個元素 (4294967295, 每個列表超過40億個元素)。
3)、set(集合)
Redis 的 Set 是 String 類型的無序集合。集合成員是唯一的,這就意味著集合中不能出現重復的數據。
集合對象的編碼可以是 intset 或者 hashtable。
Redis 中集合是通過哈希表實現的,所以添加,刪除,查找的復雜度都是 O(1)。
集合中最大的成員數為 232 - 1 (4294967295, 每個集合可存儲40多億個成員)。
4)、hash(哈希)
Redis hash 是一個鍵值(key=>value)對集合。
Redis hash 是一個 string 類型的 field(字段) 和 value(值) 的映射表,hash 特別適合用于存儲對象。
Redis 中每個 hash 可以存儲 232 - 1 鍵值對(40多億)。
5)、zset(sorted set:有序集合)
Redis 有序集合和集合一樣也是 string 類型元素的集合,且不允許重復的成員。
不同的是每個元素都會關聯一個 double 類型的分數。redis 正是通過分數來為集合中的成員進行從小到大的排序。
有序集合的成員是唯一的,但分數(score)卻可以重復。
集合是通過哈希表實現的,所以添加,刪除,查找的復雜度都是 O(1)。
集合中最大的成員數為 232 - 1 (4294967295, 每個集合可存儲40多億個成員)。
6)、HyperLogLog
Redis 在 2.8.9 版本添加了 HyperLogLog 結構。
Redis HyperLogLog 是用來做基數統計的算法,HyperLogLog 的優點是,在輸入元素的數量或者體積非常非常大時,計算基數所需的空間總是固定 的、并且是很小的。
在 Redis 里面,每個 HyperLogLog 鍵只需要花費 12 KB 內存,就可以計算接近 2^64 個不同元素的基 數。這和計算基數時,元素越多耗費內存就越多的集合形成鮮明對比。
但是,因為 HyperLogLog 只會根據輸入元素來計算基數,而不會儲存輸入元素本身,所以 HyperLogLog 不能像集合那樣,返回輸入的各個元素
7)、GEO
Redis GEO 主要用于存儲地理位置信息,并對存儲的信息進行操作,該功能在 Redis 3.2 版本新增。
8)、Stream
Redis Stream 是 Redis 5.0 版本新增加的數據結構。
Redis Stream 主要用于消息隊列(MQ,Message Queue),Redis 本身是有一個 Redis 發布訂閱 (pub/sub) 來實現消息隊列的功能,但它有個缺點就是消息無法持久化,
如果出現網絡斷開、Redis 宕機等,消息就會被丟棄。
簡單來說發布訂閱 (pub/sub) 可以分發消息,但無法記錄歷史消息。
而 Redis Stream 提供了消息的持久化和主備復制功能,可以讓任何客戶端訪問任何時刻的數據,并且能記住每一個客戶端的訪問位置,還能保證消息不丟失。
9)、Bitmap
Bitmap在Redis中不是一種實際的數據類型,而是一種將String作為Bitmap使用的方法。可以理解為將String轉換為bit數組。使用Bitmap來存儲true/false類型的簡單數據極為節省空間。
2、持久化方式
redis將內存中的數據異步寫入硬盤中有三種方式:RDB(默認)、AOF、混合(RDB + AOF增量)
1)、RDB:
通過bgsave命令觸發,然后父進程執行fork操作創建子進程,子進程創建RDB文件,根據父進程內存生成臨時快照文件,
完成后對原有文件進行原子替換(定時一次性將所有數據進行快照生成一份副本存儲在硬盤中)。
優點:是一個緊湊壓縮的二進制文件,Redis加載RDB恢復數據遠遠快于AOF的方式,異步執行,非阻塞redis提供服務
缺點:由于每次生成RDB開銷較大,非實時持久化,會阻塞redis提供服務
2)、AOF:
開啟后,Redis每執行一個修改數據的命令,都會把這個命令添加到AOF文件中。
優點:實時持久化。
缺點:同步執行,會阻塞redis提供服務,? 當AOF文件體積逐漸變大,需要定期執行重寫操作來降低文件體積,加載慢。
3)、混合持久化:
AOF在重寫時,會將 rdb 快照 和 增量的 AOF日志一起寫入新的aof文件,新的文件一開始不叫appendonly.aof,等到重寫完新的AOF文件才會進行改名,原子的覆蓋原有的AOF文件,完成新舊兩個AOF文件的替換;
aof 根據配置規則(aof-use-rdb-preamble yes)在后臺自動重寫,也可以人為執行命令bgrewrite aof重寫AOF。 于是在 Redis 重啟的時候,可以先加載 rdb 的內容,然后再重放增量 AOF 日志就可以完全替代之前的 AOF 全量文件重放,重啟效率因此大幅得到提升。
3、內部執行方式(這是Redis快速的原因所在)
1)、數據存于內存
Redis的數據都在內存中,所以其處理速度很快,所有需要處理的文件都放在文件描述符的集合里rset(fds)。
2)、用了多路復用I/O
Redis內部采用了I/O多路復用技術,該技術依賴于底層的操作系統,一般有三種方式select、poll、epoll。前面兩種本質上一致,一般操作系統都提供,epoll是Linux獨有的。
1)、select模型每次都直接將rset(也就是fds)全部拷貝到內核態,因為內核態速度比用戶空間態快很多。
2)、如果沒數據的話,select函數會阻塞,如果有數據的話會執行兩步
第一步:將有數據的那個fd置位(也就是標記一下,代表這個fd有數據)
第二步:select函數不在阻塞,將繼續往下執行。也就是整體遍歷fds,找到有數據的那個fd讀取數據做處理。他的fd不能重用,每一次都需要重新創建新的fds且將用戶空間態的fds拷貝到內核態
3)、缺點
fds最大支持1024個(可以更改,但是意義不大)
fd不可重用,每次內核態都給置位了,導致為了標記fd,必須創建一個新的rset從而導致fds在用戶態內存態間多次拷貝(也就是fds)
用戶控件態拷貝rset到內核態也需要時間,雖然內核態執行比用戶態快,但是copy也需要開銷
O(n)再次遍歷問題。因為rset里的fd被置位后,select函數并不知道哪個被置位了,需要從頭遍歷到尾,逐個對比。
B、poll:
poll的結構體是為了fd重復利用,不需要每次都拷貝到內核態用的。
1)、解決了select哪些問題
采取的鏈表存儲,而不是bitmap,解決了1024長度限制問題
采取結構體每次置位結構體內的revents字段,而不破壞fd本身,所以可重用,不需要每次都創建新的fd。
2)、缺點
用戶控件態拷貝rset到內核態也需要時間,雖然內核態執行比用戶態快,但是copy也需要開銷
O(n)再次遍歷問題。因為rset里的fd被置位后,select函數并不知道哪個被置位了,需要從頭遍歷到尾,逐個對比。
C、epoll:
epoll將fd放到了紅黑樹里,且不需要拷貝到內核態,因為他采取了“共享內存”的概念。(其實還是復制,只是復制采取了其他技術可以使開銷極其的小)
epoll的置位是重排,比如五個fd, 1 2 3 4 5,1 3 5這三個fd有數據了,那么他會重排序,排成如下1 3 5 2 4。(也有的說是單獨放到新的數組里)
每一次置位nfds的值都+1。且會回調epoll_wait
所以epoll_wait執行完會返回有幾個fd有數據,那么下面的for直接遍歷nfds次即可。解決了前面的兩種O(n)。變成了O1
比如三個redis-cli,假設2個redis-cli寫入命令,
select:那么select模型是輪詢這三個redis-cli的fd,看哪個fd有消息,有的話讀取處理消息。當他下次再寫命令的時候還需要重新創建fd,然后復制到內核態然后再遍歷全部。
poll:那么poll模型是輪詢這三個redis-cli的fd,看哪個fd有消息,有的話讀取處理消息。下次再寫入的時候還是遍歷全局fd,看哪個fd有消息進行處理。省去了每次都創建新的fd且復制的過程。
epoll:epoll就不輪詢了,有消息進來后你通知我,我去處理你的消息,那些沒消息的fd我不管。而且復制到內核態的過程我采取牛逼的技術讓開銷達到最小的極致。
多路復用I/O技術總結
3)、單線程
Redis是單線程提供應用的。
簡單來說,就是我們的redis-client在操作的時候,會產生具有不同事件類型的socket。在服務端,有一段I/0多路復用程序,將其置入隊列之中。然后,IO事件分派器,依次去隊列中取,
轉發到不同的事件處理器中。如下圖:
4、過期策略
過期策略一般有三種方式:key過期清除策略、惰性策略、定期過期策略。
key過期清除:依據寫入數據的過期時間來處理。
惰性過期(類比懶加載,這是懶過期):只有當訪問一個key時,才會判斷該key是否已過期,過期則清除。該策略可以最大化地節省CPU資源,卻對內存非常不友好。極端情況可能出現大量的過期key沒有再次被訪問,從而不會被清除,占用大量內存。
定期過期:每隔一定的時間,會掃描一定數量的數據庫的expires字典中一定數量的key,并清除其中已過期的key。該策略是前兩者的一個折中方案。通過調整定時掃描的時間間隔和每次掃描的限定耗時,可以在不同情況下使得CPU和內存資源達到最優的平衡效果。
redis采用的是定期刪除+惰性刪除策略。
為什么不用定時刪除策略:
定時刪除,用一個定時器來負責監視key,過期則自動刪除。雖然內存及時釋放,但是十分消耗CPU資源。在大并發請求下,CPU要將時間應用在處理請求,而不是刪除key,因此沒有采用這一策略.
定期刪除+惰性刪除工作流程:
定期刪除,redis默認每個100ms檢查,是否有過期的key,有過期key則刪除。需要說明的是,redis不是每個100ms將所有的key檢查一次,而是隨機抽取進行檢查。因此,如果只采用定期刪除策略,會導致很多key到時間沒有刪除。
所以需要惰性刪除策略。也就是說在你獲取某個key的時候,redis會檢查一下,這個key如果設置了過期時間那么是否過期了?如果過期了此時就會刪除。
采用定期刪除+惰性刪除不能確保完全的過期該過期的數據:
如果定期刪除沒刪除key,然后你也沒及時去請求key,也就是說惰性刪除也沒生效。這樣,redis的內存會越來越高,所以還需要配合內存淘汰機制。
5、內存淘汰機制
在redis.conf中有一行配置,專門配置內存淘汰機制。
# maxmemory-policy allkeys-lru
下面是一些備選機制:
1)noeviction:當內存不足以容納新寫入數據時,新寫入操作會報錯。應該沒人用吧。
2)allkeys-lru:當內存不足以容納新寫入數據時,在鍵空間中,移除最近最少使用的key。推薦使用。
3)allkeys-random:當內存不足以容納新寫入數據時,在鍵空間中,隨機移除某個key。應該也沒人用吧,你不刪最少使用Key,去隨機刪。
4)volatile-lru:當內存不足以容納新寫入數據時,在設置了過期時間的鍵空間中,移除最近最少使用的key。這種情況一般是把redis既當緩存,又做持久化存儲的時候才用。不推薦
5)volatile-random:當內存不足以容納新寫入數據時,在設置了過期時間的鍵空間中,隨機移除某個key。依然不推薦
6)volatile-ttl:當內存不足以容納新寫入數據時,在設置了過期時間的鍵空間中,有更早過期時間的key優先移除。不推薦
ps:如果沒有設置 expire 的key, 不滿足先決條件(prerequisites); 那么 volatile-lru, volatile-random 和 volatile-ttl 策略的行為, 和 noeviction(不刪除) 基本上一致。
6、事務
Redis 事務可以一次執行多個命令, 并且帶有以下三個重要的保證:
批量操作在發送 EXEC 命令前被放入隊列緩存。
收到 EXEC 命令后進入事務執行,事務中任意命令執行失敗,其余的命令依然被執行。
在事務執行過程,其他客戶端提交的命令請求不會插入到事務執行命令序列中。
一個事務從開始到執行會經歷以下三個階段:
開始事務。
命令入隊。
執行事務。
PS:
單個 Redis 命令的執行是原子性的,但 Redis 沒有在事務上增加任何維持原子性的機制,所以 Redis 事務的執行并不是原子性的。
事務可以理解為一個打包的批量執行腳本,但批量指令并非原子化的操作,中間某條指令的失敗不會導致前面已做指令的回滾,也不會造成后續的指令不做。
7、發布訂閱
發布訂閱(pub/sub)是一種消息通信模式,主要是解除消息發布者和消息訂閱者之間通信的耦合。
Redis作為一個pub/sub的服務器,在訂閱者和發布者之間起到了一個消息路由的功能。訂閱者可以通過subscribe和psubscribe命令向redis 服務器訂閱自己感興趣的消息類型,
redis將信息類型稱為通道(channel)。當發布者通過publish命令向redis server發送特定類型的信息時,訂閱該信息類型的全部client都會收到此消息。
8、集群中節點間內部通信機制
1)、基礎通信原理
a、redis cluster節點間采取gossip協議進行通信,沒有采用集中式的存儲在某個節點上
b、10000端口
????? 每個節點都有一個專門用于節點間通信的端口,就是自己提供服務的端口號+10000,比如7001,那么用于節點間通信的就是17001端口
????? 每隔節點每隔一段時間都會往另外幾個節點發送ping消息,同時其他幾點接收到ping之后返回pong
c、交換的信息
???? 故障信息、節點的增加和移除、hash slot信息,等等
2)、gossip協議介紹
???? gossip 過程是由種子節點發起,當一個種子節點有狀態需要更新到網絡中的其他節點時,它會隨機的選擇周圍幾個節點散播消息,收到消息的節點也會重復該過程,
????? 直至最終網絡中所有的節點都收到了消息。這個過程可能需要一定的時間,由于不能保證某個時刻所有節點都收到消息,但是理論上最終所有節點都會收到消息,
????? 因此它是一個最終一致性協議。
gossip協議包含多種消息,包括ping,pong,meet,fail,等等
meet: 某個節點發送meet給新加入的節點,讓新節點加入集群中,然后新節點就會開始與其他節點進行通信
redis-trib.rb add-node 其實內部就是發送了一個gossip meet消息,給新加入的節點,通知那個節點去加入我們的集群
ping: 每個節點都會頻繁給其他節點發送ping,其中包含自己的狀態還有自己維護的集群元數據,互相通過ping交換元數據
???????? 每個節點每秒都會頻繁發送ping給其他的集群,通過ping,頻繁的互相之間交換數據,互相進行元數據的更新
pong: 返回ping和meet,包含自己的狀態和其他信息,也可以用于信息廣播和更新
fail: 某個節點判斷另一個節點fail之后,就發送fail給其他節點,通知其他節點,指定的節點宕機了
3)、ping消息深入
ping很頻繁,而且要攜帶一些元數據,所以可能會加重網絡負擔
每個節點每秒會執行10次ping,每次會選擇5個最久沒有通信的其他節點
當然如果發現某個節點通信延時達到了cluster_node_timeout / 2,那么立即發送ping,避免數據交換延時過長,落后的時間太長了
9、數據備份與恢復
Redis創建當前數據庫的備份,有種方式:save、bgsave。
save命令:同步執行,會阻塞Redis服務
bgsave命令:異步執行,不會阻塞Redis服務
這兩種方式都會在Redis安裝目錄中創建dump.rdb文件,如果需要恢復數據,只需將備份文件 (dump.rdb) 移動到 redis 安裝目錄并啟動服務即可。
10、管道技術和分區
管道技術:
Redis 管道技術可以在服務端未響應時,客戶端可以繼續異步向服務端發送請求,并最終一次性讀取所有服務端的響應。
分區:
分區是分割數據到多個Redis實例的處理過程,因此每個實例只保存key的一個子集。
有兩種分區類型:范圍分區、哈希分區。
11、重要程序
redis-server:Redis服務器程序
redis-cli:Redis客戶端程序,它是一個命令行操作工具。也可以使用telnet根據其純文本協議操作。
redis-benchmark:Redis性能測試工具,測試Redis在你的系統及配置下的讀寫性能。
部署方式:
1、單機模式
?? ??由一臺計算來提供Redis服務, 容量和性能受限于機器的配置且不具備高可用。
2、主從復制(本質和單機一樣,只是多了查詢負載均衡)
????? 通過一臺主服務器Master和多臺備份服務器組成的集群提供服務,支持自動備份、負載均衡,主服務器掛掉,需要人工進行切換(不支持高可用)。
???? ?Redis為了解決單點數據庫問題,會把數據復制多個副本部署到其他節點上,通過復制,對數據進行冗余備份,從而保證數據高度可靠性。
復制(Replication)的原理:
①從數據庫向主數據庫發送sync(數據同步)命令。
②主數據庫接收同步命令后,會保存快照,創建一個RDB文件。
③當主數據庫執行完保持快照后,會向從數據庫發送RDB文件,而從數據庫會接收并載入該文件。
④主數據庫將緩沖區的所有寫命令發給從服務器執行。
⑤以上處理完之后,之后主數據庫每執行一個寫命令,都會將被執行的寫命令發送給從數據庫。
注意:在Redis2.8之后,主從斷開重連后會根據斷開之前最新的命令偏移量進行增量復制
3、哨兵模式(主從復制+哨兵)(本質和單機一樣,保證了高可用)
???? 通過一臺主服務器Master和多臺備份服務器組成的集群提供服務,保證高可用。
????
???? 哨兵是Redis集群架構中非常重要的一個組件,哨兵的出現主要是解決了主從復制出現故障時需要人為干預的問題。
哨兵模式架構圖
A、Redis哨兵主要功能
(1)集群監控:負責監控Redis master和slave進程是否正常工作
(2)消息通知:如果某個Redis實例有故障,那么哨兵負責發送消息作為報警通知給管理員
(3)故障轉移:如果master node掛掉了,會自動轉移到slave node上
(4)配置中心:如果故障轉移發生了,通知client客戶端新的master地址
B、Redis哨兵的高可用原理:
當主節點出現故障時,由Redis Sentinel自動完成故障發現和轉移,并通知應用方,實現高可用性。
哨兵機制建立了多個哨兵節點(進程),共同監控數據節點的運行狀況。
同時哨兵節點之間也互相通信,交換對主從節點的監控狀況。
每隔1秒每個哨兵會向整個集群:Master主服務器+Slave從服務器+其他Sentinel(哨兵)進程,發送一次ping命令做一次心跳檢測。
哨兵用來判斷節點是否正常的重要依據,涉及兩個新的概念:主觀下線和客觀下線。
主觀下線:一個哨兵節點判定主節點down掉是主觀下線。
客觀下線:只有半數哨兵節點都主觀判定主節點down掉,此時多個哨兵節點交換主觀判定結果,才會判定主節點客觀下線。
基本上哪個哨兵節點最先判斷出這個主節點客觀下線,就會在各個哨兵節點中發起投票機制Raft算法(選舉算法),最終被投為領導者的哨兵節點完成主從自動化切換的過程。
C、哨兵選舉
一般情況下當哨兵發現主節點sdown之后 該哨兵節點會成為領導者負責處理主從節點的切換工作:
哨兵A發現Redis主節點失聯;
哨兵A報出sdown,并通知其他哨兵,發送指令sentinel is-master-down-by-address-port給其余哨兵節點;
其余哨兵接收到哨兵A的指令后嘗試連接Redis主節點,發現主節點確實失聯;
哨兵返回信息給哨兵A,當超過半數的哨兵認為主節點下線后,狀態會變成odown;
最先發現主節點下線的哨兵A會成為哨兵領導者負責這次的主從節點的切換工作;
哨兵的選舉機制是以各哨兵節點接收到發送sentinel is-master-down-by-address-port指令的哨兵id 投票,票數最高的哨兵id會成為本次故障轉移工作的哨兵Leader;
D、哨兵故障轉移
當哨兵發現主節點下線之后經過上面的哨兵選舉機制,選舉出本次故障轉移工作的哨兵節點完成本次主從節點切換的工作:
哨兵Leader 根據一定規則從各個從節點中選擇出一個節點升級為主節點;
其余從節點修改對應的主節點為新的主節點;
當原主節點恢復啟動的時候,變為新的主節點的從節點
哨兵Leader選擇新的主節點遵循下面幾個規則:
健康度:從節點響應時間快;
完整性:從節點消費主節點的offset偏移量盡可能的高;
穩定性:若仍有多個從節點,則根據從節點的創建時間選擇最有資歷的節點升級為主節點;
在哨兵模式下主從節點總是會變更,因此在應用中訪問哨兵模式下的Redis時可以使用對應的哨兵接口連接:
例如 java:JedisSentinelPool;Python:SentienlConnectionPool
4、集群模式
?redis集群在3.0以后提供了分布式存儲方案,保證高可用,提高并發量。集群由多個節點(Node)組成,將數據按一定的規則分配到多臺機器,內存/QPS不受限于單機,實現高擴展性。
集群中的節點分為主節點和從節點,只有主節點負責讀寫請求和集群信息的維護,從節點只進行主節點數據和狀態信息的復制。為了適應選舉算法要求,一般要求集群的主節點和從節點數量都采用奇數,最少需要3個節點。
可以直接使用 redis-cli --cluster 來管理集群,包括創建集群、伸縮集群節點、槽遷移、完整性檢查、分區平衡等。
集群模式架構圖
為了實現分布式集群,引入了槽的概念來處理數據分區。
數據分區規則一般考量2個重要因素:1、是否均勻, 2、伸縮節點對數據分布的影響。
一般有下面幾種方法來實現分區算法:
1)、哈希取余
根據key計算hash值,然后對節點數量取余。該方法初始化時能夠做到均勻分布,但后期伸縮時會引發大量數據遷移。
2)、一致性哈希
將hash值區間(0~2 32-1)抽象為一個順時針環形,節點均勻分布在環形上,根據key計算hash值,然后在環形上順時針查找節點,找到第一個就將數據落到該節點。
??? 相比哈希取余來說,該方法減少數據遷移,因為其將影響訪問控制到相鄰節點,但會造成數據不均勻。
3)、帶虛擬槽的一致性哈希
Redis就是采用這種方式來處理數據分布的。一共設定16384個槽slot,采用hash算法將這些槽分配到各個節點上,hash_slot = crc16(key) mod 16384。
?? 該方法克服了上面兩種的缺點,能夠很好的解決均勻分布、伸縮節點對數據分布影響很小。
Redis集群內節點通過ping/pong消息實現節點通信,消息不但可以傳播節點槽信息,還可以傳播其他狀態如:主從狀態、節點故障等。因此故障發現也是通過消息傳播機制實現的,主要環節包括:主觀下線(pfail)和客觀下線(fail)
主客觀下線:集群中每個節點都會定期向其他節點發送ping消息,接收節點回復pong消息作為響應。如果通信一直失敗,則發送節點會把接收節點標記為主觀下線(pfail)狀態。
客觀下線:超過半數,對該主節點做客觀下線
主節點選舉出某一主節點作為領導者,來進行故障轉移
??? 其中虛擬槽的分配算法沒有采用最終一致性的hash算法原因如下:
????? 1)、發生縮容時,需要知道被影響的那部分數據,要進行手動遷移
????? 2)、因為其本質是一個順時針環,所有會發生熱點數據都集中在某一個Master上會出現性能瓶頸
集群限制
由于Redis集群中數據分布在不同的節點上,因此有些功能會受限:
db庫:單機的Redis默認有16個db數據庫,但在集群模式下只有一個db0;
復制結構:上面的復制結構有樹狀結構,但在集群模式下只允許單層復制結構;
事務/lua腳本:僅允許操作的key在同一個節點上才可以在集群下使用事務或lua腳本;(使用Hash Tag可以解決)
key的批量操作:如mget,mset操作,只有當操作的key都在同一個節點上才可以執行;(使用Hash Tag可以解決)
keys/flushall:只會在該節點之上進行操作,不會對集群的其他節點進行操作;?
Hash Tag:
上面介紹集群限制的時候,由于key被分布在不同的節點之上,因此無法跨節點做事務或lua腳本操作,但我們可以使用hash tag方式解決。
hash tag:當key包含{}的時候,不會對整個key做hash,只會對{}包含的部分做hash然后分配槽slot;因此我們可以讓不同的key在同一個槽內,這樣就可以解決key的批量操作和事務及lua腳本的限制了;
但由于hash tag會將不同的key分配在相同的slot中,如果使用不當,會造成數據分布不均的情況,需要注意。
優缺點分析
缺點:
1、容量及處理能力有限,受限于單臺機器的配置。
???? 為了應對容量的問題,一般做如下處理:
???? a、采用分布式集群處理(分片集群),將數據通過路由代理分片到不同的服務器
???? b、采用集群方式,利用虛擬槽的概念來做。
2、redis和數據庫雙寫一致性問題
???? Redis只能做到最終一致性,不能保證強一致性。
3、緩存穿透
????? 若緩存和數據庫中都沒有的數據,大流量都會直接打到DB,導致DB掛掉,這種現象俗稱“緩存穿透”。
????? 為了應對這種情況,一般做如下處理:
?????? a、會將該key寫入緩存值設置為null,為了減少對正常應用的影響,把有效時間設置的短一點,具體根據應用場景來定。
?????? b、采用降級或限流控制流量入口
4、緩存雪崩
???? 若緩存中的數據大批量已經過期,大流量都會直接打到DB,導致DB掛掉,這種現象俗稱“緩存雪崩”。
???? 為了應對這種情況,一般做如下處理:
???? a、緩存數據的過期時間設置隨機,防止同一時間大量數據過期現象發生。
???? b、如果緩存數據庫是分布式部署,將熱點數據均勻分布在不同的緩存數據庫中,且設置不同的過期時間。
???? c、熱點數據考慮設置永遠不過期。
???? d、采用降級或限流控制流量入口
5、緩存擊穿
???? 若緩存中沒有但數據庫中有(指同一條數據),大流量并發取這條數據到DB,導致DB掛掉,這種現象俗稱“緩存擊穿”。
???? 為了應對這種情況,一般做如下處理:
???? a、熱點數據考慮設置永遠不過期。
???? b、采用布隆過濾器。
???? c、采用降級或限流控制流量入口。
6、熱點Key并發競爭
???? 若同時有多個有個多個或不同系統請求并發設置一個key,會發生數據應用不一致問題。例如像事務中的臟讀、幻讀等。
???? 為了應對這種情況,一般做如下處理:
???? a、若不要求順序,可以采用加鎖或分布式鎖
???? b、若要求按順序,可以采用加鎖或分布式鎖,先放入隊列,然后異步處理隊列。
???? c、或者采用其他方式,做到串行處理set操作即可。? ???
優點:
1、數據處理速度快
???? 因為其基于內存操作,每秒可以執行大約 110000 個寫入操作,或者 81000 個讀操作,其速度遠超數據庫。
2、支持豐富的數據類型
???? Redis不僅僅支持簡單的key-value類型的數據,同時還提供String,list,set,zset,hash等數據結構的存儲。
3、支持數據的持久化
????? 可以將內存中的數據保存在磁盤中,重啟的時候可以再次加載進行使用
4、支持數據的備份
???? 內置支持master-slave模式的數據備份
5、內部操作原子性
???? Redis的所有操作都是原子性的(要不成功,要不失敗),同時Redis還支持對幾個操作全并后的原子性執行,也就是事務。
???? 但要注意這里的事務只能保證批量塊的事務,批量塊內部的語句不具備事務性。
6、豐富的特性
????? Redis 可以在如緩存、消息傳遞隊列中使用(Redis 支持“發布+訂閱”的消息模式),在應用程序如 Web 應用程序會話、網站頁面點擊數、設置key有效期等中使用。
常見應用場景:
1、緩存
? 緩存現在幾乎是所有中大型網站都在用的必殺技,合理的利用緩存不僅能夠提升網站訪問速度,還能大大降低數據庫的壓力。Redis提供了鍵過期功能,也提供了靈活的鍵淘汰策略,
所以,現在Redis用在緩存的場合非常多。例如:token、熱點數據。
2、排行榜
很多網站都有排行榜應用的,如京東的月度銷量榜單、商品按時間的上新排行榜等。Redis提供的有序集合數據類構能實現各種復雜的排行榜應用。
3、計數器
? 如電商網站商品的瀏覽量、視頻網站視頻的播放數等。為了保證數據實時效,每次瀏覽都得給+1,并發量高時如果每次都請求數據庫操作無疑是種挑戰和壓力。
? Redis提供的incr命令來實現計數器功能,內存操作,性能非常好,非常適用于這些計數場景。
4、分布式會話
集群模式下,在應用不多的情況下一般使用容器自帶的session復制功能就能滿足,當應用增多相對復雜的系統中,一般都會搭建以Redis等內存數據庫為中心的session服務,session不再由容器管理,而是由session服務及內存數據庫管理。
5、分布式鎖
? 在很多互聯網公司中都使用了分布式技術,分布式技術帶來的技術挑戰是對同一個資源的并發訪問,如全局ID、減庫存、秒殺等場景,并發量不大的場景可以使用數據庫的悲觀鎖、樂觀鎖來實現,但在并發量高的場合中,利用數據庫鎖來控制資源的并發訪問是不太理想的,大大影響了數據庫的性能。可以利用Redis的setnx功能來編寫分布式的鎖,如果設置返回1說明獲取鎖成功,否則獲取鎖失敗,實際應用中要考慮的細節要更多。
6、 社交網絡
點贊、踩、關注/被關注、共同好友等是社交網站的基本功能,社交網站的訪問量通常來說比較大,而且傳統的關系數據庫類型不適合存儲這種類型的數據,Redis提供的哈希、集合等數據結構能很方便的的實現這些功能。
7、最新列表
? Redis列表結構,LPUSH可以在列表頭部插入一個內容ID作為關鍵字,LTRIM可用來限制列表的數量,這樣列表永遠為N個ID,無需查詢最新的列表,直接根據ID去到對應的內容頁即可。
8、消息系統(不推薦使用)
消息隊列是大型網站必用中間件,如ActiveMQ、RabbitMQ、Kafka等流行的消息隊列中間件,主要用于業務解耦、流量削峰及異步處理實時性低的業務。
Redis提供了發布/訂閱及阻塞隊列功能,能實現一個簡單的消息隊列系統。
調優經驗:
PS:后續逐漸把實踐調優過程補充上來
API應用:
各個平臺一般都有相應的操作組件,下面介紹下java和DoNet平臺下的訪問組件。
java平臺:
1、jedis
<dependency>
??? <groupId>redis.clients</groupId>
??? <artifactId>jedis</artifactId>
??? <version>版本</version>
</dependency>
2、Spring Boot整合Spring Cache應用Redis
<dependency>
??? <groupId>org.springframework.boot</groupId>
??? <artifactId>spring-boot-starter-data-redis</artifactId>
??? <version>版本</version>
</dependency>
3、spring-data-redis
<dependency>
??? <groupId>org.springframework.data</groupId>
??? <artifactId>spring-data-redis</artifactId>
??? <version>版本</version>
</dependency>
4、Redisson(底層使用Netty)
<dependency>
???????? <groupId>org.redisson</groupId>
???????? <artifactId>redisson-spring-boot-starter</artifactId>
???????? <version>版本</version>
</dependency>
5、Lettuce
<dependency>
??? <groupId>io.lettuce</groupId>
??? <artifactId>lettuce-core</artifactId>
??? <version>5.1.8.RELEASE</version>
</dependency>
DoNet平臺:
1、CSRedisCore 推薦使用這個
Nuget: 下載CSRedisCore
2、StackExchange.Redis?
Nuget: StackExchange.Redis
3、ServiceStack.Redis 微軟提供 收費
Nuget: ServiceStack.Redis
總結
以上是生活随笔為你收集整理的大数据系列——Redis理论的全部內容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: 推荐系统-通过数据挖掘算法协同过滤讨论基
 - 下一篇: smart的一些简单使用