朱曄的互聯網架構實踐心得S1E10:數據的權衡和折騰【系列完】

朱曄的互聯網架構實踐心得S1E10:數據的權衡和折騰【系列完】算法

下載本文PDF進行閱讀數據庫

本文站在數據的維度談一下在架構設計中的一些方案對數據的權衡以及數據流轉過程當中的折騰這兩個事情。最後進行系列文章的總結和以後系列文章寫做計劃的一些展望。設計模式

數據的權衡

正所謂魚和熊掌不能兼得,舍了才能得。架構或技術設計方案中針對數據這個事情,有太多體現了權衡思想的地方。緩存

 

空間和時間

咱們來想一想有哪些廣義上空間換時間(性能)的例子,也就是經過使用更多的存儲或內存空間加快了任務的單次執行速度或整體吞吐量:安全

  • 讓數據在更快的地方:也就是緩存。速度和價格原本就是矛盾的,咱們不可能10萬買到百千米加速在4秒內的高性能跑車。存儲雖便宜可是速度慢,內存雖然貴可是速度快,使用級聯的緩存存儲方案咱們能夠在這當中作一個平衡。不只僅是架構設計上咱們幾乎都會用到緩存,CPU會有多級緩存,OS也有頁面緩存機制。
  • 讓數據一次性提交:也就是緩衝。在進行IO操做的時候,真正和磁盤和網絡交互以前,咱們每每都會創建緩衝區。在大多數的時候進行IO操做對於10字節和100字節的數據須要的IO時間是同樣的,咱們能夠在緩衝區進行短期的數據累積後一次性進行操做,這種作法不必定能提升單次執行性能可是能夠增長吞吐(對於繁忙的系統,吞吐達到瓶頸後單次的執行會排隊,因此反過來也能夠認爲提升單次性能)。
  • 讓數據更靠近用戶:CDN就是一個典型應用。讓數據離用戶更近意味數據不須要通過太多的機房和鏈路交換就能夠到達用戶終端,顯然能夠提升訪問性能。其實說白了就是讓數據在離用戶更近的地方緩存一份,在客戶端緩存也算。
  • 讓數據面向查詢存儲:相對於面向存儲優化。常見的存儲數據結構上,咱們知道寫入性能最好的是追加文件的日誌存儲,而後是LSM樹而後是B+樹,讀取性能則反過來。爲了性能咱們一般會在保存數據時候進行必定的排序分類而後按必定的數據結構保存,而不只僅是把原始信息存下來,這樣在查詢搜索的時候避免了數據全掃。這些特殊的(甚至有的時候是額外的)數據結構的維護也體現了空間換時間。
  • 讓數據面向輸出優化:以前說的物化視圖就是一個例子,在保存數據的時候直接保存咱們最終須要查詢的數據,這樣查詢的時候就不須要作各類關聯。在微博架構設計的時候咱們每每會更極端,爲每一個人維護一個信息流隊列,在發微博的時候直接爲全部的粉絲的隊列追加數據,這樣就避免了首頁查詢時候很是誇張的Join操做。
  • 讓數據複製多份:複製多份數據意味着咱們能夠有多個相同的數據源來爲讀取作服務。以前也提到過,雖然這是爲伸縮性考慮,可是必定程度上其實也能夠提升性能。
  • 讓數據計算默默進行:搞兩份數據,一份數據是用戶如今就在看的,另外一份數據是新版本的用戶將要看到的數據,經過在後臺另外一塊空間單獨處理這份新數據,處理完成後再展示給用戶就至關於用戶不須要花費時間等待數據的處理加工了。
  • 浪費一半的空間倒騰:這裏說的是相似於JVM的新生代的複製算法。始終有兩塊大小一致的倖存區,在須要回收的時候能夠將一個倖存區和伊甸園中的有用對象一次性搬到另外一塊區域中。雖然浪費了空間,可是這種每次都給我一張新的紙來畫圖的方式解決了碎片化的問題。

