天天看點

Mysql分庫分表方案

mysql分庫分表方案

1.為什麼要分表:

當一張表的資料達到幾千萬時,你查詢一次所花的時間會變多,如果有聯合查詢的話,我想有可能會死在那兒了。分表的目的就在于此,減小資料庫的負擔,縮短查詢時間。

mysql中有一種機制是表鎖定和行鎖定,是為了保證資料的完整性。

表鎖定表示你們都不能對這張表進行操作,必須等我對表操作完才行。

行鎖定也一樣,别的sql必須等我對這條資料操作完了,才能對這條資料進行操作。

mysql proxy:amoeba

做mysql叢集,利用amoeba。

從上層的java程式來講,不需要知道主伺服器和從伺服器的來源,即主從資料庫伺服器對于上層來講是透明的。可以通過amoeba來配置。

3.大資料量并且通路頻繁的表,将其分為若幹個表

比如對于某網站平台的資料庫表-公司表,資料量很大,這種能預估出來的大資料量表,我們就事先分出個n個表,這個n是多少,根據實際情況而定。

某網站現在的資料量至多是5000萬條,可以設計每張表容納的資料量是500萬條,也就是拆分成10張表,那麼如何判斷某張表的資料是否容量已滿呢?可以在程式段對于要新增資料的表,在插入前先做統計表記錄數量的操作,當<500萬條資料,就直接插入,當已經到達閥值,可以在程式段新建立資料庫表(或者已經事先建立好),再執行插入操作。

利用merge存儲引擎來實作分表

如果要把已有的大資料量表分開比較痛苦,最痛苦的事就是改代碼,因為程式裡面的sql語句已經寫好了。用merge存儲引擎來實作分表, 這種方法比較适合.

舉例子:

Mysql分庫分表方案

資料庫架構

1、簡單的mysql主從複制:

mysql的主從複制解決了資料庫的讀寫分離,并很好的提升了讀的性能,其圖如下:

其主從複制的過程如下圖所示:

Mysql分庫分表方案

但是,主從複制也帶來其他一系列性能瓶頸問題:

寫入無法擴充

寫入無法緩存

複制延時

鎖表率上升

表變大,緩存率下降

那問題産生總得解決的,這就産生下面的優化方案,一起來看看。

2、mysql垂直分區

如果把業務切割得足夠獨立,那把不同業務的資料放到不同的資料庫伺服器将是一個不錯的方案,而且萬一其中一個業務崩潰了也不會影響其他業務的正常進行,并且也起到了負載分流的作用,大大提升了資料庫的吞吐能力。經過垂直分區後的資料庫架構圖如下:

Mysql分庫分表方案

然而,盡管業務之間已經足夠獨立了,但是有些業務之間或多或少總會有點聯系,如使用者,基本上都會和每個業務相關聯,況且這種分區方式,也不能解決單張表資料量暴漲的問題,是以為何不試試水準分割呢?

3、mysql水準分片(sharding)

這是一個非常好的思路,将使用者按一定規則(按id哈希)分組,并把該組使用者的資料存儲到一個資料庫分片中,即一個sharding,這樣随着使用者數量的增加,隻要簡單地配置一台伺服器即可,原理圖如下:

Mysql分庫分表方案

如何來确定某個使用者所在的shard呢,可以建一張使用者和shard對應的資料表,每次請求先從這張表找使用者的shard id,再從對應shard中查詢相關資料,如下圖所示:

Mysql分庫分表方案

拆分政策選擇

其實拆分很靈活,有的是

垂直切分,将一個庫拆成兩個或多個,将有相關聯的表放在一個庫裡。有的是

水準切分将資料量大的表按照一定邏輯進行拆分。

個人感覺垂直切分的相對來說緩解了io的瓶頸,而水準切分,目的是減輕了單個表或某些表讀寫的壓力。

我們項目根據個人需求,采用的水準切分,沒有去分庫。之後要看看需要采用何種的切分了。

了解到的有: 分表、分區、merge引擎分表。

merge引擎分表

簡介

先介紹merge表,此方法隻适用于myisam。我資料庫的表都是采用innodb引擎的,是以首先就被pass了,但是還是在這裡簡單介紹下吧。

mysql 5.1 手冊裡的說的

Mysql分庫分表方案

改變到merge引擎表,意味着成為一個被分區的表,這樣将單一的表各分區存儲在分離的檔案中。分區可以使一些操作效率更顯著,并且不受myisam存儲引擎的限制。(蹩腳的英語,各位看官多擔待吧。)

