1. 程式人生 > >MySQL 對於千萬級的大表要怎麼優化

MySQL 對於千萬級的大表要怎麼優化

千萬級,MySQL實際上確實不是什麼壓力,InnoDB的儲存引擎,使用的是B+樹儲存結構,千萬級的資料量,基本也就是三到四層的搜尋,如果有合適的索引,效能基本也不是問題。

但經常出現的情況是,業務上面的增長,導致資料量還會繼續增長,為了應對這方面的問題而必須要做擴充套件了此時可能首先需要考慮的就是分表策略了。

當然分表,可能還有其它幾個原因,比如表變大了,千萬級的資料庫,為了減少運維成本,降低風險,就想到了通過分表來解決問題,這都是比較合適的。

分表,還有另一個方面的意思,就是在資料量更大的情況下,為了分擔業務壓力,將資料表分到不同的例項中去,這樣有兩方面的好處:1. 降低業務風險,如果一套資料庫叢集出問題了,那至少還有其它的可以服務,這樣被影響的業務可能只是一部分。2. 降低運維成本,如果資料庫想要做遷移,或者正常維護等操作了,那涉及到的資料量小,下線時間短,操作快,從而對業務影響也就小了。這種方式,我們稱之為“分例項”。

分表的話,還是要根據具體的業務邏輯等方面來做,這方面有更精彩的回答,我這裡貼一下:

========================================

分庫分表是MySQL永遠的話題,一般情況下認為MySQL是個簡單的資料庫,在資料量大到一定程度之後處理查詢的效率降低,如果需要繼續保持高效能運轉的話,必須分庫或者分表了。關於資料量達到多少大是個極限這個事兒,本文先不討論,研究原始碼的同學已經證實MySQL或者Innodb內部的鎖粒度太大的問題大大限制了MySQL提供QPS的能力或者處理大規模資料的能力。在這點上,一般的使用者只好坐等官方不斷推出的優化版本了。

在一般運維的角度來看,我們什麼情況下需要考慮分庫分表?

首先說明,這裡所說的分庫分表是指把資料庫資料的物理拆分到多個例項或者多臺機器上去,而不是類似分割槽表的原地切分。

原則零:能不分就不分。

是的,MySQL 是關係資料庫,資料庫表之間的關係從一定的角度上映射了業務邏輯。任何分庫分表的行為都會在某種程度上提升業務邏輯的複雜度,資料庫除了承載資料的儲存和訪問外,協助業務更好的實現需求和邏輯也是其重要工作之一。分庫分表會帶來資料的合併,查詢或者更新條件的分離,事務的分離等等多種後果,業務實現的複雜程度往往會翻倍或者指數級上升。所以,在分庫分表之前,不要為分而分,去做其他力所能及的事情吧,例如升級硬體,升級,升級網路,升級資料庫版本,讀寫分離,負載均衡等等。所有分庫分表的前提是,這些你已經盡力了。

原則一:資料量太大,正常的運維影響正常業務訪問。

這裡說的運維,例如:

(1)對資料庫的備份。如果單表或者單個例項太大,在做備份的時候需要大量的磁碟IO或者網路IO資源。例如1T的資料,網路傳輸佔用50MB的時候,需要20000秒才能傳輸完畢,在此整個過程中的維護風險都是高於平時的。我們在Qunar的做法是給所有的資料庫機器新增第二塊網絡卡,用來做備份,或者SST,Group Communication等等各種內部的資料傳輸。1T的資料的備份,也會佔用大量的磁碟IO,如果是SSD還好,當然這裡忽略某些廠商的產品在集中IO的時候會出一些BUG的問題。如果是普通的物理磁碟,則在不限流的情況下去執行xtrabackup,該例項基本不可用。

(2)對資料表的修改。如果某個表過大,對此表做DDL的時候,MySQL會鎖住全表,這個時間可能很長,在這段時間業務不能訪問此表,影響甚大。解決的辦法有類似騰訊遊戲DBA自己改造的可以線上秒改表,不過他們目前也只是能新增欄位而已,對別的DDL還是無效;或者使用pt-online-schema-change,當然在使用過程中,它需要建立觸發器和影子表,同時也需要很長很長的時間,在此操作過程中的所有時間,都可以看做是風險時間。把資料表切分,總量減小,有助於改善這種風險。

(3)整個表熱點,資料訪問和更新頻繁,經常有鎖等待,你又沒有能力去修改原始碼,降低鎖的粒度,那麼只會把其中的資料物理拆開,用空間換時間,變相降低訪問壓力。

原則二:表設計不合理,需要對某些欄位垂直拆分

