標籤:watch 種類型 over 技術分享 大量 中心 壓力 欄位 記憶體操作
Redis適合哪些業務情境
常規業務系統的資料庫訪問中,讀寫操作的比例一般在7/3到9/1,也就是說讀操作遠多於寫操作,因此高並發系統設計裡,通過NoSQL技術將熱點資料(短期內變動機率小的資料)放入記憶體以達到減輕DB壓力,提升資料訪問速度的目的,Redis和MongoDB是當下應用最廣泛的NoSQL產品,當然如果系統裡的寫操作居多,也沒有必要使用緩衝,因此Redis主要用於解決訪問效能和並發能力的問題。除了純資料緩衝的作用之外,得益於其超高速的響應能力,Redis也常用於提供分布式鎖的解決方案。
哪些設計思路保證了Redis高效能
單個Redis server對請求的處理是基於單線程工作模型的,但由於是純記憶體操作,並且單線程的工作模式避免了線程環境切換帶來的額外開銷,同時使用NIO多工機制(單線程維護多個I/O socket的狀態,socket event handler統一進行event分發,並通知到各個event listener),所以即使是單台Redis server的效能也是非常的快,可支援11萬次/秒的SET操作,8.1萬次/秒的GET操作。
高並發系統裡有時候單台Redis server不能滿足效能需求,Redis 3.0之前的辦法是Redis Sentinel,多個節點同時提供服務,並且每個節點都儲存全量資料,Redis 3.0之後引入了Redis Cluster,通過資料分區(Data Sharding)在每個節點上只保留部分資料(總共有16384個slot)來實現高可用。
Redis Cluster採用無中心節點方式實現,用戶端直接與redis叢集的每個節點串連,客戶請求到達節點之後,使用統一的雜湊演算法,CRC16(key)%16384,計算出key對應的slot,然後從Redis Cluster定位出具體的server,具體的data sharding,最終將資料返回給使用者 。但由於Redis的事務僅能解決單台server上的ACID問題,對於多台server常見的問題是多個請求針對同一個key的操作一致性問題,需要結合Zookeeper使用分布式鎖的機制解決一致性問題和順序操作問題。
Redis支援動態添加和刪除節點,動態遷移和重新平衡slot,動態重新選舉Leader並進行fail-over;每一個節點雖然只儲存一部分的slot,但會儲存一份相同的data sharding mapping table,這張表記錄所有16384個slot的host分布位置,並且節點之間會定期同步更新這張表的資訊,這樣的設計可以保證Redis cluster內部節點之間只需要很少的資訊就可以相互同步資訊。用戶端訪問Redis Cluster的時候會指定叢集內的一台host:port,如果操作的key不在當前的host上,則host會根據data sharding mapping table告訴用戶端正確的host:port;如果訪問的host:port下線了,則用戶端的連結自動轉移到對應的master或者slave節點。
Redis中各種資料類型的使用情境
Redis的資料存放區主要通過key/value實現,key都是string類型,value則分不同的應用情境有五種類型定義:
#1 string類型:可以包含任何資料(jps圖片或者經過序列化的對象,單個key最大可以儲存512M的資料),具有全域統計功能的資料,如全域ID產生器、叢集配置資訊等;
#2 hash類型:用於儲存物件結構的資料,多個field綁定到一個key上(對比使用string類型儲存物件的優勢在於hash類型可以直接update具體field的值而不影響其他field),如實現SSO,cookie為key,使用者資訊為value,並有指定到期時間;
#3 list類型:用於儲存需要基於隊列或者棧操作的系列資料,如訊息佇列;
#4 set類型:用於儲存需要維護一個全域不重複的集合,如服務註冊發現,可以實現全域去重的功能,如訪問網頁的獨立IP,共同好友等;
#5 zset類型:用於儲存需要維護一個全域不重複但有權重排序的列表可以使用SORTED SET,如積分熱門排行榜、帶權重的訊息佇列。
對上述的欄位類型都可以進行的類似的操作,
設定一個值:[set|hmset|lpush|sadd] key value
擷取一個值:[get|hget] key
刪除一個值:[del|hdel] key
設定一個具有到期時間的值:[setex] key time value
如果值不存在就設定這個值:[setnx] key value
尋找redis中的keys或者pattern:[keys/scan] key
判斷一個值是否存在:[exists|hexists] key
給指定值設定到期時間:[expire] key seconds
將指定key的value加1|減1:[incr|decr] key
將一個key\value遷移到指定server:[migrate] host port key dest-db timeout [copy] [replace]
HyperLogLog用於做基數統計(基於set類型的封裝,僅根據輸入的獨立元素個數進行統計,而不儲存元素本身),可以保證在輸入元素數量或者體積非常大的時候可以保證統計所需的空間固定為12kb(最大2^64個元素) 。
在指定的key中添加基數:[pfadd] key value
統計指定key中不同基數的個數:[pfcount] key
將sourceKey的技術合并到destKey的基數統計中:[pfmerge] destKey sourceKey
Pub/Sub用於做訊息的發布訂閱(基於list類型的封裝,將訊息封裝成list的節點)。
建立一個資訊接收channel:[subscribe] channel
向指定的channel發送一個資訊:[publish] channel message
單個redis命令的執行具有原子性,對於多個命令而言redis提供基礎事務機制,但是不保證多個命令執行的原子性,一個典型的redis事務如下:
開啟一個事務:[multi]
之後可以計劃多條redis命令,但並不會執行
提交並執行之前的所有命令:[exec]
開始執行之前計劃的redis命令,如果其中某條命令執行失敗並不會影響其他命令的執行
取消執行事務塊內所有計劃的redis命令:[discard]
監視一個或者多個key:[watch] key
表示在執行exec之前如果key被事務之前的命令修改,則當前事務被discard。
Redis資料到期策略和記憶體回收策略
針對已經到期的資料Redis採用定期刪除和延遲刪除結合的策略,但是兩者都有缺陷;由於定期檢查所有的key是否到期會帶來效能問題,因此定期刪除策略使用的是隨機抽查,另外在操作Key前會判斷是否已經到期,如到期則立即刪除;這樣的策略會導致一些已經到期的key還堆積在記憶體裡,使得redis server記憶體佔用率居高不下,因此需要結合redis.conf中的maxmemory-policy配置使用,也就是當redis server的記憶體不足以寫入新資料時的記憶體回收策略,
#1 noeviction:表示直接報錯;
#2 allkeys-lur:表示在所有keys中根據LRU刪除key;
#3 allkeys-random:表示在所有keys中隨機刪除key;
#4 volatile-lru/volatile-random/volatile-ttl用於當redis server既充當cache又當DB的時候,表示在設定了expire date的keys中進行刪除,ttl表示刪除擁有更早到期時間的key。
解決Redis緩衝穿透和緩衝雪崩問題
緩衝穿透和雪崩可以看做一個問題,只是嚴重程度不同;當一個請求到達redis之後發現沒有對應的快取資料,然後向DB發送資料請求,如果能擷取到資料那問題就停留在了緩衝穿透上,DB擷取到的資料會緩衝到redis上;如果DB中也沒有對應的資料,並且當這樣的請求達到一定數量級並且耗用完所有的DB資源,最終導致DB串連異常就出現了緩衝雪崩問題。
解決緩衝穿透問題的思路有下述幾種,不管是否從DB中尋找到對應的值(沒有值就為null),都在redis中記錄一條緩衝記錄;在Dao層維護一張BitMap,用bit記錄對應的key是否有對應值,從而避免冗餘的DB操作;後台線程專門用於更新即將到期的Redis資料,從而避免緩衝穿透。
解決緩衝雪崩問題的思路有下述幾種,在DB Connection上添加互斥鎖,這樣當大量緩衝請求失效的時候需要排隊去DB請求資料;對設定了相同到期時間的資料設定一個隨機值,避免資料集體失效;使用雙緩衝或者多層緩衝策略,需要配合緩衝預熱。
漫談使用Redis作為快取