《高併發系統實戰課》學習筆記 day1 精簡數據會有更好的性能 數據的歸類和整理 實體輔助表 實體關係表

任何老系統在做高併發改造的時候都要先做一次表的梳理
因爲老系統在使用數據庫的時候存在很多問題,比如實體表字段過多,表查詢維度和用途多樣,表之間關係混亂,這些問題會讓緩存改造十分困難,嚴重拖慢改造進度。

精簡數據會有更好的性能

用戶中心的主要功能是維護用戶信息,用戶權限和登錄狀態,它保存的數據大部分都屬於讀多寫少的數據。用戶中心常見的優化方式主要是將用戶中心和業務徹底拆開,不再與業務耦合,並適當增加緩存來提高系統性能。


對錶的功能和字段進行了業務解耦和精簡,他的核心是,長度小的數據在吞吐,查詢,傳輸上都會很快,也會更好的管理和緩存。
精簡後的表擁有更少的字段,對應的業務用途也會比較單純。
不過需要注意,精簡數據量雖然能換來更好的響應速度,但是不提倡過度設計,因爲表字段如果缺少冗餘會導致業務實現更爲繁瑣。
因此我們需要在更多的字段和更少的職能之間找到平衡。

數據的歸類和整理

除了通過精簡表的職能來提高表的性能和維護性外,我們還可以針對不同類型的表做不同方向的緩存優化,如下圖


數據主要有四種,實體對象主表,輔助查詢表,實體關係和歷史數據,不同類型的數據所對應的緩存策略是不同的,如果將一些職能拆分不清楚的數據硬是放在緩存中,使用的時候就會碰到很多燒腦的問題。

將歷史記錄和需要實時更新的好友狀態混在一起,顯然不合理,如果我們做歸類梳理的話,應該拆分成三個職能表,分別進行管理。
1,歷史記錄表,不做緩存,僅僅展示最近幾條,極端情況臨時緩存
2,好友關係(緩存關係,用於統計有幾個好友)
3,來訪統計數字。
因此需要明白數據歸類的重要性。
數據實體表
先看下用戶賬號表,這個表是一個實體表,實體表一般作爲主表,他的一行數據代表一個實體,每個實體都擁有一個獨立且唯一的ID作爲標識。其中,“實體”代表一個抽象的事務,具體的字段表示的是當前實體實時的狀態屬性。
這個ID對於高併發環境下的緩存很重要,用戶登錄後就需要用自己的賬戶的ID直接查找到對應的訂單,暱稱頭像和好友列表信息。如果我們的業務都是通過這樣的方式查找,性能肯定很好,並且很適合做長期緩存。
但是除了按ID查詢外,還有一些是需要通過組合條件查詢的,比如:

在 7 月 4 日下單購買耳機的訂單有哪些?
天津的用戶裏有多少新註冊的用戶?有多少老用戶?
昨天是否有用戶名前綴是 rick 賬戶註冊?

這種根據條件查詢統計的數據是不太容易做緩存的,因爲高併發服務緩存的數據通常是能夠快速通過Hash直接匹配的數據,而這種帶條件查詢統計的數據很容易出現不一致,數據量不確定導致的性能不穩定等問題,並且如果涉及的數據出現變化,我們很難通過數據確定同步更新那些緩存。
因此,這類數據只適合存在關係數據庫或提前預置計算好結果放在緩存中直接使用,做定期更新。
除了組合條件查詢不好緩存外,像count(),sum()等對數據進行實時計算也有更新不及時的問題,同樣只能定期緩存彙總結果,不能頻繁查詢。所以我們應該在後續的開發過程中儘量避免使用數據庫進行計算。
如果這種查詢的頻率比較高,就會嚴重影響其他用戶的登陸,而且新增的暱稱索引還會額外降低當前表插入數據的性能,這也是爲什麼我們的後臺系統往往會單獨分出一個從庫,做特殊索引。
一般來說,高併發用緩存來優化讀取的性能時,緩存保存的基本都是實體數據。那常見的方法是先通過“key 前綴 + 實體 ID”獲取數據(比如 user_info_9527),然後通過一些緩存中的關聯關係再獲取指定數據,比如我們通過 ID 就可以直接獲取用戶好友關係 key,並且拿到用戶的好友 ID 列表。通過類似的方式,我們可以在 Redis 中實現用戶常見的關聯查詢操作。總體來說,實體數據是我們業務的主要承載體,當我們找到實體主體的時候,就可以根據這個主體在緩存中查到所有和它有關聯的數據,來服務用戶。現在我們來稍微總結一下,我們整理實體表的核心思路主要有以下幾點:

精簡數據總長度
減少表承擔的業務職能
減少統計計算查詢
實體數據更適合放在緩存中;
儘量讓實體能夠通過ID或關係方式查找。
減少實時條件篩選方式的對外服務。

下面我們繼續來看另外三種表結構,你會發現它們不太適合放在緩存中,因爲維護它們的一致性很麻煩。

實體輔助表

