成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

分庫(kù)分表會(huì)帶來(lái)讀擴(kuò)散問題?怎么解決?

數(shù)據(jù)庫(kù) MySQL
MySQL在單表數(shù)據(jù)過大時(shí),查詢性能會(huì)變差,因此當(dāng)數(shù)據(jù)量變得巨大時(shí),需要考慮水平分表。

今天這篇文章,其實(shí)也是我曾經(jīng)面試中遇到過的真題。

分庫(kù)分表大家可能聽得多了,但讀擴(kuò)散問題大家了解嗎?

這里涉及到幾個(gè)問題。

分庫(kù)分表是什么?

讀擴(kuò)散問題是什么?

分庫(kù)分表為什么會(huì)引發(fā)讀擴(kuò)散問題?

怎么解決讀擴(kuò)散問題?

能不能不要在評(píng)論區(qū)叫我刁毛?

不好意思,失態(tài)了。

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

相信兄弟們也一定有機(jī)會(huì)遇到哈哈哈。

我們先從分庫(kù)分表的話題聊起吧。

分庫(kù)分表

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

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

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

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

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

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

分表

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

那分表具體怎么做?

根據(jù)id范圍分表

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

我們假設(shè)每張分表能放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范圍分表

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

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

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

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

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

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

如果有,就多加一層。

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

通過orm讀寫分表

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

如果是通過第三方orm庫(kù)的方式來(lái)做的話,那需要根據(jù)不同語(yǔ)言實(shí)現(xiàn)不同的代碼庫(kù),所以不少?gòu)S都選擇后者加個(gè)proxy的方式,這樣就不需要關(guān)心上游服務(wù)用的是什么語(yǔ)言。

通過proxy管理分表

根據(jù)id取模分表

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

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

比如一個(gè)id=31進(jìn)來(lái),我們一共分了5張表,分別是user0到user4。對(duì)31%5=1,取模得1,于是就能知道應(yīng)該讀寫user1表。

根據(jù)id取模分表

優(yōu)點(diǎn)當(dāng)然是比較簡(jiǎn)單。而且讀寫數(shù)據(jù)都可以很均勻的分?jǐn)偟矫總€(gè)分表上。

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

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

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

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

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

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

也不是。

將上面兩種方式結(jié)合起來(lái)

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

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

這時(shí)候就可以將id取模和id范圍分表的方式結(jié)合起來(lái)。

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

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

這樣就可以將寫單表分?jǐn)倿閷懚啾怼?/p>

這在分庫(kù)的場(chǎng)景下優(yōu)勢(shì)會(huì)更明顯,不同的庫(kù),可以把服務(wù)部署到不同的機(jī)器上,這樣各個(gè)機(jī)器的性能都能被用起來(lái)。

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

讀擴(kuò)散問題

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

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

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

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

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

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

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

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

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

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

隨著我的表越來(lái)越多,次數(shù)會(huì)越來(lái)越多,這就是所謂的讀擴(kuò)散問題。

讀擴(kuò)散問題

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

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

引入新表來(lái)做分表

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

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

通過新索引表解決讀擴(kuò)散問題

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

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

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

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

舉例說明讀擴(kuò)散問題

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

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

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

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

但這個(gè)做法的缺點(diǎn)也比較明顯,你需要維護(hù)兩套表,并且普通索引列更新時(shí),要兩張表同時(shí)進(jìn)行更改。

有一定的開發(fā)量。

有沒有更簡(jiǎn)單的方案?

使用其他更合適的存儲(chǔ)

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

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

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

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

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

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

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

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

mysql同步es

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

有。

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

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

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

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

又是個(gè)極小的細(xì)節(jié)。

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

用tidb替換mysql

總結(jié)

  • mysql在單表數(shù)據(jù)過大時(shí),查詢性能會(huì)變差,因此當(dāng)數(shù)據(jù)量變得巨大時(shí),需要考慮水平分表。
  • 水平分表需要選定一個(gè)分片鍵,一般選擇主鍵,然后根據(jù)id進(jìn)行取模,或者根據(jù)id的范圍進(jìn)行分表。
  • mysql水平分表后,對(duì)于非分片鍵字段的查詢會(huì)有讀擴(kuò)散的問題,可以用普通索引列作分片鍵建一個(gè)新表,先查新表拿到id后再回到原表再查一次原表。這本質(zhì)上是借鑒了倒排索引的思路。
  • 如果想要支持更多維度的查詢,可以監(jiān)聽mysql的binlog,將數(shù)據(jù)寫入到es,提供近實(shí)時(shí)的查詢能力。
  • 當(dāng)然,用tidb替換mysql也是個(gè)思路。tidb屬實(shí)是個(gè)好東西,不少?gòu)S都拿它換個(gè)皮貼個(gè)標(biāo),做成自己的自研數(shù)據(jù)庫(kù),非常推薦大家學(xué)習(xí)一波。
  • 不要做過早的優(yōu)化,沒事別上來(lái)就分100個(gè)表,很多時(shí)候真用不上。

參考資料《圖解分庫(kù)分表》

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

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

責(zé)任編輯:姜華 來(lái)源: 小白debug
相關(guān)推薦

2019-07-31 09:27:23

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

2022-06-02 16:04:55

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

2020-01-07 09:40:25

數(shù)據(jù)庫(kù)MySQLRedis

2022-10-11 17:51:49

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

2024-02-21 12:17:00

2020-07-30 17:59:34

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

2025-01-07 08:20:49

2019-11-12 09:54:20

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

2025-03-03 12:30:00

import *代碼開發(fā)

2021-08-31 20:21:11

VitessMySQL分庫(kù)

2023-08-11 08:59:49

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

2021-03-17 16:15:55

數(shù)據(jù)MySQL 架構(gòu)

2020-11-18 09:39:02

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

2022-09-26 08:28:22

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

2022-07-11 08:16:47

NewSQL關(guān)系數(shù)據(jù)庫(kù)系統(tǒng)

2020-07-28 09:04:09

NewSQL分庫(kù)分表

2021-01-26 05:37:08

分庫(kù)分表內(nèi)存

2025-04-01 08:45:00

2024-07-26 00:16:11

2024-03-08 22:21:06

海量數(shù)據(jù)MySQ數(shù)據(jù)庫(kù)
點(diǎn)贊
收藏

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

主站蜘蛛池模板: 亚洲综合小视频 | 精品一二三区 | 国产91九色 | 国产在线a | 在线观看免费毛片 | www.com久久久 | 久久99视频精品 | 中文字幕在线人 | 国产成人综合在线 | av大片在线 | 欧美成视频 | 欧美大片在线观看 | 日韩中文字幕在线不卡 | 精品免费国产一区二区三区四区 | 久久精品亚洲精品国产欧美 | 在线国产视频 | 四虎av电影| 中文字字幕一区二区三区四区五区 | 亚洲精品一区二区三区蜜桃久 | 91欧美 | 在线高清免费观看视频 | 欧美在线观看一区 | 美女国产 | 特黄av| 久久伊人精品 | 日日夜夜狠狠操 | 亚洲国产一区视频 | 91成人免费看片 | 精品国产乱码久久久久久果冻传媒 | 久久亚洲欧美日韩精品专区 | 国产一级毛片精品完整视频版 | 国产免费一区二区三区免费视频 | 中文字幕在线一区 | 久久国产精品无码网站 | 亚洲色综合 | 别c我啊嗯国产av一毛片 | 日韩a v在线免费观看 | 久久久国产精品一区 | 中文字幕免费在线观看 | 国产精品福利一区二区三区 | 天天干天天干 |