用最少的机器支撑万亿级访问,微博6年Redis优化历程
https://mp.weixin.qq.com/s?__biz=MzAwMDU1MTE1OQ==&mid=2653547263&idx=1&sn=fe484b24660b7e1dc4beabca71fe1cb1&scene=21#wechat_redirect
微博是從 2010 年開始引入 ?Redis ,現在 Redis 已經廣泛應用于微博的多個業務場景,如關系、計數、通知提醒等,目前 Redis 集群存儲超過百億記錄,每天上萬億的讀取訪問。隨著業務的快速發展,我們在使用過程中碰到的問題及解決方法給大家做一個分享。主要包括以下方面: 實現機制高可用、業務極致定制以及服務化。
?
Redis 2.0 時代(2010 - 2011)
實現機制高可用優化
?
微博最早使用的是 Redis 2.0 版本,在初期業務規模不大的時候, Redis 服務運行比較穩定。但是隨著業務數據量和訪問量的增加,一些問題逐漸暴露出來:
?
持久化問題
?
在我們大多數業務場景中 Redis 是當做存儲來使用,會開啟持久化機制。線上采用單機多實例的部署結構,服務器的內存使用率也會比較高。由于官方版本觸發?bgsave?和?bgrewriteaof?操作的時間點是不可控的,依賴于相關的配置項和業務的寫入模型,因此可能會出現單機部署的多個 Redis 實例同時觸發?bgsave?或?bgrewriteaof?操作,這兩個操作都是通過 fork 出一個子進程來完成的,由于 copy-on-write 機制,可能會導致服務器內存很快耗盡, Redis 服務崩潰。
?
此外在磁盤壓力較大時(生成 rdb、aof 重寫),對 aof 的寫入及 fsync 操作可能會出現阻塞,雖然從 2.4 版本開始 fsync 操作調整到 bio 線程來做,主線程 aof 的寫入阻塞仍會導致服務阻塞。
?
主從同步問題
?
為了提高服務可用性,避免單點問題,我們線上業務 Redis 大多采用主從結構部署。官方版本的主從同步機制,在網絡出現問題時(如瞬斷),會導致主從重新進行一次全量復制。對單個端口來說,如果數據量小,那么這個影響不大,而如果數據量比較大的話,就會導致網絡流量暴增,同時 slave 在加載 rdb 時無法響應任何請求。當然官方 2.8 版本支持了 psync 增量復制的機制,一定程度上解決了主從連接斷開會引發全量復制的問題,但是這種機制受限于復制積壓緩沖區大小,同時在主庫故障需要執行切主操作場景下,主從仍然需要進行全量復制。
?
版本升級及管理問題