隨着存儲和帶寬的優化,互聯網架構更多考慮空間換時間,在有明顯帶寬和存儲瓶頸的視頻圖片等資源的傳輸上,咱們會採起壓縮的方式用時間來換空間。網絡

 

一致性和可用性

對於分佈式存儲,數據複製多份(分片)保存。在出現網絡故障的時候,節點之間的數據沒法一致,這個時候若是咱們追求數據一致性那麼就只能等待系統恢復再去使用這個系統,這個時候系統就不可用,固然咱們也能夠放棄一致性的追求先湊合用系統,這個過程當中節點之間的數據沒法確保一致。分佈式的狀況下受到外部客觀因素的限制,不可能二者都保證,咱們只能根據業務需求來決定放棄哪一個。有關CAP有不少討論,對於各類分佈式存儲也有按照CP(偏向於一致性)和AP(偏向於可用性)進行了分類,目前其實大多數的系統都把這個選擇權交給了客戶端交給了用戶,在使用數據的時候咱們能夠選擇是CP仍是AP,因此不能簡單認定某個存儲就是CP或AP的。數據結構

 

數據查詢的專有DSL(領域專用語言)和通用查詢語言

相似Mongodb、ElasticSearch、HBase等存儲系統都有本身的查詢DSL,關係型數據庫大多支持以SQL這種通用查詢語言進行數據查詢。ElasticSearch現已支持了SQL查詢,基於HBase也有不少組件提供SQL查詢(好比Phoenix)。咱們知道每一種存儲引擎都有其特色,專有DSL能夠作到讓你以引擎最合適的方式來作查詢,支持了通用的SQL後每每會產生濫用致使的性能問題,可是能夠帶來無比的便利性(語言和語言之間的翻譯是很痛苦的,並且這個翻譯每每是有損的)。在咱們本身作服務設計對外API,設計業務邏輯處理引擎的時候,其實也會遇到這樣的設計層次問題。咱們是提供專門的API來容許外部操做咱們的數據呢,仍是開發出一套DSL容許外部使用這套語言來作複合查詢,甚至直接支持相似SQL這種通用語言(直通數據庫)。好比在作風控規則引擎的時候,咱們能夠把每一條規則做爲寫死的代碼邏輯來寫,也能夠開發出一套DSL讓風控專家能夠配置數據字段和規則,甚至能夠直接使用SQL進行配置,開發報表系統、監控系統也是相似的道理。架構

 

數據的折騰

從數據的角度咱們來看看在互聯網系統中,數據到底經歷了什麼,體如今數據的存儲和流轉兩方面。就來講說用戶的註冊這一過程吧:框架

  1. 用戶填寫了註冊表單,產生了用戶名、密碼這一註冊數據
  2. Web網站服務端從HTTP(S)請求的Body中收到了Key=Value形式的數據
  3. 數據經過Thrift二進制協議編碼傳輸給了用戶服務(RPC調用),在這個過程當中其實只有 Value編碼了進去參與傳輸
  4. 在用戶服務服務端收到數據後根據接口IDL和數據拼在一塊兒反序列化讓不一樣的數據字段又有了含義
  5. 用戶服務把這個信息保存到了關係型數據庫MySQL(以MySQL二進制協議在網絡傳輸),數據成爲了一份RedoLog後以B樹形式數據結構在磁盤上存儲
  6. MySQL主庫又把數據以SQL語句(或數據行)的形式把數據複製到從庫
  7. 用戶服務隨後又把得到的用戶Id以及用戶的基本Profile信息以Key爲字符串Value爲Json文本的形式在Redis中進行緩存
  8. 用戶服務而後把有新用戶註冊這個事件做爲一個消息(Json序列化)推送到MQ Broker上
  9. (異步)紅包服務監聽了這個消息,在收到消息後從新把字節流轉換爲事件Json對象爲新用戶派發紅包
  10. (異步)風控服務也監聽了消息,從事件中提取出IP地址等信息,使用CEP復瑣事件處理技術對消息進行處理而後觸發報警
  11. (異步)大數據倉庫服務也監聽了消息,從數據庫查詢用戶完整的信息而且把數據保存到了HBase中以LSM樹存儲
  12. 在用戶服務完成處理後,Web網站也就知道了用戶註冊成功了告知用戶註冊成功的消息,而後用戶嘗試登錄,因爲主從同步的延遲,數據庫從庫沒有查詢到新註冊的用戶,用戶迷茫了,我註冊成功了但我是誰呢(這裏開個玩笑,這是一個設計錯誤,通常而言對於本身產生的數據的查詢原則是隻能在主庫查詢)

