日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

mysql 分库分表的方法

發(fā)布時間:2024/9/20 数据库 58 豆豆
生活随笔 收集整理的這篇文章主要介紹了 mysql 分库分表的方法 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

來源:http://hudeyong926.iteye.com/blog/902801

一,先說一下為什么要分表

當一張的數(shù)據(jù)達到幾百萬時,你查詢一次所花的時間會變多,如果有聯(lián)合查詢的話,我想有可能會死在那兒了。分表的目的就在于此,減小數(shù)據(jù)庫的負擔,縮短查詢時間。

根據(jù)個人經(jīng)驗,mysql執(zhí)行一個sql的過程如下:
1,接收到sql;2,把sql放到排隊隊列中 ;3,執(zhí)行sql;4,返回執(zhí)行結(jié)果。在這個執(zhí)行過程中最花時間在什么地方呢?第一,是排隊等待的時間,第二,sql的執(zhí)行時間。其實這二個是一回事,等待的同時,肯定有sql在執(zhí)行。所以我們要縮短sql的執(zhí)行時間。

?

mysql中有一種機制是表鎖定和行鎖定,為什么要出現(xiàn)這種機制,是為了保證數(shù)據(jù)的完整 性,我舉個例子來說吧,如果有二個sql都要修改同一張表的同一條數(shù)據(jù),這個時候怎么辦呢,是不是二個sql都可以同時修改這條數(shù)據(jù)呢?很顯然mysql 對這種情況的處理是,一種是表鎖定(myisam存儲引擎),一個是行鎖定(innodb存儲引擎)。表鎖定表示你們都不能對這張表進行操作,必須等我對 表操作完才行。行鎖定也一樣,別的sql必須等我對這條數(shù)據(jù)操作完了,才能對這條數(shù)據(jù)進行操作。如果數(shù)據(jù)太多,一次執(zhí)行的時間太長,等待的時間就越長,這 也是我們?yōu)槭裁匆直淼脑颉?/p>

?

垂直分割

就是將一個大表分為多個小表.把主碼和一些列放到一個表,然后把主碼和另外的列放到另一個表中。
如果一個表中某些列常用,而另外一些列不常用,則可以采用垂直分割,另外垂直分割可以使得數(shù)據(jù)行變小,一個數(shù)據(jù)頁就能存放更多的數(shù)據(jù),在查詢時就會減少I/O次數(shù)。其缺點是需要管理冗余列,查詢所有數(shù)據(jù)需要join操作。比如物料有很多屬性,不同的部門有不同的屬性需求,比如財務部門有財務的屬性要求,采購部門有采購的屬性要求,按部門要求不同拆分為不同的表,僅將基本的公共屬性放在主表中,根據(jù)不同的部門要求建不同的表及查詢視圖,性能要好一些

二,分表

1,做mysql集群,例如:利用mysql cluster ,mysql proxy,mysql replication,drdb等等

有人會問mysql集群,根分表有什么關(guān)系嗎?雖然它不是實際意義上的分表,但是它啟到 了分表的作用,做集群的意義是什么呢?為一個數(shù)據(jù)庫減輕負擔,說白了就是減少sql排隊隊列中的sql的數(shù)量,舉個例子:有10個sql請求,如果放在一 個數(shù)據(jù)庫服務器的排隊隊列中,他要等很長時間,如果把這10個sql請求,分配到5個數(shù)據(jù)庫服務器的排隊隊列中,一個數(shù)據(jù)庫服務器的隊列中只有2個,這樣 等待時間是不是大大的縮短了呢?這已經(jīng)很明顯了。所以我把它列到了分表的范圍以內(nèi),我做過一些mysql的集群:

linux mysql proxy 的安裝,配置,以及讀寫分離

mysql replication 互為主從的安裝及配置,以及數(shù)據(jù)同步

優(yōu)點:擴展性好,沒有多個分表后的復雜操作(php代碼)

缺點:單個表的數(shù)據(jù)量還是沒有變,一次操作所花的時間還是那么多,硬件開銷大。

2,預先估計會出現(xiàn)大數(shù)據(jù)量并且訪問頻繁的表,將其分為若干個表

使用MD5哈希

做法是對UID進行md5加密,然后取前幾位(我們這里取前兩位),然后就可以將不同的UID哈希到不同的用戶表(user_xx)中了

