咱們做后端開(kāi)發(fā)、架構(gòu)設(shè)計(jì)的,只要接觸過(guò)大數(shù)據(jù)場(chǎng)景,幾乎都踩過(guò)MySQL單庫(kù)單表的坑。比如說(shuō),當(dāng)業(yè)務(wù)發(fā)展到一定階段,用戶(hù)量破百萬(wàn)、千萬(wàn),訂單表、用戶(hù)表的數(shù)據(jù)量飆升到千萬(wàn)級(jí)、億級(jí),你就會(huì)發(fā)現(xiàn),原來(lái)好好運(yùn)行的MySQL,突然變得“步履蹣跚”——查詢(xún)一條數(shù)據(jù)要好幾秒,甚至超時(shí);寫(xiě)入并發(fā)一高,就出現(xiàn)鎖表、丟數(shù)據(jù)的情況;想擴(kuò)容吧,單庫(kù)單表根本無(wú)從下手,硬擴(kuò)容不僅成本高,還容易出故障。
這不是MySQL不行,而是它的設(shè)計(jì)初衷就不是為了承載“海量數(shù)據(jù)+高并發(fā)”的場(chǎng)景。MySQL單庫(kù)的并發(fā)連接數(shù)、磁盤(pán)IO、內(nèi)存占用都有上限,單表的數(shù)據(jù)量一旦超過(guò)1000萬(wàn),索引效率會(huì)急劇下降,查詢(xún)優(yōu)化的空間也越來(lái)越小。很多公司就是因?yàn)闆](méi)提前做好數(shù)據(jù)庫(kù)架構(gòu)設(shè)計(jì),等到數(shù)據(jù)量暴增、系統(tǒng)崩掉的時(shí)候,才急著救火,最后不僅耗費(fèi)大量人力物力,還影響用戶(hù)體驗(yàn),甚至造成經(jīng)濟(jì)損失。
今天咱們就接地氣一點(diǎn),不聊虛的理論,專(zhuān)門(mén)講MySQL分庫(kù)分表的實(shí)戰(zhàn)技巧,還有大數(shù)據(jù)場(chǎng)景下的數(shù)據(jù)庫(kù)架構(gòu)設(shè)計(jì)方案,幫大家避開(kāi)那些踩過(guò)的坑,真正解決“數(shù)據(jù)撐爆、查詢(xún)卡頓、擴(kuò)容困難”這些核心痛點(diǎn)。不管你是后端開(kāi)發(fā)、DBA,還是剛接觸架構(gòu)設(shè)計(jì)的新手,看完這篇,都能上手實(shí)操,把MySQL的性能拉滿(mǎn)。
先搞懂:為什么一定要做分庫(kù)分表?那些繞不開(kāi)的痛點(diǎn)
在講實(shí)戰(zhàn)之前,咱們先明確一個(gè)問(wèn)題:不是所有場(chǎng)景都需要分庫(kù)分表,只有當(dāng)你的業(yè)務(wù)遇到以下這些痛點(diǎn)時(shí),才需要考慮——不然瞎折騰,反而會(huì)增加系統(tǒng)復(fù)雜度,得不償失。
第一個(gè)最直觀的痛點(diǎn):數(shù)據(jù)量太大,單表?yè)尾蛔 N乙?jiàn)過(guò)很多電商公司,訂單表只用了一年多,數(shù)據(jù)量就突破了2億條,這時(shí)候你去查一個(gè)三個(gè)月前的訂單,哪怕建了索引,也要等3-5秒,甚至超時(shí)。為什么?因?yàn)镸ySQL的索引是B+樹(shù)結(jié)構(gòu),數(shù)據(jù)量越大,B+樹(shù)的層數(shù)就越多,磁盤(pán)IO的次數(shù)就越多,查詢(xún)速度自然就慢。而且單表數(shù)據(jù)量太大,備份和恢復(fù)也特別麻煩,一次備份可能要花好幾個(gè)小時(shí),萬(wàn)一出了故障,恢復(fù)數(shù)據(jù)的時(shí)間根本來(lái)不及,直接影響業(yè)務(wù)正常運(yùn)行。
第二個(gè)痛點(diǎn):并發(fā)太高,單庫(kù)扛不住。比如電商大促的時(shí)候,每秒的訂單寫(xiě)入請(qǐng)求能達(dá)到幾千甚至幾萬(wàn),單庫(kù)的并發(fā)連接數(shù)根本頂不住,會(huì)出現(xiàn)連接超時(shí)、鎖表的情況。MySQL的默認(rèn)并發(fā)連接數(shù)大概是151,就算調(diào)大到1000、2000,也有上限,而且并發(fā)太高會(huì)導(dǎo)致CPU、內(nèi)存占用飆升,數(shù)據(jù)庫(kù)直接卡死。這時(shí)候,單庫(kù)單表就成了整個(gè)系統(tǒng)的瓶頸,哪怕你把應(yīng)用服務(wù)器擴(kuò)容到10臺(tái)、20臺(tái),數(shù)據(jù)庫(kù)這邊還是拖后腿。
第三個(gè)痛點(diǎn):擴(kuò)容困難,成本太高。單庫(kù)單表的擴(kuò)容,要么是垂直擴(kuò)容(換更強(qiáng)的服務(wù)器,比如把4核8G換成16核32G),但垂直擴(kuò)容的上限很低,而且成本越來(lái)越高,到了一定程度,再?gòu)?qiáng)的服務(wù)器也扛不住億級(jí)數(shù)據(jù)和高并發(fā)。要么是水平擴(kuò)容,但單庫(kù)單表根本無(wú)法實(shí)現(xiàn)水平擴(kuò)容,總不能把一個(gè)表拆成兩個(gè)表,手動(dòng)去維護(hù)數(shù)據(jù)分布吧?那樣不僅容易出錯(cuò),還會(huì)增加開(kāi)發(fā)和維護(hù)成本。
第四個(gè)痛點(diǎn):數(shù)據(jù)隔離性差,風(fēng)險(xiǎn)高。所有業(yè)務(wù)的數(shù)據(jù)都存在一個(gè)庫(kù)、一個(gè)表里面,一旦這個(gè)庫(kù)出現(xiàn)故障,比如磁盤(pán)損壞、SQL注入攻擊,整個(gè)業(yè)務(wù)都會(huì)癱瘓。而且不同業(yè)務(wù)的數(shù)據(jù)混在一起,權(quán)限不好控制,容易出現(xiàn)數(shù)據(jù)泄露的風(fēng)險(xiǎn)。比如用戶(hù)表和訂單表放在一起,運(yùn)維人員不小心誤刪了用戶(hù)表,整個(gè)用戶(hù)體系就崩了,損失無(wú)法挽回。
所以說(shuō),分庫(kù)分表不是“可選項(xiàng)”,而是大數(shù)據(jù)場(chǎng)景下,MySQL數(shù)據(jù)庫(kù)的“必選項(xiàng)”。它的核心目的就是:拆分?jǐn)?shù)據(jù)量,分擔(dān)并發(fā)壓力,實(shí)現(xiàn)水平擴(kuò)容,提升系統(tǒng)的穩(wěn)定性和可用性,同時(shí)降低運(yùn)維成本和風(fēng)險(xiǎn)。
核心實(shí)戰(zhàn):分庫(kù)分表的兩種核心方式,怎么選?怎么操作?
分庫(kù)分表主要分為兩種:垂直分庫(kù)分表和水平分庫(kù)分表。很多人容易混淆這兩種方式,其實(shí)核心區(qū)別很簡(jiǎn)單:垂直是“按功能拆分”,水平是“按數(shù)據(jù)范圍拆分”。咱們分別來(lái)講,結(jié)合實(shí)戰(zhàn)場(chǎng)景,告訴大家怎么選、怎么操作。
一、垂直分庫(kù)分表:解決“功能耦合”和“單庫(kù)壓力”
垂直分庫(kù)分表,簡(jiǎn)單來(lái)說(shuō),就是按照“功能模塊”來(lái)拆分。比如一個(gè)電商系統(tǒng),原來(lái)所有的數(shù)據(jù)都存在一個(gè)庫(kù)里面,包括用戶(hù)、訂單、商品、支付這四個(gè)核心模塊。垂直分庫(kù),就是把這四個(gè)模塊拆分成四個(gè)獨(dú)立的數(shù)據(jù)庫(kù):用戶(hù)庫(kù)、訂單庫(kù)、商品庫(kù)、支付庫(kù);垂直分表,就是把每個(gè)模塊里的大表,按照“字段冷熱”拆分,比如用戶(hù)表,把常用的字段(用戶(hù)ID、手機(jī)號(hào)、昵稱(chēng))放在主表,不常用的字段(頭像地址、個(gè)性簽名、注冊(cè)時(shí)間詳情)放在從表。
咱們先講垂直分庫(kù)的實(shí)戰(zhàn)要點(diǎn),這是最容易上手的一種方式,適合業(yè)務(wù)初期,數(shù)據(jù)量還沒(méi)到億級(jí),但并發(fā)已經(jīng)開(kāi)始上漲的場(chǎng)景。
垂直分庫(kù)的核心原則:按業(yè)務(wù)模塊拆分,讓每個(gè)庫(kù)只負(fù)責(zé)一個(gè)業(yè)務(wù)模塊,實(shí)現(xiàn)“專(zhuān)人專(zhuān)崗”。比如用戶(hù)庫(kù)只處理用戶(hù)的注冊(cè)、登錄、信息修改;訂單庫(kù)只處理訂單的創(chuàng)建、修改、查詢(xún);商品庫(kù)只處理商品的新增、編輯、庫(kù)存變更。這樣做的好處很明顯:第一,分擔(dān)了單庫(kù)的并發(fā)壓力,原來(lái)一個(gè)庫(kù)要扛所有業(yè)務(wù)的并發(fā),現(xiàn)在四個(gè)庫(kù)分擔(dān),壓力直接除以4;第二,數(shù)據(jù)隔離性更好,某個(gè)庫(kù)出故障,比如訂單庫(kù)崩了,用戶(hù)登錄、商品瀏覽不受影響,不會(huì)導(dǎo)致整個(gè)系統(tǒng)癱瘓;第三,運(yùn)維更方便,比如備份訂單庫(kù),只需要備份訂單相關(guān)的數(shù)據(jù),不用備份整個(gè)系統(tǒng)的數(shù)據(jù),備份和恢復(fù)的速度更快。
垂直分庫(kù)的實(shí)操步驟,咱們以電商系統(tǒng)為例,一步步來(lái):
1. 梳理業(yè)務(wù)模塊,確定拆分維度。先把系統(tǒng)的所有業(yè)務(wù)模塊列出來(lái),比如用戶(hù)、訂單、商品、支付、物流,然后判斷每個(gè)模塊的并發(fā)量和數(shù)據(jù)量,把并發(fā)高、數(shù)據(jù)量大的模塊單獨(dú)拆分成一個(gè)庫(kù)。比如訂單模塊和支付模塊并發(fā)最高,優(yōu)先拆分;用戶(hù)模塊數(shù)據(jù)量大,也單獨(dú)拆分;商品模塊可以根據(jù)情況,先拆分或者暫時(shí)不拆分。
2. 拆分?jǐn)?shù)據(jù)庫(kù),遷移數(shù)據(jù)。新建四個(gè)數(shù)據(jù)庫(kù):user_db(用戶(hù)庫(kù))、order_db(訂單庫(kù))、product_db(商品庫(kù))、pay_db(支付庫(kù))。然后把原來(lái)單庫(kù)中的對(duì)應(yīng)表,遷移到新的庫(kù)中。這里要注意,遷移數(shù)據(jù)的時(shí)候,要避免影響線上業(yè)務(wù),最好在凌晨低峰期操作,先備份數(shù)據(jù),再遷移,遷移完成后,測(cè)試無(wú)誤,再切換應(yīng)用的數(shù)據(jù)庫(kù)連接地址。
3. 調(diào)整應(yīng)用代碼,適配分庫(kù)。原來(lái)的應(yīng)用代碼,所有的SQL都是操作同一個(gè)庫(kù),現(xiàn)在要修改代碼,根據(jù)業(yè)務(wù)模塊,連接對(duì)應(yīng)的數(shù)據(jù)庫(kù)。比如查詢(xún)用戶(hù)信息,就連接user_db;創(chuàng)建訂單,就連接order_db。這里可以用數(shù)據(jù)庫(kù)連接池,給每個(gè)庫(kù)配置獨(dú)立的連接池,避免出現(xiàn)連接混亂的情況。
4. 處理跨庫(kù)關(guān)聯(lián)查詢(xún)。垂直分庫(kù)后,最麻煩的問(wèn)題就是跨庫(kù)關(guān)聯(lián)查詢(xún)。比如查詢(xún)“用戶(hù)的訂單列表”,需要關(guān)聯(lián)user_db的用戶(hù)表和order_db的訂單表,這時(shí)候直接關(guān)聯(lián)是不行的。解決方案有兩種:第一種,避免跨庫(kù)關(guān)聯(lián),在應(yīng)用層處理,先查詢(xún)用戶(hù)信息,再根據(jù)用戶(hù)ID查詢(xún)訂單信息,然后在應(yīng)用層拼接數(shù)據(jù);第二種,使用分布式數(shù)據(jù)庫(kù)中間件,比如Sharding-JDBC,它可以自動(dòng)處理跨庫(kù)關(guān)聯(lián)查詢(xún),對(duì)應(yīng)用層透明,不用修改太多代碼。
接下來(lái)講垂直分表,垂直分表主要是解決“單表字段太多”和“冷熱數(shù)據(jù)分離”的問(wèn)題。比如用戶(hù)表,有30多個(gè)字段,其中常用的只有5-6個(gè),剩下的都是不常用的字段。如果把所有字段都放在一個(gè)表里面,查詢(xún)的時(shí)候,哪怕只需要查詢(xún)常用字段,MySQL也會(huì)讀取整個(gè)行的數(shù)據(jù),浪費(fèi)磁盤(pán)IO和內(nèi)存。垂直分表就是把常用字段和不常用字段拆分成兩個(gè)表,比如user_main(主表)和user_ext(從表),兩個(gè)表用用戶(hù)ID關(guān)聯(lián)。
垂直分表的實(shí)操要點(diǎn):第一,按“字段冷熱”拆分,常用字段放主表,不常用字段放從表;第二,主表和從表的主鍵一致,方便關(guān)聯(lián)查詢(xún);第三,查詢(xún)常用字段的時(shí)候,只查主表,提升查詢(xún)速度;查詢(xún)不常用字段的時(shí)候,再關(guān)聯(lián)從表,避免浪費(fèi)資源。比如用戶(hù)登錄,只需要查詢(xún)用戶(hù)ID、手機(jī)號(hào)、密碼,就查user_main表;用戶(hù)查看個(gè)人詳情,再關(guān)聯(lián)user_ext表查詢(xún)頭像、個(gè)性簽名等字段。
垂直分庫(kù)分表的優(yōu)點(diǎn)很明顯:操作簡(jiǎn)單,容易上手,不需要復(fù)雜的中間件,適合業(yè)務(wù)初期的拆分;數(shù)據(jù)隔離性好,風(fēng)險(xiǎn)低;可以針對(duì)性地對(duì)某個(gè)模塊進(jìn)行擴(kuò)容,比如訂單庫(kù)壓力大,就單獨(dú)擴(kuò)容訂單庫(kù),不用影響其他模塊。缺點(diǎn)也很突出:如果某個(gè)模塊的數(shù)據(jù)量持續(xù)增長(zhǎng),比如訂單表,還是會(huì)遇到單表數(shù)據(jù)量太大的問(wèn)題,這時(shí)候就需要結(jié)合水平分庫(kù)分表。
二、水平分庫(kù)分表:解決“單表數(shù)據(jù)量太大”和“高并發(fā)”的核心方案
水平分庫(kù)分表,就是按照“數(shù)據(jù)范圍”來(lái)拆分,把一個(gè)大表拆分成多個(gè)小表,每個(gè)小表的數(shù)據(jù)量差不多,然后把這些小表分布到不同的數(shù)據(jù)庫(kù)中。比如訂單表,有2億條數(shù)據(jù),按照訂單ID的范圍拆分,分成10個(gè)表,每個(gè)表2000萬(wàn)條數(shù)據(jù),然后把這10個(gè)表分布到2個(gè)數(shù)據(jù)庫(kù)中,每個(gè)數(shù)據(jù)庫(kù)5個(gè)表。這樣一來(lái),單表的數(shù)據(jù)量控制在2000萬(wàn)以?xún)?nèi),查詢(xún)速度會(huì)大幅提升,并發(fā)壓力也會(huì)分擔(dān)到多個(gè)庫(kù)、多個(gè)表上。
水平分庫(kù)分表是大數(shù)據(jù)場(chǎng)景下的核心方案,適合數(shù)據(jù)量達(dá)到億級(jí)、并發(fā)量很高的場(chǎng)景,比如電商的訂單表、支付表,社交平臺(tái)的消息表、用戶(hù)行為表。它的核心原則:數(shù)據(jù)均勻分布,避免出現(xiàn)“某個(gè)表數(shù)據(jù)量太大,某個(gè)表數(shù)據(jù)量太小”的情況;拆分規(guī)則要簡(jiǎn)單易懂,方便查詢(xún)和維護(hù);盡量減少跨表、跨庫(kù)查詢(xún),降低系統(tǒng)復(fù)雜度。
水平分庫(kù)分表的核心是“拆分規(guī)則”,常用的拆分規(guī)則有三種,咱們結(jié)合實(shí)戰(zhàn)場(chǎng)景,一個(gè)個(gè)來(lái)講,告訴大家每種規(guī)則的適用場(chǎng)景和優(yōu)缺點(diǎn)。
第一種:范圍拆分。按照數(shù)據(jù)的范圍來(lái)拆分,比如按照時(shí)間范圍、ID范圍。最常用的就是時(shí)間范圍拆分,比如訂單表,按照訂單創(chuàng)建時(shí)間,每月拆分一個(gè)表,2026年1月的訂單放在order_202601表,2026年2月的訂單放在order_202602表,以此類(lèi)推?;蛘甙凑誌D范圍拆分,比如訂單ID從1到1000萬(wàn)放在order_1表,1000萬(wàn)到2000萬(wàn)放在order_2表,直到拆分完所有數(shù)據(jù)。
范圍拆分的優(yōu)點(diǎn):規(guī)則簡(jiǎn)單,容易理解和實(shí)現(xiàn);查詢(xún)某個(gè)范圍的數(shù)據(jù)時(shí),不需要跨表,比如查詢(xún)2026年3月的訂單,直接查order_202603表,速度很快;擴(kuò)容方便,比如到了2026年12月,直接新建order_202701表即可,不用修改原來(lái)的表結(jié)構(gòu)和代碼。缺點(diǎn):數(shù)據(jù)分布可能不均勻,比如電商大促的時(shí)候,11月的訂單量可能是平時(shí)的10倍,導(dǎo)致order_202611表的數(shù)據(jù)量遠(yuǎn)超其他表,出現(xiàn)“熱點(diǎn)表”問(wèn)題;查詢(xún)跨范圍的數(shù)據(jù)時(shí),需要跨多個(gè)表,比如查詢(xún)2026年1-3月的訂單,需要同時(shí)查詢(xún)order_202601、order_202602、order_202603三個(gè)表,然后拼接數(shù)據(jù)。
范圍拆分適合的場(chǎng)景:數(shù)據(jù)有明顯的時(shí)間或ID范圍特征,比如訂單表、日志表、消息表;查詢(xún)場(chǎng)景主要是按范圍查詢(xún),比如查詢(xún)某個(gè)時(shí)間段的訂單、某個(gè)ID區(qū)間的用戶(hù)。
第二種:哈希拆分。按照數(shù)據(jù)的哈希值來(lái)拆分,比如按照用戶(hù)ID的哈希值、訂單ID的哈希值,對(duì)拆分的數(shù)量取模,決定數(shù)據(jù)放在哪個(gè)表、哪個(gè)庫(kù)。比如把訂單表拆分成10個(gè)表,訂單ID的哈希值對(duì)10取模,余數(shù)為0的放在order_0表,余數(shù)為1的放在order_1表,以此類(lèi)推。
哈希拆分的優(yōu)點(diǎn):數(shù)據(jù)分布均勻,不會(huì)出現(xiàn)熱點(diǎn)表問(wèn)題,因?yàn)楣V凳请S機(jī)的,每個(gè)表的數(shù)據(jù)量差不多;查詢(xún)單個(gè)數(shù)據(jù)時(shí),速度很快,比如查詢(xún)某個(gè)用戶(hù)的訂單,只需要計(jì)算用戶(hù)ID的哈希值,就能確定對(duì)應(yīng)的表,直接查詢(xún)。缺點(diǎn):規(guī)則相對(duì)復(fù)雜,需要計(jì)算哈希值;無(wú)法按范圍查詢(xún),比如查詢(xún)2026年3月的訂單,需要遍歷所有表,因?yàn)閿?shù)據(jù)是隨機(jī)分布的;擴(kuò)容困難,比如原來(lái)拆分成10個(gè)表,現(xiàn)在要擴(kuò)容到20個(gè)表,需要重新計(jì)算所有數(shù)據(jù)的哈希值,遷移數(shù)據(jù),成本很高。
哈希拆分適合的場(chǎng)景:數(shù)據(jù)沒(méi)有明顯的范圍特征,查詢(xún)主要是單個(gè)數(shù)據(jù)查詢(xún),比如用戶(hù)表、支付表;并發(fā)量很高,需要均勻分擔(dān)壓力的場(chǎng)景。
第三種:按業(yè)務(wù)維度拆分。按照業(yè)務(wù)的核心維度來(lái)拆分,比如電商系統(tǒng),按照用戶(hù)所在的地區(qū)拆分,華北地區(qū)的用戶(hù)訂單放在order_north表,華東地區(qū)的放在order_east表,華南地區(qū)的放在order_south表;或者按照商家ID拆分,每個(gè)商家的訂單放在單獨(dú)的表中。
按業(yè)務(wù)維度拆分的優(yōu)點(diǎn):符合業(yè)務(wù)邏輯,查詢(xún)某個(gè)業(yè)務(wù)維度的數(shù)據(jù)時(shí),不需要跨表,比如查詢(xún)?nèi)A北地區(qū)的訂單,直接查order_north表;數(shù)據(jù)隔離性更好,比如某個(gè)地區(qū)的業(yè)務(wù)出問(wèn)題,不會(huì)影響其他地區(qū)的業(yè)務(wù)。缺點(diǎn):數(shù)據(jù)分布可能不均勻,比如華東地區(qū)的用戶(hù)量比華北地區(qū)多很多,導(dǎo)致order_east表的數(shù)據(jù)量太大;如果業(yè)務(wù)維度發(fā)生變化,比如新增了西南地區(qū),需要新建表,調(diào)整代碼,維護(hù)成本較高。
按業(yè)務(wù)維度拆分適合的場(chǎng)景:業(yè)務(wù)有明顯的分區(qū)特征,比如按地區(qū)、按商家、按部門(mén)拆分;查詢(xún)場(chǎng)景主要是按業(yè)務(wù)維度查詢(xún)的場(chǎng)景。
這里給大家一個(gè)實(shí)戰(zhàn)建議:實(shí)際項(xiàng)目中,很少單獨(dú)使用一種拆分規(guī)則,大多是“組合拆分”。比如訂單表,先按時(shí)間范圍拆分(每月一個(gè)表),再按用戶(hù)ID哈希拆分(每個(gè)月的表拆分成5個(gè)表),這樣既解決了范圍查詢(xún)的問(wèn)題,又解決了數(shù)據(jù)分布不均勻的問(wèn)題。
水平分庫(kù)分表的實(shí)操步驟,咱們以訂單表為例,采用“時(shí)間范圍+哈希”的組合拆分方式,一步步來(lái):
1. 確定拆分方案。訂單表數(shù)據(jù)量2億條,按時(shí)間范圍拆分,每月一個(gè)表;每個(gè)月的表,按用戶(hù)ID哈希拆分,分成5個(gè)表,這樣總共拆分出12×5=60個(gè)表;然后把這60個(gè)表分布到6個(gè)數(shù)據(jù)庫(kù)中,每個(gè)數(shù)據(jù)庫(kù)10個(gè)表(每個(gè)月2個(gè)表),這樣每個(gè)庫(kù)的并發(fā)壓力和數(shù)據(jù)量都比較均勻。
2. 選擇分布式中間件。水平分庫(kù)分表需要用到分布式數(shù)據(jù)庫(kù)中間件,因?yàn)槭謩?dòng)維護(hù)多個(gè)庫(kù)、多個(gè)表的連接和查詢(xún),成本太高,而且容易出錯(cuò)。常用的中間件有Sharding-JDBC、MyCat,其中Sharding-JDBC是輕量級(jí)的,不需要單獨(dú)部署,直接集成到應(yīng)用中,適合中小規(guī)模的項(xiàng)目;MyCat是獨(dú)立部署的,功能更強(qiáng)大,適合大規(guī)模的項(xiàng)目。這里咱們以Sharding-JDBC為例,它的優(yōu)點(diǎn)是配置簡(jiǎn)單,對(duì)應(yīng)用透明,不用修改太多代碼。
3. 配置Sharding-JDBC,實(shí)現(xiàn)分庫(kù)分表。首先在應(yīng)用中引入Sharding-JDBC的依賴(lài),然后配置分庫(kù)規(guī)則和分表規(guī)則。比如配置分庫(kù)規(guī)則:按照數(shù)據(jù)庫(kù)的序號(hào),把60個(gè)表分布到6個(gè)庫(kù)中;配置分表規(guī)則:按訂單創(chuàng)建時(shí)間拆分月份,再按用戶(hù)ID哈希拆分表。配置完成后,Sharding-JDBC會(huì)自動(dòng)處理SQL的路由,比如查詢(xún)2026年3月某個(gè)用戶(hù)的訂單,它會(huì)自動(dòng)找到對(duì)應(yīng)的庫(kù)和表,執(zhí)行查詢(xún),應(yīng)用層不需要關(guān)心具體的庫(kù)和表位置。
4. 數(shù)據(jù)遷移和切換。和垂直分庫(kù)分表一樣,數(shù)據(jù)遷移需要在低峰期操作,先備份原來(lái)的訂單表數(shù)據(jù),然后按照拆分規(guī)則,把數(shù)據(jù)遷移到對(duì)應(yīng)的分庫(kù)分表中。遷移完成后,測(cè)試查詢(xún)、寫(xiě)入、修改等操作,確保無(wú)誤后,切換應(yīng)用的數(shù)據(jù)庫(kù)連接,從原來(lái)的單庫(kù)單表,切換到Sharding-JDBC的分庫(kù)分表模式。
5. 處理跨庫(kù)跨表查詢(xún)。水平分庫(kù)分表后,跨庫(kù)跨表查詢(xún)是不可避免的,比如查詢(xún)某個(gè)用戶(hù)所有的訂單,可能涉及多個(gè)月份的表,多個(gè)數(shù)據(jù)庫(kù)。Sharding-JDBC可以自動(dòng)處理跨庫(kù)跨表查詢(xún),它會(huì)把查詢(xún)請(qǐng)求發(fā)送到對(duì)應(yīng)的庫(kù)和表,執(zhí)行查詢(xún)后,把結(jié)果集合并,返回給應(yīng)用層。但要注意,跨庫(kù)跨表查詢(xún)的性能相對(duì)較差,盡量減少這種查詢(xún),比如可以在應(yīng)用層做緩存,把常用的跨表數(shù)據(jù)緩存起來(lái),提升查詢(xún)速度。
大數(shù)據(jù)數(shù)據(jù)庫(kù)架構(gòu)設(shè)計(jì):分庫(kù)分表之外,還要做好這3件事
很多人以為,做好分庫(kù)分表就萬(wàn)事大吉了,其實(shí)不然。在大數(shù)據(jù)場(chǎng)景下,MySQL的架構(gòu)設(shè)計(jì),除了分庫(kù)分表,還要做好緩存、讀寫(xiě)分離、容災(zāi)備份,這三者和分庫(kù)分表結(jié)合起來(lái),才能真正實(shí)現(xiàn)系統(tǒng)的高可用、高性能、高可靠。
一、緩存優(yōu)化:減輕數(shù)據(jù)庫(kù)的查詢(xún)壓力
分庫(kù)分表雖然提升了查詢(xún)速度,但如果并發(fā)量太高,比如每秒查詢(xún)請(qǐng)求達(dá)到幾萬(wàn),數(shù)據(jù)庫(kù)還是會(huì)有壓力。這時(shí)候,就需要用緩存來(lái)分擔(dān)數(shù)據(jù)庫(kù)的查詢(xún)壓力,把常用的數(shù)據(jù)緩存起來(lái),查詢(xún)的時(shí)候,先查緩存,緩存沒(méi)有再查數(shù)據(jù)庫(kù),這樣可以大幅減少數(shù)據(jù)庫(kù)的查詢(xún)次數(shù),提升系統(tǒng)的響應(yīng)速度。
常用的緩存中間件有Redis、Memcached,其中Redis應(yīng)用最廣泛,支持多種數(shù)據(jù)結(jié)構(gòu),緩存性能高,還能實(shí)現(xiàn)分布式緩存。緩存的實(shí)戰(zhàn)要點(diǎn):第一,緩存熱點(diǎn)數(shù)據(jù),比如用戶(hù)信息、商品信息、熱門(mén)訂單,這些數(shù)據(jù)查詢(xún)頻率高,緩存起來(lái)效果最好;第二,設(shè)置合理的緩存過(guò)期時(shí)間,避免緩存數(shù)據(jù)和數(shù)據(jù)庫(kù)數(shù)據(jù)不一致,比如用戶(hù)信息的緩存過(guò)期時(shí)間可以設(shè)置為1小時(shí),商品信息可以設(shè)置為6小時(shí);第三,處理緩存穿透、緩存擊穿、緩存雪崩問(wèn)題,這是緩存優(yōu)化的核心,也是最容易踩坑的地方。
比如緩存穿透,就是查詢(xún)一個(gè)不存在的數(shù)據(jù),緩存和數(shù)據(jù)庫(kù)都沒(méi)有,導(dǎo)致每次查詢(xún)都要訪問(wèn)數(shù)據(jù)庫(kù),浪費(fèi)資源。解決方案:對(duì)不存在的數(shù)據(jù),也緩存一個(gè)空值,設(shè)置較短的過(guò)期時(shí)間;或者用布隆過(guò)濾器,提前過(guò)濾掉不存在的數(shù)據(jù)。緩存擊穿,就是某個(gè)熱點(diǎn)數(shù)據(jù)的緩存過(guò)期了,大量請(qǐng)求同時(shí)訪問(wèn)數(shù)據(jù)庫(kù),導(dǎo)致數(shù)據(jù)庫(kù)壓力驟增。解決方案:給熱點(diǎn)數(shù)據(jù)設(shè)置永不過(guò)期,或者用互斥鎖,只有一個(gè)請(qǐng)求去數(shù)據(jù)庫(kù)查詢(xún),其他請(qǐng)求等待緩存更新。緩存雪崩,就是大量緩存同時(shí)過(guò)期,導(dǎo)致大量請(qǐng)求訪問(wèn)數(shù)據(jù)庫(kù),數(shù)據(jù)庫(kù)崩潰。解決方案:給緩存過(guò)期時(shí)間加上隨機(jī)值,避免大量緩存同時(shí)過(guò)期;或者搭建多級(jí)緩存,比如本地緩存+分布式緩存,即使分布式緩存過(guò)期,本地緩存也能臨時(shí)提供服務(wù)。
二、讀寫(xiě)分離:分擔(dān)數(shù)據(jù)庫(kù)的讀寫(xiě)壓力
大數(shù)據(jù)場(chǎng)景下,數(shù)據(jù)庫(kù)的讀寫(xiě)壓力往往不均衡,查詢(xún)請(qǐng)求遠(yuǎn)多于寫(xiě)入請(qǐng)求,比如電商系統(tǒng),用戶(hù)瀏覽商品、查詢(xún)訂單的請(qǐng)求,是下單、支付請(qǐng)求的10倍以上。這時(shí)候,就可以采用讀寫(xiě)分離的架構(gòu),把讀請(qǐng)求和寫(xiě)請(qǐng)求分開(kāi),主庫(kù)負(fù)責(zé)寫(xiě)入請(qǐng)求,從庫(kù)負(fù)責(zé)讀請(qǐng)求,這樣可以分擔(dān)主庫(kù)的壓力,提升系統(tǒng)的并發(fā)能力。
讀寫(xiě)分離的核心是“主從復(fù)制”,主庫(kù)寫(xiě)入數(shù)據(jù)后,通過(guò)主從復(fù)制,把數(shù)據(jù)同步到從庫(kù),從庫(kù)負(fù)責(zé)處理所有的讀請(qǐng)求。常用的主從復(fù)制方式有異步復(fù)制、半同步復(fù)制,異步復(fù)制的優(yōu)點(diǎn)是性能高,主庫(kù)寫(xiě)入后不用等待從庫(kù)同步,直接返回;缺點(diǎn)是數(shù)據(jù)一致性可能存在問(wèn)題,比如主庫(kù)崩了,從庫(kù)可能還沒(méi)同步最新的數(shù)據(jù)。半同步復(fù)制的優(yōu)點(diǎn)是數(shù)據(jù)一致性好,主庫(kù)寫(xiě)入后,必須等待至少一個(gè)從庫(kù)同步完成,才返回;缺點(diǎn)是性能比異步復(fù)制稍差。實(shí)際項(xiàng)目中,大多采用異步復(fù)制,結(jié)合緩存,平衡性能和一致性。
讀寫(xiě)分離的實(shí)操要點(diǎn):第一,配置主從復(fù)制,主庫(kù)開(kāi)啟二進(jìn)制日志,從庫(kù)配置主庫(kù)的地址和賬號(hào),實(shí)現(xiàn)數(shù)據(jù)同步;第二,通過(guò)中間件實(shí)現(xiàn)讀寫(xiě)分離路由,比如Sharding-JDBC、MyCat,自動(dòng)把寫(xiě)請(qǐng)求路由到主庫(kù),讀請(qǐng)求路由到從庫(kù);第三,處理主從延遲問(wèn)題,主從復(fù)制會(huì)有一定的延遲,比如1-3秒,這時(shí)候如果用戶(hù)剛下單,就查詢(xún)訂單,可能會(huì)查詢(xún)不到數(shù)據(jù)。解決方案:對(duì)實(shí)時(shí)性要求高的查詢(xún),路由到主庫(kù);對(duì)實(shí)時(shí)性要求不高的查詢(xún),路由到從庫(kù);或者在應(yīng)用層做延遲處理,比如下單后,延遲1秒再查詢(xún)。
三、容災(zāi)備份:確保數(shù)據(jù)安全,避免故障損失
大數(shù)據(jù)場(chǎng)景下,數(shù)據(jù)是核心資產(chǎn),一旦數(shù)據(jù)丟失或損壞,損失無(wú)法挽回。所以,容災(zāi)備份是數(shù)據(jù)庫(kù)架構(gòu)設(shè)計(jì)中必不可少的一環(huán),要做到“即使出現(xiàn)故障,也能快速恢復(fù)數(shù)據(jù),不影響業(yè)務(wù)正常運(yùn)行”。
容災(zāi)備份的核心是“多副本+定期備份”。多副本就是給每個(gè)數(shù)據(jù)庫(kù)配置多個(gè)從庫(kù),比如主庫(kù)+2個(gè)從庫(kù),即使主庫(kù)崩了,也可以快速切換到從庫(kù),繼續(xù)提供服務(wù)。定期備份就是定期對(duì)數(shù)據(jù)庫(kù)進(jìn)行備份,比如每天凌晨備份一次全量數(shù)據(jù),每小時(shí)備份一次增量數(shù)據(jù),備份數(shù)據(jù)要存儲(chǔ)在不同的地方,比如本地存儲(chǔ)+云存儲(chǔ),避免出現(xiàn)存儲(chǔ)設(shè)備損壞,導(dǎo)致備份數(shù)據(jù)丟失。
容災(zāi)備份的實(shí)操要點(diǎn):第一,配置多從庫(kù),實(shí)現(xiàn)主從切換,比如用Keepalived實(shí)現(xiàn)主庫(kù)故障自動(dòng)切換,當(dāng)主庫(kù)崩了,自動(dòng)把從庫(kù)提升為主庫(kù),保證業(yè)務(wù)不中斷;第二,制定合理的備份策略,全量備份+增量備份結(jié)合,全量備份每周一次,增量備份每天一次,備份完成后,要定期測(cè)試恢復(fù),確保備份數(shù)據(jù)可用;第三,異地容災(zāi),比如主庫(kù)和從庫(kù)放在不同的機(jī)房,即使一個(gè)機(jī)房出現(xiàn)故障,另一個(gè)機(jī)房的數(shù)據(jù)庫(kù)也能正常運(yùn)行,避免出現(xiàn)區(qū)域性故障導(dǎo)致數(shù)據(jù)丟失。
實(shí)戰(zhàn)避坑:分庫(kù)分表最容易踩的5個(gè)坑,看完少走彎路
分庫(kù)分表雖然好用,但實(shí)操過(guò)程中,很容易踩坑,很多公司就是因?yàn)椴攘诉@些坑,導(dǎo)致系統(tǒng)出現(xiàn)故障,甚至數(shù)據(jù)丟失。下面咱們總結(jié)5個(gè)最容易踩的坑,結(jié)合實(shí)戰(zhàn)經(jīng)驗(yàn),告訴大家怎么避開(kāi)。
第一個(gè)坑:盲目拆分,沒(méi)有規(guī)劃。很多人看到數(shù)據(jù)量增長(zhǎng),就急著分庫(kù)分表,沒(méi)有梳理業(yè)務(wù)場(chǎng)景,沒(méi)有確定拆分規(guī)則,盲目拆分后,不僅沒(méi)有提升性能,反而增加了系統(tǒng)復(fù)雜度,出現(xiàn)很多問(wèn)題。比如本來(lái)可以用垂直分庫(kù)解決的問(wèn)題,非要用水平分庫(kù)分表,導(dǎo)致開(kāi)發(fā)和維護(hù)成本大幅增加。避坑方法:先梳理業(yè)務(wù)場(chǎng)景,明確痛點(diǎn),確定是否需要分庫(kù)分表;如果需要,先從垂直分庫(kù)分表入手,逐步過(guò)渡到水平分庫(kù)分表,不要一步到位。
第二個(gè)坑:拆分規(guī)則不合理,導(dǎo)致數(shù)據(jù)分布不均。比如用哈希拆分,沒(méi)有選擇合適的哈希字段,導(dǎo)致某個(gè)表的數(shù)據(jù)量是其他表的10倍,出現(xiàn)熱點(diǎn)表,查詢(xún)速度還是很慢;或者用范圍拆分,沒(méi)有考慮業(yè)務(wù)峰值,導(dǎo)致某個(gè)時(shí)間段的表數(shù)據(jù)量過(guò)大。避坑方法:選擇合適的拆分規(guī)則,結(jié)合業(yè)務(wù)場(chǎng)景,盡量讓數(shù)據(jù)均勻分布;如果是范圍拆分,要考慮業(yè)務(wù)峰值,適當(dāng)調(diào)整拆分粒度,比如大促月份,拆分粒度可以細(xì)一點(diǎn),每月拆分成2個(gè)表。
第三個(gè)坑:忽略跨庫(kù)跨表查詢(xún)的性能問(wèn)題。很多人拆分后,沒(méi)有考慮跨庫(kù)跨表查詢(xún)的場(chǎng)景,導(dǎo)致查詢(xún)速度比單庫(kù)單表還慢。比如查詢(xún)某個(gè)用戶(hù)所有的訂單,需要遍歷多個(gè)庫(kù)、多個(gè)表,查詢(xún)時(shí)間大幅增加。避坑方法:盡量減少跨庫(kù)跨表查詢(xún),在應(yīng)用層做緩存,把常用的跨表數(shù)據(jù)緩存起來(lái);如果必須跨庫(kù)跨表查詢(xún),使用分布式中間件,優(yōu)化查詢(xún)語(yǔ)句,避免全表掃描。
第四個(gè)坑:沒(méi)有做好數(shù)據(jù)一致性。分庫(kù)分表后,數(shù)據(jù)分布在多個(gè)庫(kù)、多個(gè)表中,很容易出現(xiàn)數(shù)據(jù)不一致的問(wèn)題,比如下單后,訂單表寫(xiě)入成功,但庫(kù)存表寫(xiě)入失敗,導(dǎo)致訂單和庫(kù)存不一致。避坑方法:使用分布式事務(wù),比如Seata,保證跨庫(kù)操作的原子性;或者采用最終一致性方案,比如消息隊(duì)列,下單后發(fā)送消息,庫(kù)存服務(wù)消費(fèi)消息,更新庫(kù)存,如果失敗,進(jìn)行重試,確保最終數(shù)據(jù)一致。
第五個(gè)坑:忽略運(yùn)維成本。分庫(kù)分表后,數(shù)據(jù)庫(kù)的數(shù)量和表的數(shù)量大幅增加,運(yùn)維成本也會(huì)大幅提升,比如備份、恢復(fù)、監(jiān)控、故障排查,都比單庫(kù)單表復(fù)雜很多。避坑方法:選擇合適的中間件,簡(jiǎn)化運(yùn)維操作;搭建完善的監(jiān)控系統(tǒng),實(shí)時(shí)監(jiān)控每個(gè)庫(kù)、每個(gè)表的性能和狀態(tài),及時(shí)發(fā)現(xiàn)問(wèn)題;制定規(guī)范的運(yùn)維流程,比如備份、恢復(fù)、擴(kuò)容的流程,避免出現(xiàn)人為失誤。
MySQL分庫(kù)分表,核心是“拆分?jǐn)?shù)據(jù)、分擔(dān)壓力、提升性能”,垂直分庫(kù)分表適合業(yè)務(wù)初期,解決功能耦合和單庫(kù)壓力;水平分庫(kù)分表適合大數(shù)據(jù)、高并發(fā)場(chǎng)景,解決單表數(shù)據(jù)量太大的問(wèn)題。實(shí)際項(xiàng)目中,要結(jié)合業(yè)務(wù)場(chǎng)景,選擇合適的拆分規(guī)則和中間件,同時(shí)做好緩存、讀寫(xiě)分離、容災(zāi)備份,避開(kāi)常見(jiàn)的坑,才能真正發(fā)揮分庫(kù)分表的作用,構(gòu)建穩(wěn)定、高效的大數(shù)據(jù)數(shù)據(jù)庫(kù)架構(gòu)。
來(lái)源:上海門(mén)戶(hù)網(wǎng)?http://blog.nxtcbmw.cn/