mysql分庫分表方案
1.為什麼要分表:
當一張表的資料達到幾千萬時,你查詢一次所花的時間會變多,如果有聯合查詢的話,我想有可能會死在那兒了。分表的目的就在于此,減小資料庫的負擔,縮短查詢時間。
mysql中有一種機制是表鎖定和行鎖定,是為了保證資料的完整性。
表鎖定表示你們都不能對這張表進行操作,必須等我對表操作完才行。
行鎖定也一樣,别的sql必須等我對這條資料操作完了,才能對這條資料進行操作。
mysql proxy:amoeba
做mysql叢集,利用amoeba。
從上層的java程式來講,不需要知道主伺服器和從伺服器的來源,即主從資料庫伺服器對于上層來講是透明的。可以通過amoeba來配置。
3.大資料量并且通路頻繁的表,将其分為若幹個表
比如對于某網站平台的資料庫表-公司表,資料量很大,這種能預估出來的大資料量表,我們就事先分出個n個表,這個n是多少,根據實際情況而定。
某網站現在的資料量至多是5000萬條,可以設計每張表容納的資料量是500萬條,也就是拆分成10張表,那麼如何判斷某張表的資料是否容量已滿呢?可以在程式段對于要新增資料的表,在插入前先做統計表記錄數量的操作,當<500萬條資料,就直接插入,當已經到達閥值,可以在程式段新建立資料庫表(或者已經事先建立好),再執行插入操作。
利用merge存儲引擎來實作分表
如果要把已有的大資料量表分開比較痛苦,最痛苦的事就是改代碼,因為程式裡面的sql語句已經寫好了。用merge存儲引擎來實作分表, 這種方法比較适合.
舉例子:
資料庫架構
1、簡單的mysql主從複制:
mysql的主從複制解決了資料庫的讀寫分離,并很好的提升了讀的性能,其圖如下:
其主從複制的過程如下圖所示:
但是,主從複制也帶來其他一系列性能瓶頸問題:
寫入無法擴充
寫入無法緩存
複制延時
鎖表率上升
表變大,緩存率下降
那問題産生總得解決的,這就産生下面的優化方案,一起來看看。
2、mysql垂直分區
如果把業務切割得足夠獨立,那把不同業務的資料放到不同的資料庫伺服器将是一個不錯的方案,而且萬一其中一個業務崩潰了也不會影響其他業務的正常進行,并且也起到了負載分流的作用,大大提升了資料庫的吞吐能力。經過垂直分區後的資料庫架構圖如下:
然而,盡管業務之間已經足夠獨立了,但是有些業務之間或多或少總會有點聯系,如使用者,基本上都會和每個業務相關聯,況且這種分區方式,也不能解決單張表資料量暴漲的問題,是以為何不試試水準分割呢?
3、mysql水準分片(sharding)
這是一個非常好的思路,将使用者按一定規則(按id哈希)分組,并把該組使用者的資料存儲到一個資料庫分片中,即一個sharding,這樣随着使用者數量的增加,隻要簡單地配置一台伺服器即可,原理圖如下:
如何來确定某個使用者所在的shard呢,可以建一張使用者和shard對應的資料表,每次請求先從這張表找使用者的shard id,再從對應shard中查詢相關資料,如下圖所示:
拆分政策選擇
其實拆分很靈活,有的是
垂直切分,将一個庫拆成兩個或多個,将有相關聯的表放在一個庫裡。有的是
水準切分将資料量大的表按照一定邏輯進行拆分。
個人感覺垂直切分的相對來說緩解了io的瓶頸,而水準切分,目的是減輕了單個表或某些表讀寫的壓力。
我們項目根據個人需求,采用的水準切分,沒有去分庫。之後要看看需要采用何種的切分了。
了解到的有: 分表、分區、merge引擎分表。
merge引擎分表
簡介
先介紹merge表,此方法隻适用于myisam。我資料庫的表都是采用innodb引擎的,是以首先就被pass了,但是還是在這裡簡單介紹下吧。
mysql 5.1 手冊裡的說的
改變到merge引擎表,意味着成為一個被分區的表,這樣将單一的表各分區存儲在分離的檔案中。分區可以使一些操作效率更顯著,并且不受myisam存儲引擎的限制。(蹩腳的英語,各位看官多擔待吧。)
以上應該是使用merge表的主要原因吧。
建立使用
能夠建立merge表的要求,首先是一組資料結構完全相同的表,并且存儲引擎為myisam。
讓我們先建立一個
之後查詢
你建立了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存儲引擎表,專門用來存儲主鍵,資料隻有一行,用的話通過
來取。并且設定了兩個庫,相同的方法,隻是每次增長的步長不同,防止一個宕掉,還可以穩定運作。
2.動态選擇表名
表分好之後,問題又來了,資料庫層我們的項目使用的是mybatis架構。sql語句都寫在了xml檔案中,現在我需要動态的設定表名。
其實設定mybatis本身,就可以解決這個問題
隻要把屬性statementtype設定為statement,表名就可以以參數形式傳入。傳入參數時要以美元符${columnname}這樣傳入參數,至于statement,preparedstatement 的差別我想大家應該都能知道的。
另一種解決方式,是使用《shardbatis插件》,它是開源的,可以實作資料水準切分功能,有興趣的朋友可以了解下。
分區表
從mysql5.1之後,提供了一種partition引擎的表,看這句
在我的了解,如果把一張表分區之後,不同分區放在不同磁盤位置上,對整體的讀取是否更有益?
分區表優缺點
這裡主要是看的mysql手冊,我也就起到了個翻譯的作用。
在選擇mysql 分區方案時,還有一個需要考慮的,在mysql的bug中有一個關于mysql分區表查詢緩存的bug: 《partitioning + query cache》,因為這個問題,mysql已經将分區表的查詢緩存disable了,無論你是否開啟查詢緩存,都不會啟用查詢緩存。如果你在意這點,請慎重選擇方案。
以上是關于,mysql三個拆分方案的總結,資料方面都是自己查找的是以不免有些會不準确,如有發現請務必告知,希望與各位共成長~~~。