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

光知道分庫分表可不敢直接去面試,分表后讀擴散怎么解決才是重點

數(shù)據(jù)庫
現(xiàn)在一談數(shù)據(jù)量大時,幾乎每個人都知道分庫分表,拆分策略也都能說出來個123,不過分庫分表后必然會大佬數(shù)據(jù)分散的問題,那怎么解決拆分后的讀擴散問題,也是需要我們提前想清楚的。最近讀到一篇關于解決讀擴散問題的文章,推薦給大家。

今天這篇文章,其實也是我曾經面試中遇到過的真題。

分庫分表大家可能聽得多了,但 讀擴散 問題大家了解嗎?

這里涉及到幾個問題。

分庫分表是什么?

讀擴散問題是什么?

分庫分表為什么會引發(fā)讀擴散問題?

怎么解決讀擴散問題?

這些問題還是比較有意思的。

相信兄弟們也一定有機會遇到哈哈哈。

我們先從分庫分表的話題聊起吧。

分庫分表

我們平時做項目開發(fā)。一開始,通常都先用一張數(shù)據(jù)表,而一般來說數(shù)據(jù)表寫到2kw條數(shù)據(jù)之后,底層B+樹的層級結構就可能會變高,不同層級的數(shù)據(jù)頁一般都放在磁盤里不同的地方,換言之,磁盤IO就會增多,帶來的便是查詢性能變差。 如果對上面這句話有疑惑的話,可以去看下我之前寫的文章。

于是,當我們單表需要管理的數(shù)據(jù)變得越來越多,就不得不考慮數(shù)據(jù)庫 分表 。而這里的分表,分為 水平分表和垂直分表 。

垂直分表的原理比較簡單,一般就是把某幾列拆成一個新表,這樣單行數(shù)據(jù)就會變小,B+樹里的單個數(shù)據(jù)頁(固定16kb)內能放入的行數(shù)就會變多,從而使單表能放入更多的數(shù)據(jù)。

垂直分表沒有太多可以說的點。下面,我們重點說說最常見的 水平分表 。

水平分表有好幾種做法,但不管是哪種,本質上都是將原來的 user 表,變成 user_0, user1, user2 .... uerN 這樣的N多張小表。

從讀寫一張user 大表 ,變成讀寫 user_1 … userN 這樣的N張 小表 。

分表

每一張小表里,只保存一部分數(shù)據(jù),但具體保存多少,這個自己定,一般就訂個 500w~2kw 。

那分表具體怎么做?

根據(jù)id范圍分表

我認為最好用的,是根據(jù)id范圍進行分表。

我們假設每張分表能放 2kw 行數(shù)據(jù)。那user0就放主鍵id為 1~2kw 的數(shù)據(jù)。user1就放id為 2kw+1 ~ 4kw ,user2就放id為 4kw+1 ~ 6kw , userN就放 2N kw+1 ~ 2(N+1)kw 。

根據(jù)id范圍分表

假設現(xiàn)在有條數(shù)據(jù),id=3kw,將這個 3kw除2kw = 1.5 ,向下取整得到 1 ,那就可以得到這條數(shù)據(jù)屬于 user1表 。于是去讀寫user1表就行了。這就完成了數(shù)據(jù)的路由邏輯,我們把這部分邏輯封裝起來,放在數(shù)據(jù)庫和業(yè)務代碼之間。

這樣。 對于業(yè)務代碼來說 ,它只知道自己在讀寫一張 user 表,根本不知道底下還分了那么多張小表。

對于數(shù)據(jù)庫來說,它并不知道自己被分表了,它只知道有那么幾張表,正好名字長得比較像而已。

這還只是在 一個數(shù)據(jù)庫 里做分表,如果范圍再搞大點,還能在 多個數(shù)據(jù)庫 里做分表,這就是所謂的 分庫分表 。

不管是單庫分表還是分庫分表,都可以通過這樣一個中間層邏輯做路由。

還真的就應了那句話,沒有什么是加中間層不能解決的。

如果有,就多加一層。

至于這個中間層的實現(xiàn)方式就更靈活了,它既可以像 第三方orm庫 那樣加在業(yè)務代碼中。

