mysql的擴展性之數據拆分

 

前言

前面我們介紹過mysql的複製特性,它在一定程度上可以提高數據的訪問性能,但是當你的數據量很大的時候,我們的mysql服務器就顯得力不從心。這時候,我們可以引入數據拆分的解決方案。我在這裏根據數據拆分的簡易程度分爲分表、垂直分區、水平分區三種,並依次介紹。

分表

分表就是根據數據的邏輯關係,將一個表分爲多個。例如可以把帖子表posts,分爲post0,post1,post2。分表過後它們仍然處於同一個數據庫當中,至於分爲幾個表爲好,可以根據實際情況自己來定。

如果事先能估計到某些數據表數據量會很大,就可以選擇在數據庫設計初期,就做好分表。當然了,也可以在程序運行過程中,根據數據量的大小在手動分區。

那麼表如何來分呢?沒有一定答案,只能根據應用的類型來確定分表方案。比如你的客戶是按不同城市來劃分的,這時你可以按城市來分:custom_0371、custom_0373。你也可以把每個表的數據記錄定爲1萬條,那麼blog_00存儲前1萬條數據,第10001條數據就存儲在blog_01,以此類推。再比如日誌表,你可以按時間來劃分,logs_201001、logs_201002。

這裏我們來舉個具體點的例子。假設有一個帖子表posts,它的數據結構如下:

create table posts (
id int(11) not null auto_increment,
title varchar(50) not null,
createtime int(11) not null default 0,
userid int(11) not null,
primary key (id),
key userid(userid)
);

現在這個裏面的數據量過大,我們需要對它進行拆分,根據目前的數據量增長的趨勢,我們計劃把它拆分爲10個小表,分別是posts_01、posts_02、…、posts_09,這幾個表的數據結構一點也不用改變。這個表裏面有個userid字段,我們向這個表插入數據,讀取數據都需要這個字段,在這裏我們就可以選取userid作爲分表的依據,求userid % 10的值。

比如現在userid爲10的用戶要發表一篇帖子,求userid % 10 的值爲0,那麼這條記錄就插入posts_00這張表中。要讀取這篇帖子的話,以前可能是需要傳遞帖子id就行了,現在分表以後就需要調整了,還需要傳遞userid這個字段,知道userid爲10的話,才能去posts_00表中獲取數據。

使用merge存儲引擎實現分表

使用show engines 查詢到的MRG_MYISAM就是指這個存儲引擎。

比如有兩個myisam類型的表user1、user2,他們的數據結構、索引、字段順序都完全一樣,user1存儲前10000條數據,user2從10001開始存儲。我們可以建立一個merge類型的表alluser,並且數據結構和user1、user2也完全相同,並且指定alluser由user1和userid組成,那麼我們就可以直接訪問alluser這個表了。

垂直分區

如果上面的分表還只是涉及一個服務器的一個數據庫的話,垂直分區一般都會涉及到兩臺甚至多臺服務器了。

一般我們的應用程序都是都會按照模塊來劃分,模塊之間的耦合讀一般很低。基於這種情況,我們可以把blog模塊涉及到的表放到A服務器,把論壇模塊涉及到的表放到B服務器上,把整個系統的核心數據放到C服務器上,這就是垂直分區,也可以稱之爲縱向切分。

顯然,經過縱向切分以後,單臺服務器的負載會下降,進而提高了整個系統的響應能力。但是垂直分區也不是那麼容易的事,一般各個模塊直接雖然耦合度較低,但也是有聯繫的,這勢必導致要修改程序。並且也不能過度的拆分,過度的拆分也會導致系統過於複雜而難以維護。

當然垂直分區也不能解決所有的問題,比如單表數據量過大的問題。我們還可以結合上面提到的分表和下面即將提到的水平分區來解決這個問題。

水平分區

其實水平分區和第一節提到的分表比較相似,都是對一個表進行邏輯上拆分。只不過分表還是在一臺服務器上操作,而我在這裏提到的水平分區是將一個表的數據分佈到多臺服務器上,可以稱之爲分表的升級版吧。

還是那上面的帖子表posts來舉例。這裏把posts分佈到十臺服務器上,分別是0、1、2…。posts表在這十臺服務器上還是叫posts,表結構還是一樣。分區依據還是userid,還是對10取餘。

這時候我們需要計算的不是目標表名了,而是目標服務器。假如還是用useri爲10舉例,那就是,這個用戶發的帖子需要存儲到0這臺服務器上。

數據拆分遇到的問題

數據分區之後,就帶來了如何訪問這些數據的問題。上面我只是簡單的寫了一些,其實真實情況要複雜。

order排序問題,數據水平拆分後就分佈於多個表甚至多個服務器上,這給排序帶來了很大的問題,並且目前也沒有什麼好的解決方案。所以,在項目初期都應該考慮到這樣的問題。

由於不能擴服務器做join查詢,所以以前程序裏面的join操作可能有問題了,解決方案就是拆開join sql,分多次查詢。可以先把主表的數據查出來,然後根據某個key,再把其他數據查詢出來。有人可能說,這樣多麻煩啊,並且性能也很差勁吧?是的,會麻煩一些,其實就算不分表,我們也應該堅決摒棄那種多於兩個表的join語句;說到性能,我們可以用緩存來解決。就我所知,java領域中的hibernate也是這麼幹的。

其中,transaction是最難辦的了,這樣的問題也沒有什麼完美的解決方案,一般都會採取拆分事務的辦法。不過在web項目中,一般都不會引入事務,有些情況下可以用程序去解決,或者乾脆無視。

整合方案

假如我們已經確定了數據拆分的方案,那麼我們如何將它與我們的應用程序整合呢。這的確是一個比較重要的問題,也是比較難辦的問題。

(1) 封裝數據訪問層。

可以自定義一些配置文件,然後在發送query到數據庫之前,讓數據訪問層根據配置文件生成特定的sql語句或者連接到不同的數據庫。

這種方式應該說很是靈活,修改起來也比較容易。但是難度也是很大的,現實情況下數據的拆分會很複雜,並且是幾種方案一起使用,如果訪問層代碼寫的不好,很容易造成難以維護,甚至和其他模塊的代碼耦合到一塊。

一個好的數據訪問層應該是透明化的,也就是說寫好配置文件後,我們不在需要關注數據應該存儲到那臺服務器。

(2) 使用開源的中間代理層

有時候感覺自己封裝數據訪問層太麻煩的話,可以選用一個開源的代理層。目前有mysql proxy和amoeba可以供我們使用。

代理層其實就是在客戶端和mysql服務器之間建立一個連接池,客戶端的請求發送到代理層,代理層經過分析後,再把sql發送到相應的mysql服務器。

mysql proxy是mysql官方提供的,它之所以靈活是因爲它只提供了基礎功能,其他功能比如負載均衡、讀寫分離,可以寫lua腳本來實現。

amoeba是國內一個開發者寫的,項目開始於08年,已經有不少中小公司試着在實際環境中使用了。我最近使用了一下,使用上還算簡單,但是它的sql解析功能似乎還不算完善。

 

from http://www.usewo.com/?p=54 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章