自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

入職第一天,MySQL就崩了...

數(shù)據(jù)庫 MySQL
前段時間我離職了,因為個人原因,在修整一段時間后,重新入職了一家新公司。

[[399633]]

圖片來自 Pexels

入職的第一天我就經(jīng)歷了一次生產(chǎn)事故,運維同學(xué)告警說線上 MySQL 負(fù)載壓力大,直接就把主庫 MySQL 壓崩了(第一天這可不是好兆頭)。

運維同學(xué)緊急進(jìn)行了主從切換,在事后尋找導(dǎo)致生產(chǎn)事故的原因時,排查到是慢查詢導(dǎo)致 MySQL 雪崩的主要原因。

在導(dǎo)出慢查詢的 SQL 后,項目經(jīng)理直接說吧這個 MySQL 優(yōu)化的功能交給新來的吧!

我趕緊打開跳板機(jī)進(jìn)行查看,不看不知道一看嚇一跳:

單表的數(shù)據(jù)量已經(jīng)達(dá)到了 5 億級別!這尼瑪肯定是歷史問題一直堆積到現(xiàn)在才導(dǎo)致的啊,項目經(jīng)理直接就把這個坑甩給了我,我心中想,我難道試崗期都過不了么????

好在我身經(jīng)百戰(zhàn),趕快與項目經(jīng)理與老同事進(jìn)行溝通,了解業(yè)務(wù)場景,才發(fā)現(xiàn)導(dǎo)致現(xiàn)在的情況是這樣的。

我所在的公司是主要做 IM 社交系統(tǒng)的,這個 5 億級別的數(shù)據(jù)表是關(guān)注表,也是俗稱的粉絲表,在類似與某些大 V、或者是網(wǎng)紅,粉絲過百萬是非常常見的。

在 A 關(guān)注 B 后會產(chǎn)生一條記錄,B 關(guān)注 A 時也會產(chǎn)生一條記錄,時間積累久了才達(dá)到今天這樣的數(shù)據(jù)規(guī)模,項目經(jīng)理慢悠悠的對我說,這個優(yōu)化不用著急,先出方案吧!

我心中一萬個草泥馬經(jīng)過,這上來就給了一塊不好啃的骨頭,看來是要試試我能力的深淺啊。

按照我之前經(jīng)驗,單表在達(dá)到 500W 左右的數(shù)據(jù)就應(yīng)該考慮分表了,常見分表方案無非就是 hash 取模,或者 range 分區(qū)這兩種方法。

但是這次的數(shù)據(jù)分表與遷移過程難度在于兩方面:

  • 數(shù)據(jù)平滑過度,在不停機(jī)的情況把單表數(shù)據(jù)逐步遷移。(老板說:敢宕機(jī)分分鐘損失幾千塊,KPI 直接給你扣成負(fù)的)
  • 數(shù)據(jù)分區(qū),采用 hash 還是 range?(暫時不能使用一些分庫分表中間件,無奈)

首先說說 hash:

常規(guī)我們都是拿用戶 id 進(jìn)行取模,模到多少直接把數(shù)據(jù)塞進(jìn)去就行了,簡單粗暴。

但是假如說 user_id=128 與 user_id=257 再模 128 后都是對應(yīng) user_attention_1 這個表,他倆也恰好是網(wǎng)紅,旗下粉絲過百萬,那輕輕松松兩個人就能把數(shù)據(jù)表撐滿。

其他用戶再進(jìn)來數(shù)據(jù)的時候無疑 user_attention_1 這個表還會成為一張大表,這就是典型的數(shù)據(jù)熱點問題,這個方案可以 PASS。

有的同學(xué)說可以 user_id 和 fans_id 組合進(jìn)行取模進(jìn)行分配,我也考慮過這個問題。

雖然這樣子數(shù)據(jù)分配均勻了,但是會有一個致命的問題就是查詢問題(因為目前沒有做類似 MongoDB 與 DB2 這種高性能查詢 DB,也沒做數(shù)據(jù)同步,考慮到工作量還是查詢現(xiàn)有的分表內(nèi)的數(shù)據(jù))。

例如業(yè)務(wù)場景經(jīng)常用到的查詢就是我關(guān)注了那些人,那些人關(guān)注了我,所以我們的查詢代碼可能會是這樣寫的:

  1. //我關(guān)注了誰 
  2. select * from user_attention where user_id = #{userId}  
  3.  
  4. //誰關(guān)注了我 
  5. select * from user_attention where fans_id = #{userId} 

在我們進(jìn)行 user_id 與 fans_id 組合后 hash 后,如果我想查詢我關(guān)注的人與誰關(guān)注我的時候,那我將檢索 128 張表才能得到結(jié)果。

這個也太惡心了,肯定不可取,并且考慮到以后擴(kuò)容至少也要影響一半數(shù)據(jù),實在不好用,這個方案 PASS。

接下來說說 range:

Range 看起來也很簡單,用戶 id 在一定的范圍時候就把他路由到一個表中。