通過orm讀寫分表

也可以在mysql和業(yè)務代碼之間加個 proxy服務 。

如果是通過第三方orm庫的方式來做的話,那需要根據(jù)不同語言實現(xiàn)不同的代碼庫,所以不少廠都選擇后者加個proxy的方式,這樣就不需要關心上游服務用的是什么語言。

通過proxy管理分表

根據(jù)id取模分表

這時候就有兄弟要提出問題了,"我看很多方案都 對id取模 ,你這個方案是不是不完整?"。

取模的方案也是很常見的。

比如一個id=31進來,我們一共分了5張表,分別是user0到user4。對 31%5=1 ,取模得 1 ,于是就能知道應該讀寫 user1 表。

根據(jù)id取模分表

優(yōu)點當然是比較簡單。而且讀寫數(shù)據(jù)都可以很均勻的分攤到每個分表上。

但 缺點 也比較明顯,如果想要擴展表的個數(shù),比如從5張表變成8張表。那同樣還是id=31的數(shù)據(jù), 31%8 = 7 ,就需要讀寫user7這張表。跟原來就對不上了。

這就需要考慮 數(shù)據(jù)遷移 的問題。很頭禿。

為了避免后續(xù)擴展的問題,我見過一些業(yè)務一開始就將數(shù)據(jù)預估得很大,然后心一橫,分成100張表,一張表如果存?zhèn)€2kw條,那也能存20億數(shù)據(jù)了。

也不是說這樣不行吧,就是這個業(yè)務直到最后放棄的時候,也就存了百萬條數(shù)據(jù),每次打開數(shù)據(jù)庫表能看到茫茫多的user_xx,就是不太舒服,專業(yè)點,叫增加了程序員的 心智負擔 。

而上面一種方式,根據(jù)id范圍去分表,就能很好的解決這些問題,數(shù)據(jù)少的時候,表也少,隨著數(shù)據(jù)增多,表會慢慢變多。而且這樣表還可以無限擴展。

那是不是說取模的做法就用不上了呢?

也不是。

將上面兩種方式結合起來

id取模的做法,最大的好處是,新寫入的數(shù)據(jù)都是實實在在的分散到了 多張表 上。

而根據(jù)id范圍去做分表,因為id是遞增的,那新寫入的數(shù)據(jù)一般都會落到 某一張表 上,如果你的業(yè)務場景寫數(shù)據(jù)特別頻繁,那這張表就會出現(xiàn) 寫熱點 的問題。

這時候就可以將id取模和id范圍分表的方式結合起來。

我們可以在某個id范圍里,引入取模的功能。比如 以前 2kw~4kw 是user1表,現(xiàn)在可以在這個范圍 再分成5個表 ,也就是引入user1-0, user1-2到user1-4,在這5個表里取模。

舉個例子,id=3kw,根據(jù)范圍,會分到user1表,然后再進行取模 3kw % 5 = 0,也就是讀寫user1-0表。

這樣就可以將寫單表分攤為寫多表。

這在分庫的場景下優(yōu)勢會更明顯,不同的庫,可以把服務部署到不同的機器上,這樣各個機器的性能都能被用起來。

根據(jù)id范圍分表后再取模

讀擴散問題

我們上面提到的好幾種分表方式,都用了id這一列作為 分表的依據(jù) ,這其實就是所謂的 分片鍵 。

實際上我們一般也是用的 數(shù)據(jù)庫主鍵 作為 分片鍵 。

這樣,理想情況下我們已知一個id,不管是根據(jù)哪種規(guī)則,我們都能很快定位到該讀哪個分表。

但很多情況下,我們的查詢又不是只查主鍵,如果我的數(shù)據(jù)庫表有一列name,并且加了個普通索引。

這樣我執(zhí)行下面的sql

select * from user where name = "小白";

由于name并不是分片鍵,我們沒法定位到具體要到哪個分表上去執(zhí)行sql。

于是就會對 所有分表 都執(zhí)行上面的sql,當然不會是串行執(zhí)行sql,一般都是 并發(fā) 執(zhí)行sql的。

如果我有100張表,就執(zhí)行100次sql。

