杭州某大廠:MySQL 連環問(第二彈)

你們好,我是yes。java

MySQL 面試題又更新啦!面試

請繼續接招。redis

說說分庫分表?

隨着用戶量的激增和時間的堆砌,存在數據庫裏面的數據愈來愈多,此時的數據庫就會產生瓶頸,出現資源報警、查詢慢等場景。算法

首先單機數據庫所能承載的鏈接數、I/O及網絡的吞吐等都是有限的,因此當併發量上來了以後,數據庫就漸漸頂不住了。數據庫

再則,若是單表的數據量過大,查詢的性能也會降低。由於數據越多 B+ 樹就越高,樹越高則查詢 I/O 的次數就越多,那麼性能也就越差。緩存

由於上述的緣由,不得已就得上分庫分表了。服務器

把之前存在一個數據庫實例裏的數據拆分紅多個數據庫實例,部署在不一樣的服務器中,這是分庫。微信

把之前存在一張表裏面的數據拆分紅多張表,這是分表。網絡

通常而言:併發

  • 分表:是爲了解決因爲單張表數據量多大,而致使查詢慢的問題。大體3、四千萬行數據就得拆分,不過具體仍是得看每一行的數據量大小,有些字段都很小的可能支持更多行數,有些字段大的可能一千萬就頂不住了。

  • 分庫:是爲了解決服務器資源受單機限制,頂不住高併發訪問的問題,把請求分配到多臺服務器上,下降服務器壓力。

大家通常怎麼分庫的?

通常分庫都是按照業務劃分的,好比訂單庫、用戶庫等等。

有時候會針對一些特殊的庫再做切分,好比一些活動相關的庫都作了拆分。

由於作活動的時候併發可能會比較高,怕影響現有的核心業務,因此即便有關聯,也會單獨作拆分。

那你以爲分庫會帶來什麼問題呢?

首先是事務的問題。

咱們使用關係型數據庫,有很大一點在於它保證事務完整性。

而分庫以後單機事務就用不上了,必須使用分佈式事務來解決,而分佈式事務基本的都是殘缺的(我以前文章把分佈式事務彙總了一波,後臺搜索分佈式事務就有了)。

這是很重要的一點須要考慮。

連表 JOIN 問題

在一個庫中的時候咱們還能夠利用 JOIN 來連表查詢,而跨庫了以後就沒法使用 JOIN 了。

此時的解決方案就是在業務代碼中進行關聯,也就是先把一個表的數據查出來,而後經過獲得的結果再去查另外一張表,而後利用代碼來關聯獲得最終的結果。

這種方式實現起來稍微比較複雜,不過也是能夠接受的。

還有能夠適當的冗餘一些字段。好比之前的表就存儲一個關聯 ID,可是業務時常要求返回對應的 Name 或者其餘字段。這時候就能夠把這些字段冗餘到當前表中,來去除須要關聯的操做。

那大家怎麼分表的?

分表其實有兩種:

  • 垂直分表

  • 水平分表

垂直分表,來看個圖,很直觀:

垂直分表就是把一些不經常使用的大字段剝離出去。

像上面的例子:用戶名是很常見的搜索結果,性別和年齡佔用的空間又不大,而地址和我的簡介佔用的空間相對而言就較大,咱們都知道一個數據頁的空間是有限的,把一些無用的數據拆分出去,一頁就能存放更多行的數據。

內存存放更多有用的數據,就減小了磁盤的訪問次數,性能就獲得提高。

水平分表,則是由於一張表內的數據太多了,上文也提到了數據越多 B+ 樹就越高,訪問的性能就差,因此進行水平拆分。

其實無論這些,淺顯的理解下,在一百個數據裏面找一個數據快,仍是在一萬個數據裏面找一個數據快?

即便有索引,那厚的書目錄多,翻目錄也慢~

那分表會有什麼問題?

垂直分表還好,就是須要關聯一下,而水平分表就有點麻煩了。

排序、count、分頁問題

若是一個用戶的數據被拆分到多個表中,那查詢結果分頁就不像之前單張表那樣直接就能查出來了,像 count 操做也是同樣的。

只能由業務代碼來實現或者用中間件將各表中的數據彙總、排序、分頁而後返回。

像 count 操做的結果其實能夠緩存下來,而後每次數據增刪都更新計數。

路由問題