因此咱們看到,互聯網分佈式架構的複雜性在於一份數據可能會以不一樣的形式作轉換不一樣的通訊結構走傳輸,不用的數據結構作存儲,在每個環節,數據可能都以不一樣的形式體現,數據自己和數據表明的意義可能頭尾分離。若是咱們對程序內存作一份Dump,對各個環節的TCP包作幾份Dump,對數據最終落地的存儲作幾份Dump而後在這幾份二進制的Dump中找到而且看一下咱們的數據長啥樣,看看是否還能理解數據的意義,這是否是會是一件有趣的事情呢?運維

就這麼簡單的一份數據,爲何咱們要這麼折騰呢?緣由在於咱們不得不引入某個技術來解決處理某一方面問題的時候又引入了更多的複雜度,而後咱們又須要引入更多的技術來處理,循環往復:

  • 在單體架構中,咱們依靠數據庫提供的索引+事務能夠解決大部分的性能和一致性問題
  • 單體架構在性能、可擴展性、可靠性等方面不能知足需求,咱們引入了分佈式架構
  • 在分佈式架構中,咱們作了數據複製的分片,不但須要解決因網絡、時鐘、資源等問題致使的節點的協調,並且須要在空間分佈和時間錯位兩個層面去考慮以前已經解決的處理的很好的事務性問題
  • 咱們使用不一樣的數據庫做爲複合數據源來取長補短,不一樣的數據庫在分佈式架構實現上各不相同,咱們須要花大量時間來作高可用的調研,同時它們在存儲模型上大不相同,咱們須要進行深度研究瞭解存儲模型對於數據增量的性能衰減以及故障後的恢復等方面(諸如Mongodb、和ElasticSearch這種數據庫愈來愈有趨勢把本身搞成大而全面的數據存儲解決方案了,官方永遠宣傳的都是本身能幹啥而不是不能幹啥,至於不能幹啥只能靠本身去研究和總結)
  • 不一樣的網絡中間件都有各自不一樣的協議和通信方式,協議在處理先後兼容性方面各不相同,不一樣的語言對於基礎數據類型都有不一樣的處理方式,在語言和協議的交互轉化中也會有一些兼容性問題
  • 咱們使用各類方式來提升性能,數據會以不一樣的形態在多處保存,隨着時間的推移數據必然會產生不一致性,對於不一致的數據咱們如何發現,發現後去容忍仍是補償同步
  • 隨着組件的增多,咱們不但願組件的不穩定形成木桶效應,咱們會引入各類彈性的方案來容許組件的暫時不穩定,咱們不但願出了問題找不到來源,會引入各類監控手段來作全鏈路全組件監控
  • 數據每每不能徹底掌握在本身手裏,愈來愈多的外部三方服務會提供數據源以及垂直領域的解決方案,和外部系統進行交互保留了分佈式系統全部的困難以外,還須要考慮安全、隔離等問題

架構的複雜性在於數據,數據的複雜性在於多變的結構、數據的共享同步以及數據的增加。單機單用戶的軟件演變爲B/S形式的軟件演變爲SAAS形式的軟件,數據從單機到對內分佈式到在整個互聯網上造成分佈式愈來愈複雜。架構設計中可能一大半的時間在考慮數據的處理存儲問題。

 