Java代碼??
  • <?php??
  • function?get_hash_table($table,?$userid)??
  • {??
  • ????$str?=?crc32($userid);??
  • ????if?($str?<?0)?{??
  • ????????$hash?=?"0"?.?substr(abs($str),?0,?1);??
  • ????}?else?{??
  • ????????$hash?=?substr($str,?0,?2);??
  • ????}??
  • ????return?$table?.?"_"?.?$hash;??
  • }??
  • //echo?get_hash_table('message',?'user18991');?//結(jié)果為message_10??
  • //echo?get_hash_table('message',?'user34523');?//結(jié)果為message_13??
  • function?calc_hash_db($u,?$s?=?4)?{??
  • ????$h?=?sprintf("%u",?crc32($u));??
  • ????$h1?=?intval(fmod($h,?$s));??
  • ????return?$h1;??
  • }??
  • ??
  • for?($i?=?1;?$i?<?40;?$i++)?{??
  • ????echo?calc_hash_tbl($i);??
  • ????echo?"<br>";??
  • ????echo?calc_hash_db($i);??
  • ????echo?"<br>";??
  • }??
  • ??
  • function?calc_hash_tbl($u,?$n?=?256,?$m?=?16)?{??
  • ????$h?=?sprintf("%u",?crc32($u));??
  • ????$h1?=?intval($h?/?$n);??
  • ????$h2?=?$h1?%?$n;??
  • ????$h3?=?base_convert($h2,?10,?$m);??
  • ????$h4?=?sprintf("%02s",?$h3);??
  • ??
  • ????return?$h4;??
  • }??
  • #################??
  • function?getTable(?$uid?){??
  • ????$ext?=?substr?(?md5($uid)?,0?,2?);??
  • ????return?"user_".$ext;??
  • }??
  • ###################??
  • private?function?getDbNo($email)??
  • {??
  • ????$m?=?md5($email);??
  • ????$n?=?hexdec(substr($m,?0,?16));??
  • ????$tableNo?=?fmod($n,?1000);??
  • ????$dbNo?=?$tableNo?%?100;??
  • ????return?array($dbNo,?$tableNo);??
  • }??
  • 通過這個技巧,我們可以將不同的UID分散到256中用戶表中,分別是user_00,user_01 ……? ? user_ff。因為UID是數(shù)字且遞增,根據(jù)md5的算法,可以將用戶數(shù)據(jù)幾乎很均勻的分別到不同的user表中。

    但是這里有個問題是,如果我們的系統(tǒng)的用戶越來越多,勢必單張表的數(shù)據(jù)量越來越大,而且根據(jù)這種算法無法擴展表,這又會回到文章開頭出現(xiàn)的問題了。

    使用移位

    Java代碼??
  • /**?
  • ?*?根據(jù)UID分表算法?
  • ?*?
  • ?*?@param?int?$uid??//用戶ID?
  • ?*?@param?int?$bit????//表后綴保留幾位?
  • ?*?@param?int?$seed?//向右移動位數(shù)?
  • ?*/??
  • function?getTable(?$uid?,?$bit?,?$seed?){??
  • ????return?"user_"?.?sprintf(?"%0{$bit}d"?,?($uid?>>?$seed)?);??
  • ????return?"user_"?.?sprintf(?"%04d",?($uid?>>?20)?);??
  • ??
  • }??
  • 這里,我們將uid向右移動20位,這樣我們就可以把大約前100萬的用戶數(shù)據(jù)放在第一個表user_0000,第二個100萬的用戶數(shù)據(jù)放在第二 個表user_0001中,這樣一直下去,如果我們的用戶越來越多,直接添加用戶表就行了。由于我們保留的表后綴是四位,這里我們可以添加1萬張用戶表, 即user_0000,user_0001 …… user_9999。一萬張表,每張表100萬數(shù)據(jù),我們可以存100億條用戶記錄。當然,如果你的用戶數(shù)據(jù)比這還多,也不要緊,你只要改變保留表后綴來 增加可以擴展的表就行了,如如果有1000億條數(shù)據(jù),每個表存100萬,那么你需要10萬張表,我們只要保留表后綴為6位即可。

    上面兩種方法,都要對我們當前系統(tǒng)的用戶數(shù)據(jù)量做出可能最大的預估,并且對數(shù)據(jù)庫單個表的最大承受量做出預估。

    比如第二種方案,如果我們預估我們系統(tǒng)的用戶是100億,單張表的最優(yōu)數(shù)據(jù)量是100萬,那么我們就需要將UID移動20來確保每個表是100萬的數(shù)據(jù),保留用戶表(user_xxxx)四位來擴展1萬張表。

    又如第一種方案,每張表100萬,md5后取前兩位,就只能有256張表了,系統(tǒng)總數(shù)據(jù)庫就是:256*100萬;如果你系統(tǒng)的總數(shù)據(jù)量的比這還多,那你實現(xiàn)肯定要MD5取前三位或者四位甚至更多位了。

    兩種方法都是將數(shù)據(jù)水平切分到不同的表中,相對第一種方法,第二種方法更具擴展性。

    ?

    3,利用merge存儲引擎來實現(xiàn)分表

    我覺得這種方法比較適合,那些沒有事先考慮,而已經(jīng)出現(xiàn)了得,數(shù)據(jù)查詢慢的情況。這個時 候如果要把已有的大數(shù)據(jù)量表分開比較痛苦,最痛苦的事就是改代碼,因為程序里面的sql語句已經(jīng)寫好了,現(xiàn)在一張表要分成幾十張表,甚至上百張表,這樣 sql語句是不是要重寫呢?舉個例子,我很喜歡舉子

    mysql>show engines;的時候你會發(fā)現(xiàn)mrg_myisam其實就是merge。

    Java代碼??
  • CREATE?TABLE?IF?NOT?EXISTS?`user1`?(????
  • ????`id`?int(11)?NOT?NULL?AUTO_INCREMENT,????
  • ????`name`?varchar(50)?DEFAULT?NULL,????
  • ????`sex`?int(1)?NOT?NULL?DEFAULT?'0',????
  • ????PRIMARY?KEY?(`id`)????
  • ??)?ENGINE=MyISAM??DEFAULT?CHARSET=utf8?AUTO_INCREMENT=1?;????
  • Query?OK,?0?rows?affected?(0.05?sec)??
  • ??
  • ?CREATE?TABLE?IF?NOT?EXISTS?`user2`?(????
  • ????`id`?int(11)?NOT?NULL?AUTO_INCREMENT,????
  • ????`name`?varchar(50)?DEFAULT?NULL,????
  • ????`sex`?int(1)?NOT?NULL?DEFAULT?'0',????
  • ????PRIMARY?KEY?(`id`)????
  • ??)?ENGINE=MyISAM??DEFAULT?CHARSET=utf8?AUTO_INCREMENT=1?;????
  • Query?OK,?0?rows?affected?(0.01?sec)??
  • ??
  • mysql>?INSERT?INTO?`user1`?(`name`,?`sex`)?VALUES('張映',?0);??
  • Query?OK,?1?row?affected?(0.00?sec)??
  • ??
  • mysql>?INSERT?INTO?`user2`?(`name`,?`sex`)?VALUES('tank',?1);??
  • Query?OK,?1?row?affected?(0.00?sec)??
  • ??
  • CREATE?TABLE?IF?NOT?EXISTS?`alluser`?(??
  • ????`id`?int(11)?NOT?NULL?AUTO_INCREMENT,??
  • ????`name`?varchar(50)?DEFAULT?NULL,??
  • ????`sex`?int(1)?NOT?NULL?DEFAULT?'0',??
  • ????INDEX(id)??
  • ??)?TYPE=MERGE?UNION=(user1,user2)?INSERT_METHOD=LAST?AUTO_INCREMENT=1?;??
  • Query?OK,?0?rows?affected,?1?warning?(0.00?sec)??
  • ??
  • mysql>?select?id,name,sex?from?alluser;??
  • +----+--------+-----+??
  • |?id?|?name???|?sex?|??
  • +----+--------+-----+??
  • |??1?|?張映?|???0?|??
  • |??1?|?tank???|???1?|??
  • +----+--------+-----+??
  • 2?rows?in?set?(0.00?sec)??
  • ??
  • mysql>?INSERT?INTO?`alluser`?(`name`,?`sex`)?VALUES('tank2',?0);??
  • Query?OK,?1?row?affected?(0.00?sec)??
  • ??
  • mysql>?select?id,name,sex?from?user2??
  • ?->?;??
  • +----+-------+-----+??
  • |?id?|?name??|?sex?|??
  • +----+-------+-----+??
  • |??1?|?tank??|???1?|??
  • |??2?|?tank2?|???0?|??
  • +----+-------+-----+??
  • 2?rows?in?set?(0.00?sec)??
  • 從上面的操作中,我不知道你有沒有發(fā)現(xiàn)點什么?假如我有一張用戶表user,有50W條數(shù)據(jù),現(xiàn)在要拆成二張表user1和user2,每張表25W條數(shù)據(jù),

    INSERT INTO user1(user1.id,user1.name,user1.sex)SELECT (user.id,user.name,user.sex)FROM user where user.id <= 250000

    INSERT INTO user2(user2.id,user2.name,user2.sex)SELECT (user.id,user.name,user.sex)FROM user where user.id > 250000

    這樣我就成功的將一張user表,分成了二個表,這個時候有一個問題,代碼中的sql語 句怎么辦,以前是一張表,現(xiàn)在變成二張表了,代碼改動很大,這樣給程序員帶來了很大的工作量,有沒有好的辦法解決這一點呢?辦法是把以前的user表備份 一下,然后刪除掉,上面的操作中我建立了一個alluser表,只把這個alluser表的表名改成user就行了。但是,不是所有的mysql操作都能 用的

    a,如果你使用 alter table 來把 merge 表變?yōu)槠渌眍愋?#xff0c;到底層表的映射就被丟失了。取而代之的,來自底層 myisam 表的行被復制到已更換的表中,該表隨后被指定新類型。

    b,網(wǎng)上看到一些說replace不起作用,我試了一下可以起作用的。暈一個先

    Java代碼??
  • mysql>?UPDATE?alluser?SET?sex=REPLACE(sex,?0,?1)?where?id=2;????
  • Query?OK,?1?row?affected?(0.00?sec)????
  • ?Rows?matched:?1??Changed:?1??Warnings:?0????
  • ?????
  • ?mysql>?select?*?from?alluser;????
  • ?+----+--------+-----+????
  • ?|?id?|?name???|?sex?|????
  • ?+----+--------+-----+????
  • ?|??1?|?張映?|???0?|????
  • ?|??1?|?tank???|???1?|????
  • ?|??2?|?tank2??|???1?|????
  • ?+----+--------+-----+????
  • ?3?rows?in?set?(0.00?sec)????
  • ?

    c,一個 merge 表不能在整個表上維持 unique 約束。當你執(zhí)行一個 insert,數(shù)據(jù)進入第一個或者最后一個 myisam 表(取決于 insert_method 選項的值)。mysql 確保唯一鍵值在那個 myisam 表里保持唯一,但不是跨集合里所有的表。

    d,當你創(chuàng)建一個 merge 表之時,沒有檢查去確保底層表的存在以及有相同的機構(gòu)。當 merge 表被使用之時,mysql 檢查每個被映射的表的記錄長度是否相等,但這并不十分可靠。如果你從不相似的 myisam 表創(chuàng)建一個 merge 表,你非常有可能撞見奇怪的問題。

    好困睡覺了,c和d在網(wǎng)上看到的,沒有測試,大家試一下吧。

    優(yōu)點:擴展性好,并且程序代碼改動的不是很大

    缺點:這種方法的效果比第二種要差一點

    三,總結(jié)一下

    上面提到的三種方法,我實際做過二種,第一種和第二種。第三種沒有做過,所以說的細一 點。哈哈。做什么事都有一個度,超過個度就過變得很差,不能一味的做數(shù)據(jù)庫服務器集群,硬件是要花錢買的,也不要一味的分表,分出來1000 表,mysql的存儲歸根到底還以文件的形勢存在硬盤上面,一張表對應三個文件,1000個分表就是對應3000個文件,這樣檢索起來也會變的很慢?。我的 建議是

    方法1和方法2結(jié)合的方式來進行分表

    方法1和方法3結(jié)合的方式來進行分表

    我的二個建議適合不同的情況,根據(jù)個人情況而定,我覺得會有很多人選擇方法1和方法3結(jié)合的方式

    ?

    分庫分表產(chǎn)生的問題,及注意事項
    1. 分庫分表維度的問題
    假如用戶購買了商品,需要將交易記錄保存取來,如果按照用戶的緯度分表,則每個用戶的交易記錄都保存在同一表中,所以很快很方便的查找到某用戶的購買情況,但是某商品被購買的情況則很有可能分布在多張表中,查找起來比較麻煩。反之,按照商品維度分表,可以很方便的查找到此商品的購買情況,但要查找到買人的交易記錄比較麻煩。?
    所以常見的解決方式有:
    ???? a.通過掃表的方式解決,此方法基本不可能,效率太低了。
    ???? b.記錄兩份數(shù)據(jù),一份按照用戶緯度分表,一份按照商品維度分表。
    ???? c.通過搜索引擎解決,但如果實時性要求很高,又得關(guān)系到實時搜索。
    2. 聯(lián)合查詢的問題
    聯(lián)合查詢基本不可能,因為關(guān)聯(lián)的表有可能不在同一數(shù)據(jù)庫中。?
    3.?? 避免跨庫事務
    避免在一個事務中修改db0中的表的時候同時修改db1中的表,一個是操作起來更復雜,效率也會有一定影響。
    4.?? 盡量把同一組數(shù)據(jù)放到同一DB服務器上
    例如將賣家a的商品和交易信息都放到db0中,當db1掛了的時候,賣家a相關(guān)的東西可以正常使用。也就是說避免數(shù)據(jù)庫中的數(shù)據(jù)依賴另一數(shù)據(jù)庫中的數(shù)據(jù)。


    總結(jié)

    以上是生活随笔為你收集整理的mysql 分库分表的方法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。