1.爲何要分庫分表(設計高併發系統的時候,數據庫層面該如何設計)?用過哪些分庫分表中間件?不一樣的分庫分表中間件都有什麼優勢和缺點?
2.如今有一個未分庫分表的系統,將來要分庫分表,如何設計纔可讓系統從未分庫分表動態切換到分庫分表上
3.如何設計能夠動態擴容縮容的分庫分表方案?
4.分庫分表以後,id主鍵如何處理?
5.mysql的主從同步與讀寫分離
(1)如何實現mysql的讀寫分離?
(2)MySQL主從複製原理的是啥?
(3)mysql主從同步延時問題
6.mysql何時建立索引
7.mysql索引底層數據結構與算法mysql
1.爲何要分庫分表(設計高併發系統的時候,數據庫層面該如何設計)?用過哪些分庫分表中間件?不一樣的分庫分表中間件都有什麼優勢和缺點?
大傢俱體是如何對數據庫如何進行垂直拆分或水平拆分的?算法
實際上這是跟着你的公司業務發展走的,你公司業務發展越好,用戶就越多,數據量越大,請求量越大,那你單個數據庫必定扛不住。
好比你單表都幾千萬數據了,你肯定你能抗住麼?絕對不行,單表數據量太大,會極大影響你的sql執行的性能,到了後面你的sql可能就跑的很慢了。通常來講,就以個人經驗來看,單表到幾百萬的時候,性能就會相對差一些了,你就得分表了。
分表是啥意思?就是把一個表的數據放到多個表中,而後查詢的時候你就查一個表。好比按照用戶id來分表,將一個用戶的數據就放在一個表中。而後操做的時候你對一個用戶就操做那個表就行了。這樣能夠控制每一個表的數據量在可控的範圍內,好比每一個表就固定在200萬之內。
分庫是啥意思?就是你一個庫通常咱們經驗而言,最多支撐到併發2000,必定要擴容了,並且一個健康的單庫併發值你最好保持在每秒1000左右,不要太大。那麼你能夠將一個庫的數據拆分到多個庫中,訪問的時候就訪問一個庫好了。sql
sharding-jdbc這種client層方案的優勢在於不用部署,運維成本低,不須要代理層的二次轉發請求,性能很高,可是若是遇到升級啥的須要各個系統都從新升級版本再發布,各個系統都須要耦合sharding-jdbc的依賴;
mycat這種proxy層方案的缺點在於須要部署,本身及運維一套中間件,運維成本高,可是好處在於對於各個項目是透明的,若是遇到升級之類的都是本身中間件那裏搞就好了。
一般來講,這兩個方案其實均可以選用,可是我我的建議中小型公司選用sharding-jdbc,client層方案輕便,並且維護成本低,不須要額外增派人手,並且中小型公司系統複雜度會低一些,項目也沒那麼多;
可是中大型公司最好仍是選用mycat這類proxy層方案,由於可能大公司系統和項目很是多,團隊很大,人員充足,那麼最好是專門弄我的來研究和維護mycat,而後大量項目直接透明使用便可。數據庫
如何對數據庫如何進行垂直拆分或水平拆分的?
水平拆分的意思,就是把一個表的數據給弄到多個庫的多個表裏去,可是每一個庫的表結構都同樣,只不過每一個庫表放的數據是不一樣的,全部庫表的數據加起來就是所有數據。水平拆分的意義,就是將數據均勻放更多的庫裏,而後用多個庫來抗更高的併發,還有就是用多個庫的存儲容量來進行擴容。
垂直拆分的意思,就是把一個有不少字段的表給拆分紅多個表,或者是多個庫上去。每一個庫表的結構都不同,每一個庫表都包含部分字段。通常來講,會將較少的訪問頻率很高的字段放到一個表裏去,而後將較多的訪問頻率很低的字段放到另一個表裏去。由於數據庫是有緩存的,你訪問頻率高的行字段越少,就能夠在緩存裏緩存更多的行,性能就越好。這個通常在表層面作的較多一些。
這個其實挺常見的,不必定我說,你們不少同窗可能本身都作過,把一個大表拆開,訂單表、訂單支付表、訂單商品表。
還有表層面的拆分,就是分表,將一個表變成N個表,就是讓每一個表的數據量控制在必定範圍內,保證SQL的性能。不然單表數據量越大,SQL性能就越差。通常是200萬行左右,不要太多,可是也得看具體你怎麼操做,也多是500萬,或者是100萬。你的SQL越複雜,就最好讓單錶行數越少。
好了,不管是分庫了仍是分表了,上面說的那些數據庫中間件都是能夠支持的。就是基本上那些中間件能夠作到你分庫分表以後,中間件能夠根據你指定的某個字段值,好比說userid,自動路由到對應的庫上去,而後再自動路由到對應的表裏去。
你就得考慮一下,你的項目裏該如何分庫分表?通常來講,垂直拆分,你能夠在表層面來作,對一些字段特別多的表作一下拆分;水平拆分,你能夠說是併發承載不了,或者是數據量太大,容量承載不了,你給拆了,按什麼字段來拆,你本身想好;分表,你考慮一下,你若是哪怕是拆到每一個庫裏去,併發和容量都ok了,可是每一個庫的表仍是太大了,那麼你就分表,將這個表分開,保證每一個表的數據量並非很大。
並且這兒還有兩種分庫分表的方式,一種是按照range來分,就是每一個庫一段連續的數據,這個通常是按好比時間範圍來的,可是這種通常較少用,由於很容易產生熱點問題,大量的流量都打在最新的數據上了;或者是按照某個字段hash一下均勻分散,這個較爲經常使用。
range來分,好處在於說,後面擴容的時候,就很容易,由於你只要預備好,給每月都準備一個庫就能夠了,到了一個新的月份的時候,天然而然,就會寫新的庫了;缺點,可是大部分的請求,都是訪問最新的數據。實際生產用range,要看場景,你的用戶不是僅僅訪問最新的數據,而是均勻的訪問如今的數據以及歷史的數據
hash分法,好處在於說,能夠平均分配沒給庫的數據量和請求壓力;壞處在於說擴容起來比較麻煩,會有一個數據遷移的這麼一個過程緩存
2.如今有一個未分庫分表的系統,將來要分庫分表,如何設計纔可讓系統從未分庫分表動態切換到分庫分表上
雙寫遷移方案
這個是咱們經常使用的一種遷移方案,比較靠譜一些,不用停機,不用看北京凌晨4點的風景
簡單來講,就是在線上系統裏面,以前全部寫庫的地方,增刪改操做,都除了對老庫增刪改,都加上對新庫的增刪改,這就是所謂雙寫,同時寫倆庫,老庫和新庫。
而後系統部署以後,新庫數據差太遠,用以前說的導數工具,跑起來讀老庫數據寫新庫,寫的時候要根據gmt_modified這類字段判斷這條數據最後修改的時間,除非是讀出來的數據在新庫裏沒有,或者是比新庫的數據新纔會寫。
接着導完一輪以後,有可能數據仍是存在不一致,那麼就程序自動作一輪校驗,比對新老庫每一個表的每條數據,接着若是有不同的,就針對那些不同的,從老庫讀數據再次寫。反覆循環,直到兩個庫每一個表的數據都徹底一致爲止。
接着當數據徹底一致了,就ok了,基於僅僅使用分庫分表的最新代碼,從新部署一次,不就僅僅基於分庫分表在操做了麼,尚未幾個小時的停機時間,很穩。因此如今基本玩兒數據遷移之類的,都是這麼幹了。服務器
3.如何設計能夠動態擴容縮容的分庫分表方案?
(1)選擇一個數據庫中間件,調研、學習、測試
(2)設計你的分庫分表的一個方案,你要分紅多少個庫,每一個庫分紅多少個表,3個庫每一個庫4個表
(3)基於選擇好的數據庫中間件,以及在測試環境創建好的分庫分表的環境,而後測試一下可否正常進行分庫分表的讀寫
(4)完成單庫單表到分庫分表的遷移,雙寫方案
(5)線上系統開始基於分庫分表對外提供服務
(6)擴容了,擴容成6個庫,每一個庫須要12個表,你怎麼來增長更多庫和表呢?數據結構
一開始上來就是32個庫,每一個庫32個表,1024張表
我能夠告訴各位同窗說,這個分法,第一,基本上國內的互聯網確定都是夠用了,第二,不管是併發支撐仍是數據量支撐都沒問題
每一個庫正常承載的寫入併發量是1000,那麼32個庫就能夠承載32 * 1000 = 32000的寫併發,若是每一個庫承載1500的寫併發,32 * 1500 = 48000的寫併發,接近5萬/s的寫入併發,前面再加一個MQ,削峯,每秒寫入MQ 8萬條數據,每秒消費5萬條數據。
有些除非是國內排名很是靠前的這些公司,他們的最核心的系統的數據庫,可能會出現幾百臺數據庫的這麼一個規模,128個庫,256個庫,512個庫
1024張表,假設每一個表放500萬數據,在MySQL裏能夠放50億條數據
每秒的5萬寫併發,總共50億條數據,對於國內大部分的互聯網公司來講,其實通常來講都夠了
談分庫分表的擴容,第一次分庫分表,就一次性給他分個夠,32個庫,1024張表,可能對大部分的中小型互聯網公司來講,已經能夠支撐好幾年了
一個實踐是利用32 * 32來分庫分表,即分爲32個庫,每一個庫裏一個表分爲32張表。一共就是1024張表。根據某個id先根據32取模路由到庫,再根據32取模路由到庫裏的表。
剛開始的時候,這個庫可能就是邏輯庫,建在一個數據庫上的,就是一個mysql服務器可能建了n個庫,好比16個庫。後面若是要拆分,就是不斷在庫和mysql服務器之間作遷移就能夠了。而後系統配合改一下配置便可。
好比說最多能夠擴展到32個數據庫服務器,每一個數據庫服務器是一個庫。若是仍是不夠?最多能夠擴展到1024個數據庫服務器,每一個數據庫服務器上面一個庫一個表。由於最可能是1024個表麼。
這麼搞,是不用本身寫代碼作數據遷移的,都交給dba來搞好了,可是dba確實是須要作一些庫表遷移的工做,可是總比你本身寫代碼,抽數據導數據來的效率高得多了。
哪怕是要減小庫的數量,也很簡單,其實說白了就是按倍數縮容就能夠了,而後修改一下路由規則。
對2 ^ n取模
orderId 模 32 = 庫
orderId / 32 模 32 = 表
259 3 8
1189 5 5
352 0 11
4593 17 15架構
一、設定好幾臺數據庫服務器,每臺服務器上幾個庫,每一個庫多少個表,推薦是32庫 * 32表,對於大部分公司來講,可能幾年都夠了
二、路由的規則,orderId 模 32 = 庫,orderId / 32 模 32 = 表
三、擴容的時候,申請增長更多的數據庫服務器,裝好mysql,倍數擴容,4臺服務器,擴到8臺服務器,16臺服務器
四、由dba負責將原先數據庫服務器的庫,遷移到新的數據庫服務器上去,不少工具,庫遷移,比較便捷
五、咱們這邊就是修改一下配置,調整遷移的庫所在數據庫服務器的地址
六、從新發布系統,上線,原先的路由規則變都不用變,直接能夠基於2倍的數據庫服務器的資源,繼續進行線上系統的提供服務併發
4.分庫分表以後,id主鍵如何處理?
snowflake算法
twitter開源的分佈式id生成算法,就是把一個64位的long型的id,1個bit是不用的,用其中的41 bit做爲毫秒數,用10 bit做爲工做機器id,12 bit做爲序列號
1 bit:不用,爲啥呢?由於二進制裏第一個bit爲若是是1,那麼都是負數,可是咱們生成的id都是正數,因此第一個bit統一都是0
41 bit:表示的是時間戳,單位是毫秒。41 bit能夠表示的數字多達2^41 - 1,也就是能夠標識2 ^ 41 - 1個毫秒值,換算成年就是表示69年的時間。
10 bit:記錄工做機器id,表明的是這個服務最多能夠部署在2^10臺機器上哪,也就是1024臺機器。可是10 bit裏5個bit表明機房id,5個bit表明機器id。意思就是最多表明2 ^ 5個機房(32個機房),每一個機房裏能夠表明2 ^ 5個機器(32臺機器)。
12 bit:這個是用來記錄同一個毫秒內產生的不一樣id,12 bit能夠表明的最大正整數是2 ^ 12 - 1 = 4096,也就是說能夠用這個12bit表明的數字來區分同一個毫秒內的4096個不一樣的id
64位的long型的id,64位的long -> 二進制
0 | 0001100 10100010 10111110 10001001 01011100 00 | 10001 | 1 1001 | 0000 00000000
2018-01-01 10:00:00 -> 作了一些計算,再換算成一個二進制,41bit來放 -> 0001100 10100010 10111110 10001001 01011100 00
機房id,17 -> 換算成一個二進制 -> 10001
機器id,25 -> 換算成一個二進制 -> 11001
snowflake算法服務,會判斷一下,當前這個請求是不是,機房17的機器25,在2175/11/7 12:12:14時間點發送過來的第一個請求,若是是第一個請
假設,在2175/11/7 12:12:14時間裏,機房17的機器25,發送了第二條消息,snowflake算法服務,會發現說機房17的機器25,在2175/11/7 12:12:14時間裏,在這一毫秒,以前已經生成過一個id了,此時若是你同一個機房,同一個機器,在同一個毫秒內,再次要求生成一個id,此時我只能把加1
0 | 0001100 10100010 10111110 10001001 01011100 00 | 10001 | 1 1001 | 0000 00000001
好比咱們來觀察上面的那個,就是一個典型的二進制的64位的id,換算成10進制就是910499571847892992。運維
怎麼說呢,大概這個意思吧,就是說41 bit,就是當前毫秒單位的一個時間戳,就這意思;而後5 bit是你傳遞進來的一個機房id(可是最大隻能是32之內),5 bit是你傳遞進來的機器id(可是最大隻能是32之內),剩下的那個10 bit序列號,就是若是跟你上次生成id的時間還在一個毫秒內,那麼會把順序給你累加,最多在4096個序號之內。
因此你本身利用這個工具類,本身搞一個服務,而後對每一個機房的每一個機器都初始化這麼一個東西,剛開始這個機房的這個機器的序號就是0。而後每次接收到一個請求,說這個機房的這個機器要生成一個id,你就找到對應的Worker,生成。
他這個算法生成的時候,會把當前毫秒放到41 bit中,而後5 bit是機房id,5 bit是機器id,接着就是判斷上一次生成id的時間若是跟此次不同,序號就自動從0開始;要是上次的時間跟如今仍是在一個毫秒內,他就把seq累加1,就是自動生成一個毫秒的不一樣的序號。
這個算法那,能夠確保說每一個機房每一個機器每一毫秒,最多生成4096個不重複的id。
利用這個snowflake算法,你能夠開發本身公司的服務,甚至對於機房id和機器id,反正給你預留了5 bit + 5 bit,你換成別的有業務含義的東西也能夠的。
這個snowflake算法相對來講仍是比較靠譜的,因此你要真是搞分佈式id生成,若是是高併發啥的,那麼用這個應該性能比較好,通常每秒幾萬併發的場景,也足夠你用了。
5.mysql的主從同步與讀寫分離
(1)如何實現mysql的讀寫分離?
其實很簡單,就是基於主從複製架構,簡單來講,就搞一個主庫,掛多個從庫,而後咱們就單單只是寫主庫,而後主庫會自動把數據給同步到從庫上去。
(2)MySQL主從複製原理的是啥?
主庫將變動寫binlog日誌,而後從庫鏈接到主庫以後,從庫有一個IO線程,將主庫的binlog日誌拷貝到本身本地,寫入一箇中繼日誌中。接着從庫中有一個SQL線程會從中繼日誌讀取binlog,而後執行binlog日誌中的內容,也就是在本身本地再次執行一遍SQL,這樣就能夠保證本身跟主庫的數據是同樣的。
這裏有一個很是重要的一點,就是從庫同步主庫數據的過程是串行化的,也就是說主庫上並行的操做,在從庫上會串行執行。因此這就是一個很是重要的點了,因爲從庫從主庫拷貝日誌以及串行執行SQL的特色,在高併發場景下,從庫的數據必定會比主庫慢一些,是有延時的。因此常常出現,剛寫入主庫的數據多是讀不到的,要過幾十毫秒,甚至幾百毫秒才能讀取到。
並且這裏還有另一個問題,就是若是主庫忽然宕機,而後剛好數據還沒同步到從庫,那麼有些數據可能在從庫上是沒有的,有些數據可能就丟失了。
因此mysql實際上在這一塊有兩個機制,一個是半同步複製,用來解決主庫數據丟失問題;一個是並行複製,用來解決主從同步延時問題。
這個所謂半同步複製,semi-sync複製,指的就是主庫寫入binlog日誌以後,就會將強制此時當即將數據同步到從庫,從庫將日誌寫入本身本地的relay log以後,接着會返回一個ack給主庫,主庫接收到至少一個從庫的ack以後纔會認爲寫操做完成了。
所謂並行複製,指的是從庫開啓多個線程,並行讀取relay log中不一樣庫的日誌,而後並行重放不一樣庫的日誌,這是庫級別的並行。
1)主從複製的原理
2)主從延遲問題產生的緣由
3)主從複製的數據丟失問題,以及半同步複製的原理
4)並行複製的原理,多庫併發重放relay日誌,緩解主從延遲問題
(3)mysql主從同步延時問題(精華)
線上確實處理過由於主從同步延時問題,致使的線上的bug,小型的生產事故
show status,Seconds_Behind_Master,你能夠看到從庫複製主庫的數據落後了幾ms
其實這塊東西咱們常常會碰到,就好比說用了mysql主從架構以後,可能會發現,剛寫入庫的數據結果沒查到,結果就完蛋了。。。。
因此實際上你要考慮好應該在什麼場景下來用這個mysql主從同步,建議是通常在讀遠遠多於寫,並且讀的時候通常對數據時效性要求沒那麼高的時候,用mysql主從同步
因此這個時候,咱們能夠考慮的一個事情就是,你能夠用mysql的並行複製,可是問題是那是庫級別的並行,因此有時候做用不是很大
因此這個時候。。一般來講,咱們會對於那種寫了以後立馬就要保證能夠查到的場景,採用強制讀主庫的方式,這樣就能夠保證你確定的能夠讀到數據了吧。其實用一些數據庫中間件是沒問題的。
通常來講,若是主從延遲較爲嚴重
一、分庫,將一個主庫拆分爲4個主庫,每一個主庫的寫併發就500/s,此時主從延遲能夠忽略不計
二、打開mysql支持的並行複製,多個庫並行複製,若是說某個庫的寫入併發就是特別高,單庫寫併發達到了2000/s,並行複製仍是沒意義。28法則,不少時候好比說,就是少數的幾個訂單表,寫入了2000/s,其餘幾十個表10/s。
三、重寫代碼,寫代碼的同窗,要慎重,當時咱們其實短時間是讓那個同窗重寫了一下代碼,插入數據以後,直接就更新,不要查詢
四、若是確實是存在必須先插入,立馬要求就查詢到,而後立馬就要反過來執行一些操做,對這個查詢設置直連主庫。
不推薦這種方法,你這麼搞致使讀寫分離的意義就喪失了
6.mysql何時建立索引
1.索引是一套數據結構
2.優點:查詢快/劣勢:下降更新表的速度,寫操做慢,查操做快,兩套數據
3.什麼狀況下建立索引
主鍵自動創建惟一索引
頻繁做爲查詢條件的字段建立索引
外鍵關係建立索引
單鍵/組合索引的選擇問題,組合索引性價比更高
查詢中排序的字段,排序字段若經過索引去訪問將大大提升排序速度
查詢中統計或者分組字段 group by/order by
7.mysql索引底層數據結構與算法
索引的數據結構 二叉樹/HASH/BTREEBtree 度(Degree)-節點的數據存儲個數 橫向變長,高度變少,節點查找是在內存裏一次IO是4K數據,節點的度就是4K數據B+Tree 非葉子節點不存儲data,只存儲key,能夠增大度通常使用磁盤IO次數評價索引結構的優劣myisam索引實現 存儲引擎是表級別索引和數據是分離的 葉子節點存的是文件指針不是數據主鍵索引/非主鍵索引/分開存儲的innodb 主鍵索引 數據文件自己就是索引文件 葉子節點存儲就是數據innodb必需要有主鍵 整型自增主鍵非主鍵索引葉子節點存儲的是主鍵,並非數據,須要查找2次才能找到數據聯合索引的底層存儲結構同上