消息隊列是在樂視這邊非常普遍使用的技術。在我們部門內部,不同的項目使用的消息隊列實現也不一樣。下麵是支付系統的流轉圖(部門兄弟畫的,借用一下): 從圖中可以看到,裡面用到了kafka消息隊列。作用是做資料庫分庫分表後的聚合,非同步彙總到一張總表。裡面也用到了redis,用來處理高併發下的訂單重覆提交。 ...
消息隊列是在樂視這邊非常普遍使用的技術。在我們部門內部,不同的項目使用的消息隊列實現也不一樣。下麵是支付系統的流轉圖(部門兄弟畫的,借用一下):
從圖中可以看到,裡面用到了kafka消息隊列。作用是做資料庫分庫分表後的聚合,非同步彙總到一張總表。裡面也用到了redis,用來處理高併發下的訂單重覆提交。我們這邊還使用了公司統一集群的apache qpid消息隊列,是AMQP的一個實現,主要用於不同部門間的通信。一般的大公司都會有一些公司統一的集群,但是這種統一集群對開發者來說相對透明,所以部門間相互合作的時候用的多,自己部門內部用,避免採坑,大家寧願自己搭一套。redis用處就更多了。阿裡的陽哥自己做了一個異常日誌監控平臺,主要就是用redis做數據傳輸和存儲。
別人做的東西我就不多說了。下午說說redis在我自己的框架中使用實戰。這是epiphany離線數據的流程圖。epiphany框架源碼地址:https://github.com/xiexiaojing/epiphany。我們部門內部使用實例地址是:https://github.com/xiexiaojing/epiphany-demo。大家可以將裡面的DAO部分數據做替換,替換成自己的資料庫隨便什麼數據即可運行。
從圖中可以看到處理過程基本都是在和redis打交道。Redis的基本數據結構是跳躍表。像這種跟存儲打交道的,數據結構是必須要瞭解的。比如lucene搜索最初的版本也是用的跳躍表,後來改成基於圖的有限自動機了。想瞭解具體瞭解跳躍表可以看我的另一篇文章《看Lucene源碼必須知道的基本規則和演算法》。像一些java寫的框架,比如dubbo,spring IoC里,一提到註冊,要註冊到一個地方,在JVM的數據結構一般是hashmap。準確的說:spring IoC里是通過一個hashmap來持有載入的BeanDefinition對象實現註冊的。
Redis持久化原理
Redis提供了兩種方式對數據進行持久化,分別是RDB(Redis DataBase)和AOF(APPEND ONLY FILE)。RDB持久化方式能夠在指定的時間間隔對數據進行快照存儲。AOF持久化方式記錄每次伺服器寫的操作,當伺服器重啟的時候會重新執行這些命令來恢複原始的數據,AOF命令以redis協議追加保存每次寫操作到文件末尾。Redis還能對AOF文件進行後臺重寫,使得AOF文件的體積不至於過大。不過,我問過很多部門,出於性能考慮,他們的持久化都是不開啟的。如果同時開啟兩種持久化方式,當redis重啟的時候會優先載入AOF文件來恢複原始的數據,因為在通常情況下AOF文件保存的數據集要比RDB文件保存的數據集要完整。
瞭解一下持久化的C語言實現。Redis需要之執行RDB的時候,伺服器會執行以下操作:redis調用系統函數fork(),創建一個子進程。子進程將數據集寫入到一個臨時RDB文件中。當子進程完成對臨時RDB文件的寫入時,redis用新的臨時RDB文件替換原來的RDB文件,並刪除舊RDB文件。在執行fork時linux操作系統(一般大公司的伺服器都是這個系統)會使用寫時複製(copy-on-write)策略,即fork函數發生的一刻父子進程共用同一記憶體數據,當父進程要更新其中某片數據時,操作系統會將該片數據複製一份以保證子進程的數據不收影響,所以新的RDB文件存儲的是之執行fork那一刻的記憶體數據。RDB文件是經過壓縮的二進位格式,所以占用的空間會小於記憶體的數據大小。但是壓縮操作很占CPU,所以可以通過配置文件配置禁止壓縮。
瞭解一下對應的redis命令。除了自動快照,還可以手動發送save或者bgsave命令讓redis直行快照。save命令是在主進程上進行的,會阻塞其他請求。後者會fork子進程進行快照操作。
和mysql存儲比較。RDB方式比較類似於mysql的mysqldump命令備份。而AOF更接近於binlog。
Redis記憶體優化
redis配置文件中有個maxmemory參數設置,如果沒有設置會繼續分配記憶體,因此可以逐漸吃掉所有可用記憶體。因此,通常建議配置一些限制和策略。這樣做的優點是:不會導致因為記憶體饑餓而整機死亡。缺點是:Redis可能會返回記憶體不足的錯誤寫命令。redis有6種過期策略。
1>volatile-lru:只對設置了過期時間的key進行LRU
2>allkeys-lur:對所有的key進行LRU
3>volatile-random:隨機刪除即將過期的key
4>allkeys-random:從所有的key中隨時刪除
5>volatile-ttl:刪除即將過期的,ttl(tiime to live)剩餘生存時間
6>noeviction:永不過期,返回錯誤
參數的設置可以採用命令方式,也可以採用配置文件方式(所有的配置都支持這兩種),配置命令如
config set maxmemory-policy volatile-lru
還可以設置隨機抽樣數,如
config set maxmemory-samples 5 就是說每次進行淘汰的時候,會隨機抽取5個key從裡面淘汰最不經常使用的。
redis壓縮列表(ziplist)。壓縮列表是列表鍵和哈希鍵的底層實現之一。當一個列表鍵只包含少量表項,並且每個列表要麼是小整數,要麼是較短的字元串,那麼redis就會使用壓縮列表來作為列表鍵的底層實現。當一個哈席鍵只包含少量key-value對,且每個key和value要麼是小整數,要麼是較短字元串,那麼redis就會使用ziplist作為哈希鍵的底層實現。
我在介紹自己的epiphany框架的時候(在上面流程圖裡也有體現),如果一個key里的結構是個hash,在小於1k的hash鍵的情況下我直接用hash,而大於1k,考慮到寫入性能差,我就直接將hash打包壓縮成一個大value來存儲。考慮使用這兩種策略的其中一個原因是小散列表使用的記憶體非常小,節省存儲空間。
跑題時間:
這幅畫的名字叫《洗盡鉛華》