爲了精簡數據且方便管理,我們經常會根據不同用途對主表拆分,常見的方式是做縱向表拆分
縱向表拆分的目的一般是2個,一個是把使用頻率不高的數據摘出來。常見的表字段很多,經過拆分,可以精簡他的職能,而輔助表的主鍵通常會和主表一致或通過ID進行關聯,他們之間的常見關係是1:1

而放到輔助表的數據,一般是主要業務查詢中不會使用的數據,這些數據只有在極個別的場景下才會取出使用,比如用戶賬號表爲主體用於做用戶登陸使用,而輔助信息表保存家庭住址、省份、微信、郵編等平時不會展示的信息。

輔助表的另外一個用途是輔助查詢,當原有的業務數據不能夠滿足其他維度的實體查詢時,可以通過輔助表來實現。
比如有一個表是以“教師”爲主體設計的,每次業務都會根據“當前教師 ID+ 條件”來查詢學生及班級數據,但從學生的角度使用系統時,需要高頻率以“學生和班級”爲基礎查詢教師數據時,就只能先查出 “學生 ID”或“班級 ID”,然後才能查找出老師 ID”,這樣不僅不方便,而且還很低效,這時候就可以把學生和班級的數據拆分出來,額外做一個輔助表包含所有詳細信息,方便這種查詢。另外,我還要提醒一下,因爲拆分的輔助表會和主體出現 1:n 甚至是 m:n 的數據關係,所以我們要定期地對數據整理覈對,通過這個方式保證我們冗餘數據的同步和完整。不過,非 1:1 數據關係的輔助表維護起來並不容易,因爲它容易出現數據不一致或延遲的情況,甚至在有些場景下,還需要刷新所有相關關係的緩存,既耗時又耗力。如果這些數據的核對通過腳本去定期執行,通過覈對數據來找出數據差異,會更簡單一些。此外,在很多情況下我們爲了提高查詢效率,會把同一個數據冗餘在多個表內,有數據更新時,我們需要同步更新冗餘表和緩存的數據。這裏補充一點,行業裏也會用一些開源搜索引擎,輔助我們做類似的關係業務查詢,比如用 ElasticSearch 做商品檢索、用 OpenSearch 做文章檢索等。這種可橫向擴容的服務能大大降低數據庫查詢壓力,但唯一缺點就是很難實現數據的強一致性,需要人工檢測、覈對兩個系統的數據。

實體關係表


在關係型數據中,可以額外用一個關係表來記錄實體間m:n的關聯關係,這樣2個實體就不用因爲相互依賴關係,導致難以維護。
在對1:n或者m:n關係的數據做緩存時,提前預估好可能參與的數據量 ,防止過大導致緩存緩慢。同時,通常保存這個關係在緩存中會把主體的ID作爲key,在value內保存多個關聯ID來記錄這2個數據的關聯關係,而對於讀取特別頻繁的業務緩存,纔會考慮把數據先按關係組織好,然後整體緩存起來,來方便查詢和使用。
需要注意的是,這種關聯數據很容易出現多級依賴,會導致我們整理起來十分麻煩。當相關表或者條件更新時,我們需要及時同步這些數據在緩存中的變化。所以,這種多級依賴關係很難在併發高的系統中維護,很多時候需要降低一致性的要求來滿足業務的高併發情況。
總的來說,只有通過ID進行關聯的數據的緩存時最容易管理的,其他的都需要特殊維護。
現在我們簡單總結一下,到底什麼樣的數據適合做緩存。一般來說,根據 ID 能夠精準匹配的數據實體很適合做緩存;而通過 String、List 或 Set 指令形成的有多條 value 的結構適合做(1:1、1:n、m:n)輔助或關係查詢;最後還有一點要注意,雖然 Hash 結構很適合做實體表的屬性和狀態,但是 Hgetall 指令性能並不好,很容易讓緩存卡頓,建議不要這樣做。
總結在項目初期,數據表的職能設計往往都會比較簡單,但隨着時間的推移和業務的發展變化,表經過多次修改後,其使用方向和職能都會發生較大的變化,導致我們的系統越來越複雜。所以,當流量超過數據庫的承受能力需要做緩存改造時,我們建議先根據當前的業務邏輯對數據表進行職能歸類,它能夠幫你快速識別出,表中哪些字段和功能不適合在特定類型的表內使用,這會讓數據在緩存中有更好的性價比。一般來說,數據可分爲四類:實體表、實體輔助表、關係表和歷史表,而判斷是否適合緩存的核心思路主要是以下幾點:能夠通過 ID 快速匹配的實體,以及通過關係快速查詢的數據,適合放在長期緩存當中;通過組合條件篩選統計的數據,也可以放到臨時緩存,但是更新有延遲;數據增長量大或者跟設計初衷不一樣的表數據,這種不適合、也不建議去做做緩存。


學習來源:極客時間

https://mp.weixin.qq.com/s/kQaIdmgSkOnhOT4bVGrGhw

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章