分表的路由能夠分:

  • Hash 路由

  • 範圍路由

  • 路由表

Hash 路由,其實就是選擇表中的某一列,而後進行 Hash 運算,將 Hash 運算獲得的結果再對子表數進行取模,這樣就能均勻的將數據分到不一樣的子表上。

這跟 HashMap 選哪一個桶是同樣的原理。

優勢就是數據分佈均勻。

缺點就是增長子表的時候麻煩,想一想 HashMap的擴容,是否是得搬遷數據?這個分表也是同樣的,咱們可都知道,數據遷移一件麻煩事!

範圍路由,其實很簡單,能夠是時間,也能夠是地址,表示必定的範圍的便可。

好比原本一張 User 表,我能夠分 User_HZ、User_BJ、User_SH,按照地名來劃分 User。

再好比 log 表,我能夠將表分爲 log_20210三、 log_202104,把日誌按照年月來劃分。

優勢就是相對而言比較容易擴展,好比如今來個 GZ,那就加個 User_GZ。若是到了 5 月,那就建個 log_202105。

缺點就是數據可能分佈不均勻,例如 BJ 的用戶特別多或者某個月搞了促銷,日誌量特別大,等等。

路由表,就是專門搞個表來記錄路由信息,來看個圖就很清楚了。

從圖中咱們就能得知,UserID 爲 2 的用戶數據在要去 User_3 這個用戶表查詢。

優勢就是靈活咯,若是要遷移數據,直接遷移而後路由表一改就完事兒了~

缺點就是得多查一次,每次查詢都須要訪問路由表,不過這個通常會作緩存的。

全局主鍵問題

之前單表的時候很簡單,就是主鍵自增,如今分表了以後就有點尷尬了。

因此須要一些手段來保證全局主鍵惟一。

  1. 仍是自增,只不過自增步長設置一下。好比如今有三張表,步長設置爲3,三張表 ID 初始值分別是一、二、3。這樣第一張表的 ID 增加是 一、四、7。第二張表是二、五、8。第三張表是三、六、9,這樣就不會重複了。

  2. UUID,這種最簡單,可是不連續的主鍵插入會致使嚴重的頁分裂,性能比較差。

  3. 分佈式 ID,比較出名的就是 Twitter 開源的 sonwflake 雪花算法,具體就不展開了,否則就又是一篇文章了,簡單點利用 redis 來遞增也行。

那上面說的路由問題的 Sharding-Key 如何設計呢?

咱們分表是按照某個列來拆分的,那個列就是 Sharding-Key,查詢的時候必須帶上這個列才行。

例如上面提到的  log_202103,那代表查詢條件必定得帶上日期,這樣才能找到正確的表。

因此設計上得考慮查詢的條件來做爲 Sharding-Key。

舉個經常會被問的訂單表 Sharding-Key 例子。

你想着查找訂單的時候會經過訂單號去找,因此應該利用訂單 ID 來做爲 Sharding-Key。

可是你想一想,你打開外賣軟件想查找你的歷史訂單的時候,你是沒有訂單 ID 的,你只有你的 UserID,那此時只能把全部子表都經過 UserID 遍歷一遍,這樣效率就很低了!

因此你想着那用 UserID 來做爲 Sharding-Key 吧!

可是,商家呢?商家確定關心本身今天賣了多少單,因此他也要查找訂單,但他只有本身的商家 ID,因此若是要查詢訂單,只能把全部子表都經過商家 ID 遍歷一遍,這樣效率就很低了!

因此 Sharding-Key 是知足不了全部查詢需求的,只能曲線救國。

通常作法就是冗餘數據。

將訂單同步到另外一張表中給商家使用,這個表按商家 ID 來做爲 Sharding-Key,也能夠將數據同步到 ES 中。通常而言這裏的數據同步都是異步處理,不會影響正常流程。

最後

今天的面試題主要是分庫分表相關的,基本上常問的都涵蓋了。

MySQL 面試題未完,持續更新~

由於分庫分表會帶來不少複雜性,因此能不分庫分表,就不要分庫分表。

這個前提請牢記。

還有,面試題交流羣持續開放,已經分享了近 50 個面經。

歡迎關注個人公衆號【yes的練級攻略】,更多硬核文章等你來讀。


我是 yes,從一點點到億點點,咱們下篇見~

本文分享自微信公衆號 - yes的練級攻略(yes_java)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索