例如用戶 id=128,那就在 [0,10000] 這個區(qū)間中對應(yīng)的是 user_attention_0 這個表,就直接把數(shù)據(jù)塞進(jìn)去就可以了。

但是這樣同樣也會產(chǎn)生熱點數(shù)據(jù)問題,看來簡單的水平分區(qū)已經(jīng)不能滿足,這個方案也可以 PASS 了,還是要另尋他徑啊。

經(jīng)過我日夜奮戰(zhàn),深思熟慮之后,給出了三個解決方案:

第一種方案:range+一致性 hash 環(huán)組合(hash 環(huán)節(jié)點 10000)

什么是 hash 環(huán)看這里:

想采用這個方案主要是因為:

  • 擴(kuò)容簡單,影響范圍小,只涉及 hash 環(huán)上單個節(jié)點影響。
  • 數(shù)據(jù)遷移簡單,每次擴(kuò)容只需吧新增的節(jié)點與后置節(jié)點進(jìn)行數(shù)據(jù)交互。
  • 查詢范圍小,按照 range 與 hash 關(guān)系檢索部分表分區(qū)。

大概思路我們還是先按照 user_id 進(jìn)行大概范圍劃分,但是 range 之后我后面對應(yīng)的可能就不是一個表了,而是一個 hash 環(huán)。

在每個 range 區(qū)域后都對應(yīng)著自己一套的環(huán),我們可以根據(jù)實際情況進(jìn)行擴(kuò)容,比如在 [1,10000] 這個范圍內(nèi)只有 2 個大 V,那我們分三個表就夠了,預(yù)留 1500 萬的數(shù)據(jù)容量。

[10001,20000] 中有 4 個網(wǎng)紅和大 V,hash 環(huán)上就給出實際 4 張表,我們的用戶 id 可以順時針順序坐落到第一個物理表,數(shù)據(jù)進(jìn)行入庫。

凡事有利有弊,方案也要結(jié)合工時,實際可行性與技術(shù)評審之后才能決定,弊端咱也要列出來:

  • 設(shè)計復(fù)雜,需要增加 range 區(qū)域與 hash 環(huán)關(guān)系。
  • 系統(tǒng)內(nèi)修改波及較多,查詢關(guān)系復(fù)雜,多了一層路由表的概念,雖然盡量吧用戶數(shù)據(jù)分配到一個區(qū)之內(nèi),但是想查詢誰關(guān)注我,與我關(guān)注誰這樣的邏輯時還是復(fù)雜。

第二種方案:range+hash 取模(hash 模 300)

這個其實就比較好理解了,就是一個簡單的 range+hash 取模組合的形式,先 range 到一定的范圍后,在這個范圍內(nèi)進(jìn)行 hash 取模找到對應(yīng)的表進(jìn)行存儲。

這個方案比方案一簡單點,但是方案一存在的問題他也存在,并且他還有擴(kuò)容數(shù)據(jù)影響范圍廣的問題。

但是實現(xiàn)起來就簡單不少,從查詢方面看根據(jù)不同場景可以控制取模的大小范圍,根據(jù)實際情況每個分區(qū)的 hash 模采用不同的值。

最后一種方案:range userId 分區(qū)

這個方案是我覺得靠譜性與實施性可能最高的一種,看起來挺像第二種方案的,但是更具體了一點,首先會定義一個中間關(guān)系表 user_attention_routing。

我們會把用戶范圍與路由到哪個表做成關(guān)系,根據(jù)范圍區(qū)間進(jìn)行查找,結(jié)合現(xiàn)有數(shù)據(jù)當(dāng)某個大 V,或者網(wǎng)紅數(shù)據(jù)量比較大,我們就給他路由自成一表數(shù)據(jù)大概是這樣的。

例如 user_id=256 是個大 V,就把他單獨提出來讓他自成一表,在查詢范圍的時候優(yōu)先查是否有自己單獨對應(yīng)的路由表。

而其他那些零碎用戶還是路由到一個統(tǒng)一表內(nèi),這時候有的同學(xué)會說這樣子數(shù)據(jù)不都又不均勻了么。

我也曾這樣認(rèn)為,但是分到絕對的均勻基本不太可能,只能做到相對,盡量把某些大 V 分出去,不占用公共資源。

當(dāng)某個人突然成為大 V 后,再把這個人再單獨分出去,不斷演變這個過程,保證數(shù)據(jù)的平衡。

并且這樣子處理之后很多原來的關(guān)聯(lián)查詢其實改動不大了,只要在數(shù)據(jù)遷移后對原來的所有包含 user_attention 進(jìn)行動態(tài)的改造即可(使用個 mybatis 的攔截器就能搞定)。

PS:其實分析實際業(yè)務(wù)場景大部分的關(guān)注數(shù)據(jù)還是來源于那些零碎用戶的。