以上應該是使用merge表的主要原因吧。

建立使用

能夠建立merge表的要求,首先是一組資料結構完全相同的表,并且存儲引擎為myisam。

讓我們先建立一個

Mysql分庫分表方案

之後查詢

Mysql分庫分表方案

你建立了total表,隻是相當于在t1,t2的表的基礎上建立的,需要注意的是在單個表中的主鍵或唯一索引,放在merge後的total表中就不能再當唯一索引用了,這點應該比較好了解但還是要說一下的。

同時你可以drop或者alter table tbl_name union=(...)改變表的資料集,這樣可以讓其動态變化,剔除不需要的。

使用場景

如果你的資料記錄呈現一定時間規律,比如每天産生的一些需要記錄的日志,可能你隻需要最近一個月的或者最近幾個月的,這樣你可以每天或者一定時間建立一個資料表,當需要查詢一段時間的資料,你隻要将這段時間的資料表建立一張總計的merge表。這樣資料集可以控制在可控的範圍呢,不錯吧。so easy。

分表

分表其實想法上很簡單,顧名思義就是将現有的一張資料量大的表去拆分。如果資料庫的性能瓶頸在幾個關鍵表上,這時你可以将分表列入你考慮的範圍。

遇到的問題

我說說我在實驗分表時遇到的問題和相關解決方式

1.如何去分表

根據什麼政策把現有表中的資料分到多個表中,并且還有考慮到以後的擴充性上。

是建立一張索引表,使用者id與資料庫id對應,(這裡他将相同結構的表分在了不同的資料庫中進一步減少壓力,但同時對于資料的同步也需要通過其他手段來解決),其本質也是分表了同時分庫了。這麼做的好處是便于以後的擴充,但損耗一點性能,因為會多一次查詢嘛。

個人想法,這樣索引表可能會成為新的瓶頸,除非使用者不會一直增長哈。

我的做法屬于另一種,寫了個算法通過計算某列值,按照一定規律将資料大緻均分在每個分表中。至于擴充性,寫算法時候考慮進去了以後增加分表數的問題了。

選擇哪種政策,是要看自己的表的業務特點了,方法沒有絕對的優缺,還是要根據自己的需求選取。

2.分表之後主鍵的維護

分表之前,主鍵就是自動遞增的bigint型。是以主鍵的格式已經提早被确定了,像什麼uuid之類的就被直接pass掉了。

還有想過自己寫一個主鍵生成程式,利用java 的atomic原子量特性,但是考慮還需要增加工作量并且高并發下,這裡很可能是個隐患。

還有就是通過應用層上管理主鍵,如redis中有原子性的遞增。

網上較有名的政策是《ticket servers: distributed unique primary keys on the cheap》, 大緻意思是使用一張名tickets64的myisam存儲引擎表,專門用來存儲主鍵,資料隻有一行,用的話通過

Mysql分庫分表方案

來取。并且設定了兩個庫,相同的方法,隻是每次增長的步長不同,防止一個宕掉,還可以穩定運作。

2.動态選擇表名

表分好之後,問題又來了,資料庫層我們的項目使用的是mybatis架構。sql語句都寫在了xml檔案中,現在我需要動态的設定表名。

其實設定mybatis本身,就可以解決這個問題

Mysql分庫分表方案

隻要把屬性statementtype設定為statement,表名就可以以參數形式傳入。傳入參數時要以美元符${columnname}這樣傳入參數,至于statement,preparedstatement 的差別我想大家應該都能知道的。

另一種解決方式,是使用《shardbatis插件》,它是開源的,可以實作資料水準切分功能,有興趣的朋友可以了解下。

分區表

從mysql5.1之後,提供了一種partition引擎的表,看這句

Mysql分庫分表方案

在我的了解,如果把一張表分區之後,不同分區放在不同磁盤位置上,對整體的讀取是否更有益?

分區表優缺點

這裡主要是看的mysql手冊,我也就起到了個翻譯的作用。

Mysql分庫分表方案

在選擇mysql 分區方案時,還有一個需要考慮的,在mysql的bug中有一個關于mysql分區表查詢緩存的bug: 《partitioning + query cache》,因為這個問題,mysql已經将分區表的查詢緩存disable了,無論你是否開啟查詢緩存,都不會啟用查詢緩存。如果你在意這點,請慎重選擇方案。

以上是關于,mysql三個拆分方案的總結,資料方面都是自己查找的是以不免有些會不準确,如有發現請務必告知,希望與各位共成長~~~。