# Redis ## NoSQL (NOT only SQL) NoSQL 的意思是它不僅僅是SQL,所以他可以做到類似SQL的關聯式結構,而redis的效能好的原因是他不需要依賴業務邏輯去儲存資料,而是以key and value方式,因為大大增加料的擴展能力,例如主從式架構,db資料的拷貝,相對SQL來說非常容易,但redis或是NOSQL還是有一些使用上的限制如下 * 不遵循SQL標準 * 不支持ACID * 遠超過SQL的性能 這裡補充一下不支持ACID準則,不代表他無法做事務機制,redis之類的nosql還是有自己對應的事物操作。 [redis cheat sheet](https://quickref.me/redis.html) ## Server 演進 在 web 1.0時期屬於單體示架構,一台server對應一台db ![](https://i.imgur.com/HjrlA2q.png) 但隨著ap架構增長,單體是架構就會出現內存壓力與db io 壓力等問題 ![](https://i.imgur.com/2lhEyfO.png) 而NoSQL目的就是要解決以上的壓力問題,以session為例子,以往我的的session需要拷貝到不同server上,這樣會造成,數據的冗余節點越多浪費更大,甚至多個節點你需要多個IO去拉取db來達到同步 ![](https://i.imgur.com/l8dGfQT.png) 下圖就是透過NOSQL去同步多台server的session資料,減少資料拷貝次數,同時減少IO壓力,這也是NoSQl很常拿來做db cache的原因,無需透過IO操作就可以同步資料 ![](https://i.imgur.com/9b6JjzN.png) NoSQL也可以透過破壞一定程度的業務邏輯來達到性能的提升,目的其實就是減少IO ![](https://i.imgur.com/GfopLHM.png) Redis 是一個開源的內存資料庫系統,支援多種資料結構(如字串、列表、哈希、集合、有序集合等)並提供高效的查詢和快速的讀寫性能。Redis 支援兩種不同的持久化方式,即 RDB(Redis Database)和 AOF(Append-Only File),它們各自有優點和缺點。 ### RDB 的優點: 效能較好:RDB 是一種快速且高效的持久化方式,它會將 Redis 的資料集快照存儲到磁碟上,並在需要的時候可以快速地恢復資料。這使得 RDB 在恢復大型資料集時非常快速,並且對於備份和複製資料庫也非常有效。 資料壓縮:RDB 支援資料壓縮,可以將資料集壓縮後存儲在磁碟上,節省了磁碟空間。 簡單:RDB 的配置和使用相對簡單,適合於快速設置和使用。 ### RDB 的缺點: 適合大型資料集的定期快照:RDB 使用的是定期快照的方式來保存資料,因此在發生故障時可能會損失最近的資料更新。 資料恢復時可能較慢:當資料集很大時,RDB 的資料恢復速度可能會比較慢,因為需要讀取整個快照並將其還原到內存中。 不適合高頻率的資料更新:如果資料集需要經常更新,那麼 RDB 的快照會比較頻繁,這可能會對系統的性能產生一些影響。 ### AOF 的優點: 高可靠性:AOF 是一種寫日誌(Write-Ahead Log)的方式,它記錄了每一次對資料庫的寫操作,因此可以提供更高的資料可靠性,即使發生故障也可以輕鬆地從日誌中恢復資料。 恢復精確度高:由於 AOF 記錄了每一次寫操作,因此資料恢復時可以更加精確地還原資料的 狀態,避免了資料丟失的風險。 適合高頻率的資料更新:AOF 可以選擇不同的同步策略,包括每次寫操作都同步到磁碟,或者定期同步到磁碟,這使得 AOF 在高頻率的資料更新場景中更加適用,可以提供更好的資料一致性和持久性。 ### AOF 的缺點: 效能較低:AOF 的寫入操作需要追加到日誌文件中,這會導致寫入操作的速度相對較慢,因此相較於 RDB,AOF 的效能可能會稍低。 文件大小較大:由於 AOF 需要記錄每一次寫操作,因此 AOF 文件的大小通常會比 RDB 文件的大小大,這會占用更多的磁碟空間。 恢復速度相對較慢:AOF 的資料恢復速度相對於 RDB 來說可能會比較慢,因為需要逐行解析並還原資料,尤其在資料集較大的情況下,恢復時間可能會較長。 綜合而言,RDB 適合對資料一致性要求不高、資料集較大且資料讀取比較頻繁的場景,而 AOF 適合對資料一致性要求較高、資料更新較為頻繁的場景。選擇哪種持久化方式應根據實際應用場景的需求來進行選擇。在某些情況下,也可以同時使用 RDB 和 AOF 來實現資料的雙重持久化,提供更高的資料保證和可靠性。 ### zset zset在redis是一個有序的集合,裡面的值不會重複,但對應到的score是可以的,適合用在做排行榜的資料。 ```javascript // zadd 新增key topn redis > zadd topn 200 java 300 c++ 400 mysql 500 php //列出所有內容 redis > zrange topn 0 -1 1) "java" 2) "c++" 3) "mysql" 4) "php" //列出前兩筆資料,排序是由小到大 redis > zrange topn 0 1 1) "java" 2) "c++" //透過withscores 可以清楚看到值得大小 redis > zrange topn 0 2 withscores 1) "java" 2) "200" 3) "c++" 4) "300" 5) "mysql" 6) "400" //可以看到在該範圍內的value redis > zrangebyscore topn 100 200 1) "java" //為key內部的value加值 redis > zincrby topn 50 java "250" //這時候value就改變了 redis > zrangebyscore topn 250 250 1) "java" //刪除key成員 redis > zrem topn php //列出成員中value的範圍個數 redis > zcount topn 100 250 (integer) 1 //查看排名 redis > zrank topn php (integer) 3 ``` SortedSet (zset)底層結構類似為java Map<String,Double>,內部的元素value賦予一個權重score進行排序,透過map的hash關聯找到對應的值,達到快速查詢的效果 ### redis many to many sample hset 類似於 js object sadd 類似於 js array ```javascript # Here are my categories > hset category:1 name cinema ... more fields ... > hset category:2 name music ... more fields ... > hset category:3 name sports ... more fields ... > hset category:4 name nature ... more fields ... # Here are my users > hset user:1 name Jack ... more fields ... > hset user:2 name John ... more fields ... > hset user:3 name Julia ... more fields ... # Let's establish the many-to-many relationship # Jack likes cinema and sports # John likes music and nature # Julia likes cinema, music and nature # For each category, we keep a set of reference on the users > sadd category:1:users 1 3 > sadd category:2:users 2 3 > sadd category:3:users 1 > sadd category:4:users 2 3 # For each user, we keep a set of reference on the categories > sadd user:1:categories 1 3 > sadd user:2:categories 2 4 > sadd user:3:categories 1 2 4 ``` ```javascript # Categories of Julia > smembers user:3:categories 1) "1" 2) "2" 3) "4" # Users interested by music > smembers category:2:users 1) "2" 2) "3" # Users interested by both music and cinema > sinter category:1:users category:2:users 1) "3" ``` ### redis 分布式存儲 #### 億萬級數據的數據存儲 當數據更多單一server勢必無法承受大規模的吞吐量,解決方式有二: **垂直整合:** 加大單一機台硬體設備效能,但肯定會有附載上限,例如 google 、 amazon 等這些大廠肯定背後不止一台 server 運行。 為了解決垂直整合的上限,這時就有分部式存儲觀念,分布式存儲就是實作 db 的 load balancer ,方法有以下三點: **水平擴充:** #### hash 取餘數 用戶透過固定 hash 算法,% N台機器節點數量,計算hash 值將讀寫操作映射到相對的 redis server 身上,由於 redis key 是獨立的,這也確保每次的讀寫都是到特定的 server 中。 **優點:** 簡單快速達到負載均衡效果,只需劃分好機台數量。 **缺點:** 當單一機台掛掉或是增加機台,分母數量變化,原本選好的 server 位置將重新計算,資料可能會有缺失,甚至 db migrate 、集群擴縮容會因為server 掛掉導致數據不一致與資料遺失而產生實作問題。 #### 一致性 hash 目的是當服務器數量發生變化,勁量減少影響客戶端到服務器的映射關係,解決hash 取餘 server 節點掛掉資料遺失問題,以及節點數量變化的數據遷移。 一致性 hash 算法不同於 hash 取餘根據 server 節點數量外,是透過將 hash算法擴大至 0 ~2^32 首尾相連的原型數據,並根據 hash 結果找到最接近的節點。為什麼是 2^32是因為 redis 理論上可以容納的最大節點數就是 2^32 。 ![](https://hackmd.io/_uploads/ryUCoBmPh.png) 一致性 hash 可以夠過 ip 地址換算找到對應的節點位置,找到 key hash 位置,key 落點的位置順時針走遇到的第一個 server 位置就是要存儲的地方 **優點:** server 掛掉並不會導致所有節點位置都需要重算,增加server容錯率跟擴展性,例如服務器2掛掉,那剩下的請求將會順時針改存儲到服務器3,影響的數據只會是服務器2跟服務器3的資料,其餘都是正常。 **缺點:** 數據傾斜問題,你會發現 c-b 之間位置較大,這時 c server可以要處理更多請求,導致 c server需要應付更多的吞吐量。 ![](https://hackmd.io/_uploads/HyYJpBXDh.png) ![](https://hackmd.io/_uploads/rkixiHmw2.png) ### hash slot 分配 在數據跟節點之間多了一個中介層,用於數據分配,一個集群最多有 16384個 slot 默認,redis 集群式透過 hash slot 去分配數據而不是用一致性 hash,解決數據傾斜問題,原本在一致性 hash 算法中的落點在hash slot 中的slot。 ![](https://hackmd.io/_uploads/Syumx8mwn.png)