這裡舉一個例子,如果你有一個使用者表,在最初設計的時候可能是這樣:

table :users

id bigint 使用者的ID

name varchar 使用者的名字

last_login_time datetime 最近登入時間

personal_info text 私人資訊

xxxxx 其他資訊欄位。

一般的users表會有很多欄位,我就不列舉了。如上所示,在一個簡單的應用中,這種設計是很常見的。但是:

設想情況一:你的業務中彩了,使用者數從100w飆升到10個億。你為了統計活躍使用者,在每個人登入的時候都會記錄一下他的最近登入時間。並且的使用者活躍得很,不斷的去更新這個login_time,搞的你的這個表不斷的被update,壓力非常大。那麼,在這個時候,只要考慮對它進行拆分,站在業務的角度,最好的辦法是先把last_login_time拆分出去,我們叫它 user_time。這樣做,業務的程式碼只有在用到這個欄位的時候修改一下就行了。如果你不這麼做,直接把users表水平切分了,那麼,所有訪問users表的地方,都要修改。或許你會說,我有proxy,能夠動態merge資料。到目前為止我還從沒看到誰家的proxy不影響效能的。

設想情況二:personal_info這個欄位本來沒啥用,你就是讓使用者註冊的時候填一些個人愛好而已,基本不查詢。一開始的時候有它沒它無所謂。但是到後來發現兩個問題,一,這個欄位佔用了大量的空間,因為是text嘛,有很多人喜歡長篇大論地介紹自己。更糟糕的是二,不知道哪天哪個產品經理心血來潮,說允許個人資訊公開吧,以方便讓大家更好的相互瞭解。那麼在所有人獵奇窺私心理的影響下,對此欄位的訪問大幅度增加。資料庫壓力瞬間抗不住了,這個時候,只好考慮對這個表的垂直拆分了。

原則三:某些資料表出現了無窮增長

例子很好舉,各種的評論,訊息,日誌記錄。這個增長不是跟人口成比例的,而是不可控的,例如微博的feed的廣播,我發一條訊息,會擴散給很多很多人。雖然主體可能只存一份,但不排除一些索引或者路由有這種儲存需求。這個時候,增加儲存,提升機器配置已經蒼白無力了,水平切分是最佳實踐。拆分的標準很多,按使用者的,按時間的,按用途的,不在一一舉例。

原則四:安全性和可用性的考慮

這個很容易理解,雞蛋不要放在一個籃子裡,我不希望我的資料庫出問題,但我希望在出問題的時候不要影響到100%的使用者,這個影響的比例越少越好,那麼,水平切分可以解決這個問題,把使用者,庫存,訂單等等本來同統一的資源切分掉,每個小的資料庫例項承擔一小部分業務,這樣整體的可用性就會提升。這對Qunar這樣的業務還是比較合適的,人與人之間,某些庫存與庫存之間,關聯不太大,可以做一些這樣的切分。

原則五:業務耦合性考慮

這個跟上面有點類似,主要是站在業務的層面上,我們的火車票業務和烤羊腿業務是完全無關的業務,雖然每個業務的資料量可能不太大,放在一個MySQL例項中完全沒問題,但是很可能烤羊腿業務的DBA 或者開發人員水平很差,動不動給你出一些么蛾子,直接把資料庫搞掛。這個時候,火車票業務的人員雖然技術很優秀,工作也很努力,照樣被老闆打屁股。解決的辦法很簡單:惹不起,躲得起。

《三國演義》第一回:“話說天下大勢,分久必合,合久必分。”其實在實踐中,有時候可能你原本要分,後來又發現分了還得合,分分合合,完全是現實的需求,隨需而變才是王道,而DBA的價值也能在此體現。或分或合的情況太多,不能窮舉,歡迎繼續交流這個話題,如果以上有錯誤之後,也請批評指正。

給生活加點料。

================================

文章摘自微信公眾號formysql。

如何分表的方案,其實這個不能一概而論,與業務邏輯有關係,與資料性質有關係,比如訂單型別的,那就非常容易了,通過時間這個特性,可以通過一個路由表,把資料分散到多個例項上面,或者多個表上面,擴充套件性非常強,但是如果是使用者關係等類似的表,他的唯一可以做HASH的值就是使用者ID,做HASH時,涉及到不均勻、可擴充套件能力,遷移麻煩等問題,所以還是不太容易的,所以只能是具體問題具體分析了。

上面說的是分表的優化方案,當然還有其它方案,那就是要儘可能的寫好SQL語句,不要留坑,MySQL就是適合那種快進快出的語句,儘可能的別把業務邏輯放到MySQL中去處理,要保持MySQL的高效執行才是最正確的選擇。