如果我有200張表,就執(zhí)行200次sql。

隨著我的表越來越多,次數(shù)會越來越多,這就是所謂的 讀擴散問題 。

讀擴散問題

這是個比較有趣的問題,它確實是個問題,但大部分的業(yè)務不會去處理它,讀100次怎么了,數(shù)據(jù)增長之后讀的次數(shù)會不斷增加又怎么了?但架不住我的 業(yè)務不賺錢 啊,也根本 長不了那么多數(shù)據(jù) 啊。

話是這么說沒錯,但面試官問你的時候,你得知道怎么處理啊。

引入新表來做分表

問題的核心在于,主鍵是分片鍵,而普通索引列并不分片。

那好辦,我們單獨建個 新的分片表 ,這個新表里的列就只有舊表的主鍵id和普通索引列,而這次換普通索引列來做分片鍵。

通過新索引表解決讀擴散問題

這樣當我們要查詢普通索引列時,先到這個新的分片表里做一次查詢,就能迅速定位到對應的主鍵id,然后再拿主鍵id去舊的分片表里查一次數(shù)據(jù)。這樣就從原來漫無目的的全表擴散查詢,縮減為只查固定幾個表了。

舉個例子。比如我的表原本長下面這樣,其中id列是主鍵,同時也是分片鍵,name列是非主鍵索引。為了簡化,假設三條數(shù)據(jù)一張表。

此時分表里 id=1,4,6 的都有 name="小白" 的數(shù)據(jù)。

當我們執(zhí)行 select * from user where name = "小白"; 則需要并發(fā)查3張表,隨著表變多,查詢次數(shù)會變得更多。

舉例說明讀擴散問題

但如果我們?yōu)閚ame列 建個新表(nameX),以name為新的分片鍵 。

這樣我們可以先執(zhí)行 select id from nameX where name = "小白";

再拿著結果里的ids去查詢 select * from user where id in (ids); 這樣就算表變多了,也可以迅速定位到某幾張具體的表,減少了查詢次數(shù)。

舉例說明通過新索引表解決讀擴散問題

但這個做法的缺點也比較明顯,你需要維護兩套表,并且普通索引列更新時,要兩張表同時進行更改。

有一定的開發(fā)量

有沒有更簡單的方案?

使用其他更合適的存儲

我們常規(guī)的查詢是通過id主鍵去查詢對應的name列。而像上面的方案,則通過引入一個新表, 倒過來 ,先用name查到對應的id,再拿id去獲取具體的數(shù)據(jù)。這其實就像是建立了一個新的索引一樣,像這種,通過name列反查原數(shù)據(jù)的思想,其實就很類似于 倒排索引 。

相當于我們是利用了倒排索引的思路去解決分表下的數(shù)據(jù)查詢問題。

回想下,其實我們的 原始需求 無非就是在大量數(shù)據(jù)的場景下依然能提供普通索引列或其他更多維度的查詢。

這種場合,更適合使用es,es天然分片,而且內部利用 倒排索引 的形式來加速數(shù)據(jù)查詢。

哦?兄弟萌,又是它, 倒排索引 ,又是個極小的細節(jié),做好筆記。

舉個例子,我同樣是一行數(shù)據(jù) id,name,age。在mysql里,你得根據(jù)id分片,如果要支持name和age的查詢,為了防止讀擴散,你得分別再建一個name的分片表和一個age的分片表。

而如果你用es,它會在它內部以id分片鍵進行分片,同時還能建一個name到id,和一個age到id的倒排索引。這是不是就跟上面做的事情沒啥區(qū)別。

而且將mysql接入es也非常簡單,我們可以通過開源工具 canal 監(jiān)聽mysql的 binlog 日志變更,再將數(shù)據(jù)解析后寫入es,這樣es就能提供 近實時 的查詢能力。

mysql同步es

覺得es+mysql還是繁瑣?有沒有其他更簡潔的方案?

有。

別用mysql了,改用 tidb 吧,相信大家多少也聽說過這個名稱,這是個 分布式數(shù)據(jù)庫 。