系列文章總結

系列文章到這裏算是一個結束了,在本系列文章中咱們沒有過多涉及具體的技術和算法,咱們從高層架構的角度闡述了個人一些實用性經驗,力求在廣度上都有涉及:

  • 第一篇文章,我談了對All-In-One架構起步的見解,談了不一樣語言的選擇和技術團隊中業務和架構團隊的特性。
  • 第二篇文章,我談了我認爲互聯網架構中最重要的三要素,微服務+消息隊列+定時任務,消息隊列和定時任務其實體現的是數據實時流式處理和非實時批次處理的兩大流派。
  • 第三篇文章,我談了如何發揮不一樣類型數據庫(關係型數據庫MySQL、緩存型數據庫Redis、文檔型數據庫Mongodb、搜索型數據庫ElasticSearch、時間型數據庫InfluxDb)所長結合以前說的三要素作複合型數據源,固然還有更多類型的數據庫,好比圖數據庫等等在須要的時候也能夠引入作合適的業務。
  • 第四篇文章,我談了如何以開源的一些項目(ElasticSearch+Logstash+Kibana、Telegraf+InfluxDb+Grafana)快速搭建起簡單的監控、日誌和數據分析平臺以及闡述了對打點和異常兩個事情的見解。
  • 第五篇文章,我談了隨着項目的發展提煉打造合適的中間件的必要性,以及介紹了一些常見中間件(配置管理、服務管理、全鏈路監控、數據訪問、分佈式緩存、任務管理、發佈管理等)的需求功能以及設計上的注意點。
  • 第六篇文章,我談了架構升級遷移的步驟和注意點,以及開發人員比較容易忽略的安全方面的問題,我總結了我認爲比較重要的安全意識的十個原則(木桶效應、不信任客戶端、數據和代碼分清楚、用戶看不到不等於黑客看不到、最小化接口權限設計和複用的矛盾、一開始就要考慮安全、作好防刷防暴破控制、產品邏輯注意一體性、作好異常數據監控報警、對內的數據注意權限控制和審計)。
  • 第七篇和第八篇文章,我針對微軟分享的三十種雲架構設計模式(涉及監控、性能、可擴展、數據管理、設計實現、消息、彈性、安全等方面)給出了本身的見解。
  • 第九篇文章,我談了架構設計評審時候咱們針對組件選型、性能、可伸縮性、靈活性、可擴展性、可靠性、安全性、兼容性、彈性處理、事務性、可測試下、可運維性、監控等方面會一塊兒討論和提出的一些點,以及寫好一篇概要的技術文檔最主要的五個手段(需求腦圖、系統架構圖、對外API腦圖、交互時序圖和數據庫ER圖)。
  • 本文最後從架構中最重要的數據角度展開討論了架構中作的妥協權衡以及分佈式架構設計的無限複雜性。

 

我想了一下,有幾方面的內容能夠造成其它的系列文章和你們一塊兒探討,盡請期待:

  • Spring框架愈來愈完善了,龐大的產品體系和插件式的架構讓Java開發愈來愈快速和靈活了,在《你不知道的Spring》系列文章中咱們或許能夠聊一下Spring那些咱們不知道的點滴以及如何以Spring爲核心作一些擴展。
  • 對於分佈式架構中的數據複製和分片、高可用、一致性處理,每個產品都有其算法和思路,也有很是多的爭論,在《分佈式架構細節設計》系列文章中咱們或許能夠詳細針對微服務和分佈式架構具體的一些處理方式挖掘一些細節的點一一展開討論。
  • JVM和JDK通過這麼多年的發展,造成了一些設計模式和技巧,理解這些更多的意義在於咱們能夠在咱們的軟件設計和架構中借鑑,在《軟件內部設計模式》系列文章中能夠聊聊這部分的一些所見。
相關文章
相關標籤/搜索