大家好,我是北北。
在這篇文章中,我說要更新一個「Interview-oriented system design」系列。
目的就是讓大家知道系統設計都在設計個啥,怎麼用套路去解決面試中的問題。
建議大家也關注下這個公眾號,我會不定期更新在兩個公眾號。
不過目前「Java編程指北」的關注還不多,發的文章看的人也不多,暫時這篇就更新在「編程指北」了。
一、系統設計是什麼?一說到系統設計,可能大家腦子裡就冒出了高並發、微服務、負載均衡、分布式、集群、CAP、一致性哈希、.這樣高大上詞語。
我覺得系統設計就是如何把資源最優化配置,簡單來說就是你需要設計計算、存儲各個模塊,並且是在一定的限制條件下。
這個過程中,你需要設計系統的架構、模塊、接口和以及數據存儲方式。
系統設計中最核心的是什麼?
我個人認為是設計數據存儲和訪問方式。
為什麼?
「數據是架構的核心」,網際網路公司的主流業務,本質上就是一個數據處理系統:
咱們後臺同學平常自嘲是 CRUD Boy 也不是沒有道理的,本來就是用代碼對著數據存儲層一頓狂操作。
平時研發流程,也是先進行領域模型設計和數據模型設計,然後才是定義接口,最後才開發。
所以,充分理解數據系統的運作和設計非常必要, 我們常見的數據處理方式包括關係型資料庫、NoSQL、大數據存儲、流式數據存儲等,各有優缺點和適應的場景。
數據存儲使用不同的系統,那麼對應的業務邏輯實現也是有很大區別的。
比如訂單系統,這類系統一般都需要一個全局的 ID 生成器,如果訂單採用 MySQL 存儲的話,我們就需要考慮 ID 生成器的策略是隨機還是遞增的。
因為如果是隨機生成,那麼插入MySQL Innodb 表的時候,底層的 B+ 樹索引也許會發生頁分裂等問題,影響插入性能。
像電商如果遇到大促,短時間生成大量訂單,寫入就會成為瓶頸?
現在典型的數據模型有:
關係型:
基於關係模型,主要面向OLTP(on-line transaction processing在線事務處理),在線指的是比如銀行轉帳、商品下單這類實時流程操作。
要求嚴格的 ACID,支持事務。
典型的如網際網路公司最愛的MySQL、財大氣粗的三桶油銀行最愛的 Oracle等都是關係型數據系統的代表。
NoSQL(Not Only SQL):
這類系統以放寬 ACID 原則為代價,NoSQL採取的是最終一致性原則,而不是像關係型資料庫那樣地嚴格遵守著ACID的原則,這意味著如果在特定時間段內沒有特定數據項的更新,則最終對其所有的訪問都將返回最後更新的值。
這就是這樣的系統通常被描述為提供基本保證的原因(基本可用,軟狀態,最終一致性) — 而不是ACID。
NoSQL 的範圍很廣,理論上不是基於關係模型的數據存儲系統都可以叫做 NoSQL,比如列存儲 Hbase,圖存儲 Neo4J、對象存儲、KV存儲 Redis、Memcache等。
(關於數據存儲這方面推薦去看看 《ddia》 這本書,比較詳細的論述了各種數據存儲模型。
正是由於 MySQL 這類傳統關係型資料庫在面對網際網路公司海量的用戶請求時有各種各樣的瓶頸,比如:
高並發讀寫需求網站的用戶並發性非常高,往往達到每秒上萬次讀寫請求,對於傳統關係型資料庫來說,硬碟I/O是一個很大的瓶頸。海量數據的高效率讀寫網站每天產生的數據量是巨大的,對於關係型資料庫來說,在一張包含海量數據的表中查詢,效率是非常低的。而常見的解決思路也是垂直拆分或者水平拆分,用各種中間件去做 Sharding,即 單機RDBMS + 中間件,但是在中間件層是比較難解決分布式事務、高可用等問題的。
並且 Sharding 本身也是會帶來一堆問題的。
所以對於那種對 ACID 要求不是很嚴的場景,是有不少 NoSQL 系統可以取代的,比如 面向高性能並發讀寫的 key-value資料庫:Redis,Tokyo 等
面向海量數據訪問的面向文檔型資料庫:MongoDB
但是 NoSQL 還是不能完全取代 RDBMS,所以現在又有 NewSQL 出來了。
NewSQL 的設計架構基本是下面這些:
通過2PC,Paxos/Raft 等協議實現數據一致Google 算是這方面的鼻祖,後來很多的 NewSQL 資料庫基本都是按照 Spanner/F1 論文去實現的。
Google Spanner/F1學習的話,可以看國內 PingCAP 開源的 TIDB:
基本上就是底層使用 TiKV:
百度:TiKV 是一個開源的分布式事務 Key-Value 資料庫,專注為下一代資料庫提供可靠、高質量、實用的存儲架構。最初由 PingCAP 團隊在 2016 年 1 月作為 TiDB 的底層存儲引擎設計並開發。
大家感興趣可以去 TIDB 官網看看它的整體架構介紹:
https://docs.pingcap.com/zh/tidb/stable/tidb-architecture
感覺跑偏了,說到了分布式資料庫去了,這方面我也只是了解個大體,還是回到我們的系統設計上來。
二、系統設計的武器庫這一節就是簡單介紹下系統設計的一些」武器庫「,其實說白了就是,就是你要做去分布式、高可用、高性能.,都有哪些組件、方法論、設計套路可以選擇。
為什麼要先列出這些武器庫呢,因為大家都知道系統設計實際上是基於各種場景和給定資源下做 Trade-off,所以首先得知道都有哪些方案,從哪些角度去 Trade-off。
在這裡給大家總結了一個列表,大家可以對照的去了解這個技術點,基本上也是後端開發必備的一些分析系統和設計系統的技術棧:
2.1 估算:網絡、磁碟、IO等CPU/Memory/Network BandwidthRandom vs sequential read/write on disksDatacenters / Racks/ Hostshttp vs http2 vs web sockets首先,第一塊是硬體和資源的估算,這裡的硬體和資源比較廣義,包含了存儲、運算力、網絡等。
像 TCP/IP Model、TCP、UDP 這些都是必需要知道的東西,這樣才能選擇適合的網絡協議棧。
比如音視頻、直播、遊戲大多都是用的 UDP 來包一層可靠傳輸,這樣可以達到更高的網絡通信效率,因為 TCP 本身的慢啟動、避讓機制是對於整個網絡最優的,有些策略對於我們應用卻是不必要的,我們可以」自私「、」貪心「一點。
了解磁碟、文件、資料庫在順序寫和隨機寫上的速度差別,也可以給我上層應用實現的時候,
2.2 鎖、同步Multithreading, Concurrency, locks, Synchronization.Optimistic vs pessimist locking.應用系統最終都是處理數據的,如何保證數據被正確的處理,不會出現並發問題,這就是並發控制需要考慮的問題。
兩個典型的思想,樂觀和悲觀,對應於:
思想非常好理解, 悲觀鎖假定會發生並發衝突,屏蔽一切可能違反數據完整性的操作。
也就是說,悲觀鎖要求對數據的修改,都必須先搶到鎖。
悲觀鎖主要用於資源並發寫很多的情況。
而樂觀鎖假定不會發生並發衝突,只在提交操作時檢查是否違反數據完整性。
常見的操作就是利用版本號 version,做一個 CAS 操作。
比如對於 SQL 的話就是這樣:
UPDATE orders set status = "發貨", version = version + 1
WHERE version = 2 and order_id = xxxx;通俗點理解就是,我拿到數據的時候版本是 version,然後我在內存中做了一些操作,更新資料庫的時候,那麼必須滿足更新時的版本號也是 version,如果版本號和我持有的不一樣,那麼就是中途有其它人修改了。
那麼我這次更新就失敗,應該重新從資料庫取出最新數據進行操作。
2.3 分布式、事務相關理論Strong vs Eventual Consistency這塊就是分布式基礎理論、分布式事務,包括 CAP 理論、事務的 ACID等特性。
還有我們系統的數據是要求強一致性還是最終一致性呢?
數據一致性,簡單來理解,就是我們的業務一般都會涉及到多個系統,比如訂單、劵系統、帳務、支付等。
那麼一個操作跨系統的時候,比如轉帳、下單支付,如何保證一次業務操作跨系統實現原子性,也就是要麼都成功,要麼都不成功,不能出現部分成功這種,如下圖:
圖源:螞蟻金服大規模分布式事務實踐和開源介紹數據強一致性常見的解決方案又有 2PC,或者改進版本 3PC、TCC 等,這些分布式事務解決方案基本就是現成的,都是前人總結好的,我們只需要去學習、理解、應用就好了。
2.4 數據冗餘和拆分Vertical and Horizontal Scaling
Partitioning or Sharding data
這三點基本都在做一個事,就是數據的 Shading。
當我們的資料庫單機無法承受高強度的i/o時,就考慮利用 Sharding 來把這種讀寫壓力分散到各個主機上去。
而 Sharing 又分為水平拆分和垂直拆分,水平拆分就是將一個庫,分散為庫1、庫2:
一般拆分的依據就是根據一個 Sharding key,比如 用戶 ID,最簡單的取餘即可判斷某個用戶應該去哪個庫:
if ID % 3 == 0:
// database 1
elif ID % 3 == 1:
// database 2
else:
// database 3但是這樣的缺點就是,擴容或者宕機的時候遷移數據很麻煩,比如由於業務發展,我們需要增加兩臺機器作為 DB4和 DB 5,那麼這時候我們就應該對 5 取餘。
那麼之前對 3 做 Sharding 就會全部失效,所以我們需要對 DB1、DB2、DB3的所有數據進行重新 Sharding 到五臺 DB。
這樣的操作顯然不行,擴容和宕機時必須停服進行數據遷移,那有沒有一種更好的辦法,讓添加或者刪除 Sharding 節點對整個分片系統的數據遷移量降低呢?
那麼就引入了一致性哈希,一致性哈希主要就是解決宕機和擴容的問題。
具體什麼是一致性哈希,可以去網上看下博客,也很好理解。
2.5 高性能緩存應該是後臺最簡單、最直接的提高性能的方式之一了。
在計算機中,緩存是存儲數據的硬體或軟體組件,以便可以更快地滿足將來對該數據的請求。
存儲在緩存中的數據可能是之前計算結果,也可能是存儲在其他位置的數據副本。
緩存本質來說是使用空間換時間的思想,它在計算機世界中無處不在, 比如 CPU 就自帶 L1、L2、L3 Cache,這個一般應用開發可能關注較少。但是在一些實時系統、大規模計算模擬、圖像處理等追求極致性能的領域,就特別注重編寫緩存友好的代碼。
緩存之所以能夠大幅提高系統的性能,關鍵在於數據的訪問具有局部性,也就是二八定律:「百分之八十的數據訪問是集中在 20% 的數據上」。
這部分數據也被叫做熱點數據。
緩存一般使用內存作為存儲,內存讀寫速度快於磁碟,但容量有限,十分寶貴,不可能將所有數據都緩存起來。
如果應用訪問數據沒有熱點,不遵循二八定律,即大部分數據訪問並沒有集中在小部分數據上,那麼緩存就沒有意義,因為大部分數據還沒有被再次訪問就已經被擠出緩存了。每次訪問都會回源到資料庫查詢,那麼反而會降低數據訪問效率。
2.6 數據模型選型2.7 設計模式Design Pattern and Object Oriented design2.8 其它Long-Polling vs Websocket三、總結這裡只是簡單給大家列舉了一些技術點,大家可以對照著去了解這些技術是什麼?適用哪些場景?各自的優劣,我就不一一的介紹了。
大家如果要系統學習系統設計的話,可以去看下這門課,不過是英文的:
《Grokking the System Design Interview》
大家記得幫我一鍵三連噢,在看點一點,助理更新起來會更有動力的~