早期 Redis 版本運行不夠穩定,經常需要修復 bug、支持新的運維需求及版本優化,導致版本迭代很頻繁。官方版本在執行升級操作時,需要服務重啟,我們大多數線上業務都開啟了持久化機制,重啟操作耗時較長,加上使用 Redis 業務線比較多,版本升級操作的復雜度很高。由于統一版本帶來的運維工作量實在太高,線上 Redis 版本曾經一度增加到十幾個,給版本管理也帶來很大的困難。
?
為了解決以上問題我們對 Redis 原生實現機制做了以下優化:
?
1. 對于持久化機制,采用 rdb + aof 的持久化方式。
?
aof 文件按固定大小滾動,生成 rdb 文件時記錄當前 aof 的 position,全量的數據包含在 rdb 和所記錄位置點之后的 aof 文件,廢棄 aof 重寫機制,生成 rdb 后刪除無效的 aof 文件;增加了定時持久化操作的配置項 cronsave,將單機部署的多個 Redis 實例的持久化操作分散在不同的時間點進行,并且錯開業務高峰;將對 aof 的寫入操作也放到 bio 線程來做,解決磁盤壓力較大時 Redis 阻塞的問題。
?
2. 對于主從同步機制,借鑒 MySQL 的復制機制并做了簡化。
?
使用 rdb + aof 的方式,支持基于 aofpositon 的增量復制。從庫只需與主庫進行一次全量同步同步,后續主從連接斷開或切主操作,從庫都是與主庫進行增量復制。
?
?
對于版本升和管理級的問題, Redis 的核心處理邏輯封裝到動態庫,內存中的數據保存在全局變量里,通過外部程序來調用動態庫里的相應函數來讀寫數據。版本升級時只需要替換成新的動態庫文件即可,無須重新載入數據。通過這樣的方式,版本升級只需執行一條指令,即可在毫秒級別完成代碼的升級,同時對客戶端請求無任何影響。
?
除了以上幾點,也做了很多其它的優化,如主從延遲時間檢測,危險命令認證等。通過逐步的優化,內部的 Redis 版本也開始進入穩定期,應用規模也在持續的增加。
?
業務極致定制化時代(2012 - 2013)
RedisCounter / LongSet
?
在某些特定的業務場景下,隨著業務規模的持續增加, Redis 的使用又暴露出來一些問題,尤其是服務成本問題(小編:是省服務器的意思?)。為此結合特定的業務場景我們對 Redis 做了一些定制的優化。這里主要介紹一下在關系和計數兩個業務場景下做的定制優化。
?
-
關系
?
微博關系業務包含添加、取消關注,判斷關注關系等相關的業務邏輯,引入 Redis 后使用的是 hash 數據結構,并且當作存儲使用。但是隨著用戶規模的快速增長,關系服務 Redis 容量達到十幾 TB,并且還在快速的增長,如何應對成本壓力?
?
為了解決服務成本問題,我們把 Redis 的角色由 storage 調整為 cache。
?
這是因為隨著用戶數量的增長,業務模型由初期的熱點數據不集中已經轉變為有明顯的冷熱之分。對于關注關系變更、判斷關注關系,hash 數據結構是最佳的數據結構,但是存在以下問題:
?
cache miss 后回寫關注列表性能差,對于關注數較多的微博會員,回寫操作耗時可達到 10ms,這對于單線程的 Redis 來說是致命的;
Redis hash 結構的內存使用率不高,要保證 cahce 的命中率所需的 cache 容量仍然是很大的。
?
于是,我們定制了 longset 數據結構,它是一個“固定長度開放尋址的 hash 數組”,通過選擇合適的 hash 算法及數組填充率,可實現關注關系變更及判斷的性能與原生 Redis hash 相當,同時 cache miss 后通過 client 重建 longset 結構,實現 O(1) 復雜度回寫。
?
通過定制 longset 數據結構,將關系 Redis 內存占用降低了一個數量級(小編:這該節約了多少服務器……發獎金了嗎?),同時保證了服務性能。
?
計數
?
微博有很多計數場景,如用戶緯度的關注數、粉絲數,微博緯度的轉發數、評論數等。計數作為微博中一項很重要的數據,在微博業務中承擔了很重要的角色。為更好的滿足計數業務需求,我們基于 Redis 定制了內部的計數服務。
?
原生 Redis 為了支持多數據類型,需要維護很多指針信息,存儲一份業務計數要占到約 80 個字節,內存利用率很低。為此我們定制了第一版計數器 Redis counter,通過預先分配內存數組存儲計數,并且采用 doublehash 解決沖突,減少了原生 Redis 大量的指針開銷。通過以上優化將內存成本降低到原來的 1/4 以下。(小編:又節約了 3 / 4 服務器……)
?
隨著微博的發展,微博緯度的計數不斷增加,在原來的轉發數、評論數基礎上,又增加了表態數,2013 年還上線了閱讀數。 Redis counter 已不能很好的解決這類擴展問題:
?
存儲單條微博相關的計數,需要重復存儲微博 mid 信息,并且數據全部存儲在內存,服務成本較高;
獲取單條微博全部的計數,需要調用多次計數接口,對服務端壓力很大。
?
為此我們又設計了改進版的計數器 CounterService,增加如下特性:
?
-
Schema 支持多列:支持動態加列,內存使用精簡到 bit
-
冷熱數據分離:頻繁訪問的熱數據存儲在 memory,訪問較少的冷數據存儲在磁盤,降低服務成本
-
LRU 緩存冷數據:增加 LRU 模塊,緩存訪問到的冷數據,保證冷數據的訪問性能。
-
異步 IO 線程訪問冷數據:避免冷數據的訪問影響服務的整體性能
?
通過以上的定制優化,我們從根本上解決了計數業務的成本及性能問題。
?
除了以上關系、計數業務場景的定制優化,為了滿足判斷類業務場景需求,定制了 BloomFilter 服務;為了滿足 feed 聚合業務場景需求,定制了 VerctorService 服務;為了降低服務成本,定制了 SSDCache 服務等。(小編:老板感動得流淚了)
?
服務化時代(2014 -)
Cache Service、SSD Cache
?
隨著微博業務的快速增長,Redis 集群規模也在持續增加,目前微博 Redis 集群內存占用數十 TB,服務于數百個業務線,Redis 集群的管理依然面臨很多的問題。
?
數據遷移問題
?
隨著時間推移,越來越多的業務由于數據量的增加,單端口到內存占用已經達到上限,微博內部建議單端口內存不超過 20GB,因此需要重新拆分端口,這就涉及到數據遷移,目前遷移操作是通過內部開發的一個遷移工具來完成的,遷移操作的成本相對較高。
?
數據路由問題
?
目前的使用方式,需要在業務代碼中實現數據路由規則,路由規則的變更需要重新上線代碼,業務變更復雜度較高。同時節點配置采用 DNS 的方式,存在實時性和負載不均的問題,雖然使用過程中有對應的解決策略,但是需要一定的運維干預,運維復雜度較高。
?
HA 系統不成熟
?
當前的 HA 系統更多的是采用自動發現問題,手動確認處理的策略,沒有實現真正意義的自動化,運維成本依然很高。
?
為了解決以上問題,我們在 Redis 基礎上實現服務化框架 CacheService。
?
CacheService 最早是為了解決內部使用 memcached 遇到的問題而開發的服務化框架,主要包含以下幾個模塊:
?
-
配置中心 ConfigServer
?
微博內部的配置服務中心,主要是管理靜態配置和動態命名服務的一個遠程服務,并能夠在配置發生變更的時候實時通知監聽的 ConfigClient。
?
-
資源層
?
實際的數據存儲引擎,初期支持 memcached,后續又擴展了 Redis、SSDCache 組件,其中 SSDCache 是為了降低服務成本,內部開發的基于 SSD 的存儲組件,用于緩存介于 memory 和 DB 之間的 warm 數據。
?
-
代理層
?
代理業務端的請求,并基于設定的路由規則轉發到后端的 cache 資源,它本身是無狀態的。proxy 啟動后會去從 ConfigServer 加載后端 cache 資源的配置列表進行初始化,并接收 ConfigServer 的配置變更的實時通知。
?
-
客戶端
?
提供給業務方使用的 SDK 包,通過它不需要在業務代碼中實現數據路由規則,業務方也無需關心后端 cache 的資源。只需要簡單配置所使用的服務池名 group 和業務標識 namespace 即可使用 cache 資源,client 從 ConfigServer 獲取 proxy 的節點列表,選擇合適的 proxy 節點發送請求,支持多種負載均衡策略,同時會自動探測 proxy 節點變更。
?
-
集群管理系統 ClusterManager
?
管理集群中各個組件的運行狀態以保證業務的 SLA 指標,當出現異常時會自動執行運維處理。同時配置變更、數據遷移等集群操作也都是由它來負責。
?

為支持 Redis 服務化,在服務化框架擴展支持了 Redis proxy,同時為了實
現在線數據遷移,參照 Redis cluster 的設計思想,對內部 Redis 存儲做了改造,支持 slot 數據分片,數據遷移操作由 ClusterManager 組件執行,完成 slot 的重新規劃及數據遷移。此外還支持 Redis 的 failover 機制,在master 或 slave 節點故障時會自動執行容錯處理。我們 Redis 服務化項目 tribe 是從 2015 年底開始上線,處于逐步完善過程中。
?
總結
?
從對 Redis 的優化歷程可以看出,技術的進步是由業務的需求推動的,我們需要擁抱需求。同時對于一個服務我們需要持續優化并保證服務的運維友好性才能保證服務的生命力。后續的一些計劃,完善服務化體系中冷熱數據分級存儲機制以降低服務成本;引入新的組件以更好的滿足業務需求、進一步完善集群管理組件降低運維復雜度
?
轉載于:https://www.cnblogs.com/davidwang456/articles/8360779.html
總結
以上是生活随笔為你收集整理的用最少的机器支撑万亿级访问,微博6年Redis优化历程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Lambda架构与推荐在电商网站实践
- 下一篇: 互联网主要安全威胁解读及应对方案大讨论