分表方案首先就這樣定了,接下來另一個問題就是查詢問題,上文說過很多業(yè)務(wù)查詢無非就是誰關(guān)注了我,我關(guān)注了誰這樣的場景,如果繼續(xù)使用之前的:

  1. //我關(guān)注了誰 
  2. select * from user_attention where user_id = #{userId}  
  3.  
  4. //誰關(guān)注了我 
  5. select * from user_attention where fans_id = #{userId} 

這樣的方案,當(dāng)我要查詢我的粉絲有哪些時,這樣就悲劇了,我還是要檢索全表根據(jù) fansid 找到我所有的粉絲,因為表內(nèi)只記錄了我關(guān)注了誰這樣的數(shù)據(jù)。

考慮到這樣的問題,我決定重新設(shè)計數(shù)據(jù)存儲形式,使用空間換時間的思路,原來處理的方式是用戶在關(guān)注對方的時候產(chǎn)生一條記錄。

現(xiàn)在處理方式是用戶 A 在關(guān)注用戶 B 時寫入兩條數(shù)據(jù),通過字段區(qū)分關(guān)系,假如 user_attention 表是這樣的:

在用戶 1 關(guān)注 2 后產(chǎn)生兩條數(shù)據(jù),state(1 代表我關(guān)注了,0 代表我被關(guān)注了,2 代表咱倆互關(guān)),采用這樣的數(shù)據(jù)存儲方式后,我所有的查詢都可以從 user_id 進(jìn)行出發(fā)了,不在逆向去推 fans_id 這樣的方式。

數(shù)據(jù)庫索引設(shè)計上,考慮好 user_id、fans_id、state 與 user_id、state 這樣的結(jié)構(gòu)即可,是不是感覺很簡單,雖然數(shù)據(jù)量存儲變多了,但是查詢方便了好多。

分表和查詢問題解決了,最后就是要考慮數(shù)據(jù)遷移的過程了,這一步也非常重要。搞不好就要被扣掉自己的 KPI 了(步步為營啊)!

數(shù)據(jù)遷移最需要考慮的問題就是個時效性,遷移程序必不可少,如何生產(chǎn)環(huán)境正常跑著,遷移腳本線下跑著數(shù)據(jù)互不影響呢?

答案就是經(jīng)典套路數(shù)據(jù)雙寫,因為老的數(shù)據(jù)不是一下子就遷移到新表內(nèi)的,現(xiàn)在和 user_attention 產(chǎn)生的數(shù)據(jù)還是要保持的,在產(chǎn)生老表數(shù)據(jù)的同時,根據(jù)路由規(guī)則,直接存到新表內(nèi)一份。

線下的遷移程序多開幾臺服務(wù)慢慢跑唄,不過可要控制好數(shù)據(jù)量,別占滿 IO 影響生產(chǎn)環(huán)境,線下的模擬和演練也是必不可少的,誰都不能保證會不會出啥問題呢。

遷移腳本和線上做好 user_id 和 fans_id 的唯一索引就行,在某些極端情況下,數(shù)據(jù)會存在新表內(nèi)寫入數(shù)據(jù),但是老表內(nèi)數(shù)據(jù)還沒更新的可能這個做好版本號控制和日志記錄就可以了,這些都比較簡單。

當(dāng)新表數(shù)據(jù)和老表完全同步時我們就可以吧所有系統(tǒng)內(nèi)波及老表查詢的語句都改成新表查詢,驗證下有沒有問題,如果沒有問題最后就可以痛快的!

  1. truncate table user_attention; 

干掉這個 5 億數(shù)據(jù)量的定時炸彈了。好了,今天分享就結(jié)束了,看來我不僅能挺過試崗期也能挺過試用期了,不說了下班回家抱娃去了??。

作者:TOM,一個二線城市的程序員

編輯:陶家龍

出處:轉(zhuǎn)載自公眾號 JavaTom

責(zé)任編輯:武曉燕 來源: JavaTom
相關(guān)推薦

2021-03-29 08:20:51

入職后端官場

2025-01-16 08:19:39

Python代碼開發(fā)

2021-02-03 21:15:44

Ansible系統(tǒng)運維系統(tǒng)管理員

2020-02-03 13:30:54

釘釘企業(yè)微信移動應(yīng)用

2012-08-10 22:44:52

ArchSummit

2023-07-06 09:01:33

2024-10-31 16:38:49

2021-02-23 07:08:04

分布式文件

2022-09-14 11:34:46

首席信息官IT價值

2010-06-02 17:23:10

JavaJazoon

2012-06-25 11:27:43

2014-08-11 13:10:48

2015-07-31 10:01:55

win10使用總結(jié)

2013-08-02 17:19:21

2020-04-08 07:55:08

MySQLSLA數(shù)據(jù)

2019-02-14 10:04:34

程序員離職技術(shù)

2020-02-20 17:16:55

遠(yuǎn)程辦公

2014-08-04 10:58:06

OpenstackRDOOpenstack搭建

2020-02-04 11:22:47

云計算行業(yè)辦公

2022-09-05 13:29:02

安全領(lǐng)導(dǎo)者員工入職
點贊
收藏

51CTO技術(shù)棧公眾號