它通過引入 Range 的概念進行數(shù)據(jù)表分片,比如第一個分片表的id在0~2kw,第二個分片表的id在2kw~4kw。

哦?有沒有很熟悉,這不就是文章開頭提到的根據(jù)id范圍進行數(shù)據(jù)庫分表嗎?

它支持普通索引,并且普通索引也是分片的,這是不是又跟上面提到的倒排索引方案很類似。

又是個極小的細節(jié)。

并且tidb跟mysql的語法幾乎一致,現(xiàn)在也有非常多現(xiàn)成的工具可以幫你把數(shù)據(jù)從mysql遷移到tidb。所以開發(fā)成本并不高。

用tidb替換mysql

總結

mysql在單表數(shù)據(jù)過大時,查詢性能會變差,因此當數(shù)據(jù)量變得巨大時,需要考慮水平分表。

水平分表需要選定一個分片鍵,一般選擇主鍵,然后根據(jù)id進行取模,或者根據(jù)id的范圍進行分表。

mysql水平分表后,對于非分片鍵字段的查詢會有讀擴散的問題,可以用普通索引列作分片鍵建一個新表,先查新表拿到id后再回到原表再查一次原表。這本質上是借鑒了倒排索引的思路。

如果想要支持更多維度的查詢,可以監(jiān)聽mysql的binlog,將數(shù)據(jù)寫入到es,提供近實時的查詢能力。

當然,用tidb替換mysql也是個思路。tidb屬實是個好東西,不少廠都拿它換個皮貼個標,做成自己的 自研數(shù)據(jù)庫 ,非常推薦大家學習一波。

不要做過早的優(yōu)化,沒事別上來就分100個表,很多時候真用不上。

參考資料

《圖解分庫分表》

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

最后

當年我還在某個游戲項目組里做開發(fā)的時候,從企鵝那邊挖來的策劃信誓旦旦的說,我們要做的這款游戲老少皆宜,肯定是爆款。要做成全球同服。上線至少 過億注冊 , 十萬人同時在線 。要好好規(guī)劃和設計。

我們算了下,信他能有個1億注冊。用了id范圍的方式進行分片,分了 4張表 。

搞得我熱血沸騰。

那天晚上下班,夏蟬鳴泣,從赤道吹來的熱風陣陣拂過我的手臂,我聽著澤野弘之的歌,就算是開電瓶車,我都感覺自己像是在開高達。

一年后。

游戲上線前一天通知運維加機器,怕頂不住,要整夜關注。

后來上線了,全球最高在線人數(shù) 58 人。其中有 7 個是項目組成員。

還是夏天,還是同樣的下班路,想哭,但我不能哭,因為騎電瓶車的時候擦眼淚不安全。

責任編輯:張燕妮 來源: 網(wǎng)管叨bi叨
相關推薦

2022-05-25 08:06:37

MySQL分庫分表

2025-04-01 08:45:00

2020-07-30 17:59:34

分庫分表SQL數(shù)據(jù)庫

2019-11-12 09:54:20

分庫分表數(shù)據(jù)

2023-08-11 08:59:49

分庫分表數(shù)據(jù)數(shù)據(jù)庫

2021-08-31 20:21:11

VitessMySQL分庫

2020-11-18 09:39:02

MySQL數(shù)據(jù)庫SQL

2022-07-11 08:16:47

NewSQL關系數(shù)據(jù)庫系統(tǒng)

2024-07-26 00:16:11

2019-01-16 14:00:54

數(shù)據(jù)庫分庫分表

2020-07-28 09:04:09

NewSQL分庫分表

2021-01-26 05:37:08

分庫分表內存

2024-07-25 18:20:03

2025-04-09 00:00:00

2022-09-26 08:28:22

分庫分表數(shù)據(jù)

2021-03-17 16:15:55

數(shù)據(jù)MySQL 架構

2019-01-30 09:53:58

數(shù)據(jù)庫性能分庫分表

2022-06-30 07:34:46

分庫分表外賣訂單系統(tǒng)

2019-07-31 09:27:23

數(shù)據(jù)庫MySQLSQL

2024-01-03 08:14:33

GreatSQLMyCat庫名字
點贊
收藏

51CTO技術棧公眾號