MySQL 數據庫分表分區

博主QQ:819594300mysql

博客地址:http://zpf666.blog.51cto.com/算法

有什麼疑問的朋友能夠聯繫博主,博主會幫大家解答,謝謝支持!1、分表sql

爲何要分表?數據庫

咱們的數據庫數據愈來愈大,隨之而來的是單個表中數據太多。以致於查詢書讀變慢,並且因爲表的鎖機制致使應用操做也搜到嚴重影響,出現了數據庫性能瓶頸。服務器

什麼是分表?app

分表是將一個達標按照必定的規則分解成多張具備獨立存儲空間的實體表,每一個表都對應三個文件,.MYD數據文件、.MYI索引文件、.frm表結構文件。這些表能夠分佈在同一塊磁盤上,也能夠在不一樣主機的不一樣的磁盤上。less

App讀寫的時候根據事先定義好的規則獲得對應的表名,而後去操做它。ide

將單個數據庫表進行拆分,拆分紅多個數據表,而後用戶訪問的時候,根據必定的算法(如用hash的方式,也能夠用求餘(取模)的方式),讓用戶訪問不一樣的表,這樣數據分散到多個數據表中,減小了單個數據表的訪問壓力。提高了數據庫訪問性能。分表的目的就在於此,減少數據庫的負擔,縮短查詢時間。函數

Mysql分表分爲垂直切分和水平切分性能

垂直切分是指數據表列的拆分,把一張列比較多的表拆分爲多張表。咱們經常把經常使用的幾個列單獨放在一個表,不經常使用的單獨放在另一個表。

水平拆分是指數據錶行的拆分,把一張表的數據拆分紅多張表來存放。一般狀況下,咱們使用hash算法和取模等方式來進行表的拆分。好比:好比一張有400W的用戶表users,爲提升其查詢效率咱們把其分紅4張表users1,users2,users3,users4,經過用ID取模的方法把數據分散到四張表內Id%4= [0,1,2,3],而後查詢,更新,刪除也是經過取模的方法來查詢。

分表的幾種方式?

1)mysql集羣

它並非分表,可是起到了和分表相同的做用。集羣可分擔數據庫的操做次數,將任務分擔到多臺數據庫上。集羣能夠讀寫分離,減小讀寫壓力,從而提高數據庫性能。

2)預先估計會出現大量數據而且訪問頻繁的表,將其分爲若干個表。

根據必定的算法(如用hash的方式,也能夠用求餘(取模)的方式)讓用戶訪問不一樣的表。

例如論壇裏面發表帖子的表,時間長了這張表確定很大,幾十萬,幾百萬都有可能。聊天室裏面信息表,幾十我的在一塊兒一聊一個晚上,時間長了,這張表的數據確定很大。像這樣的狀況不少。因此這種能預估出來的大數據量表,咱們就事先分出個N個表,這個N是多少,根據實際狀況而定。以聊天信息表爲例:咱們事先建100個這樣的表,message_00,message_01,message_02..........message_98,message_99.而後根據用戶的ID來判斷這個用戶的聊天信息放到哪張表裏面,能夠用hash的方式來得到,也能夠用求餘的方式來得到,方法不少。或者能夠設計每張表容納的數據量是N條,那麼如何判斷某張表的數據是否容量已滿呢?能夠在程序段對於要新增數據的表,在插入前先作統計表記錄數量的操做,當<N條數據,就直接插入,當已經到達閥值,能夠在程序段新建立數據庫表(或者已經事先建立好),再執行插入操做)。

3)利用merge存儲引擎來實現分表

若是要把已有的大數據量表分開是比較痛苦的,最痛苦的事就是改代碼,由於程序裏面的sql語句已經寫好了,用merge存儲引擎來實現分表,這種方法比較適合。

注意:merge存儲引擎來實現分表有侷限性,只針對myisam存儲引擎表。

Merge分表,分爲一個主表和若干個子表,主表就是一個殼子,在邏輯上是包含子表的,可是主表不存聽任何的數據,真正的數據存放在子表中。

咱們能夠經過主表插入和查詢數據,若是清楚分表規律,也能夠直接操做子表。

下面咱們來實現一個簡單的利用merge存儲引擎來實現分表的演示

1)建立一個完整表存儲着全部的成員信息

wKioL1jd-nvjXOAuAAcYVnhsOXs559.jpg

2)加入實驗數據(咱們加入32行實驗數據)

wKiom1jd-n6CcBKRAARVocbNv1w139.jpg

wKioL1jd-n6gzvGKAACnUhQqvdc740.jpg

3)下面咱們進行分表,這裏咱們把member分紅兩個子表tb_member1,tb_member2

wKiom1jd-n-zC1DTAAJn_HNeeW4337.jpg

4)建立主表tb_member

wKioL1jd-oDRabWcAAD73aMRoPw953.jpg

注:INSERT_METHOD,此參數

INSERT_METHOD= NO 表示該表不能作任何寫入操做只做爲查詢使用,即只讀狀態;

INSERT_METHOD= LAST表示插入到最後的一張表裏面;

INSERT_METHOD= first表示插入到第一張表裏面。

Insert_method是指之後咱們插入新的數據,則要遵照該配置項後面的參數執行。這也正是merge分表的缺陷,對於新插入的數據有侷限性,而innodb分表則沒有這個侷限性。

5)接下來,咱們把原表中的數據分到兩個子表中去

wKioL1jd-oDiBlL_AAEgk5Lc6NM578.jpg

6)查看兩個子表的數據

wKiom1jd-oHDaSCyAAFTjV_La6M610.jpg

wKiom1jd-oKSThA3AAFX7sNFVDA185.jpg

7)查看主表的數據

wKioL1jd-oLzKkY0AAGuGsK4y6A899.jpg

wKioL1jd-oPT7Y5VAAG_MCjpQUo535.jpg

wKiom1jd-oODfke0AADDQr6EVXo006.jpg

總結:主表只是一個外殼,存取數據發生在一個一個的子表裏面,每一個子表都有自已獨立的相關表文件,而主表只是一個殼,並無完整的相關表文件。咱們看到的主表的數據,都是給用戶的一個假象,這也說明,分表對於客戶來講是透明的。主表和子表的關係就很相似與咱們學過的lvs的調度器和全部的節點服務器。

8)刪除原表,修改主表表名(由於此時原表已經沒有用處了,把主表改爲原表表名,這樣作的好處是不用修改APP了,這是merge的一大優點)

wKiom1jd-oSQaJI6AAEiMWLTks8054.jpg

說明:主表也包含有.frm文件,可是他有個特殊的文件是.mrg文件,這個文件不存聽任何表的真實數據,而是存放的是全部額子表表名和插入方式。

本實驗總結:merge分表的順序是:

①先建立所需的全部子表→②再建立主表→③把原表的數據導入到新建的不一樣子表中→④刪除原表,把主表更名字爲原表表名(這樣作的好處不用修改APP代碼,這是merge的優勢)

其實innodb表也能夠分表,步驟以下:

①先建立所需的全部子表→②把原表的數據導入到新建的不一樣子表中→③修改APP代碼,把客戶的請求對應到各個子表。

兩種不一樣點是,innodb不須要建立主表,可是須要修改APP代碼,比較麻煩,而merge不須要修改app,可是merge須要建主表,而innodb不須要。

2、分區

什麼是分區?

分區和分表類似,都是按照規則分解表。不一樣在於分表將大表分解爲若干個獨立的實體表,而分區是將數據分段劃分在多個位置存放,分區後,表仍是一張表,但數據散列到多個位置了。app讀寫的時候操做的仍是表名字,db自動去組織分區的數據。

分區主要有兩種形式

水平分區(Horizontal Partitioning) 這種形式分區是對錶的行進行分區,全部在表中定義的列在每一個數據集中都能找到,因此表的特性依然得以保持。

舉個簡單例子:一個包含十年發票記錄的表能夠被分區爲十個不一樣的分區,每一個分區包含的是其中一年的記錄。

垂直分區(Vertical Partitioning) 這種分區方式通常來講是經過對錶的垂直劃分來減小目標表的寬度,使某些特定的列被劃分到特定的分區,每一個分區都包含了其中的列所對應的行。

舉個簡單例子:一個包含了大text和BLOB列的表,這些text和BLOB列又不常常被訪問,這時候就要把這些不常用的text和BLOB了劃分到另外一個分區,在保證它們數據相關性的同時還能提升訪問速度。

分區技術支持

①在5.6以前,使用這個參數查看當將配置是否支持分區

mysql>show variables like '%partition%';

+-----------------------+---------------+

|Variable_name          | Value |

+-----------------------+---------------+

|have_partition_engine | YES   |

+-----------------------+------------------+

說明:若是是yes表示你當前的配置支持分區

②在5.6及以採用後,則採用以下方式進行查看

wKioL1jd-oSCbp-4AADl62tKEQs401.jpg

說明:在顯示結果中,能夠看到partition是ACTIVE的,表示支持分區。

下面咱們先演示一個按照範圍(range)方式的表分區

1)建立range分區表

wKioL1jd-oaShE1kAAPU2dwm3bw756.jpg

2)插入些數據(說明一下:處於分解點上額數據會被分配到下一個分區中,好比數據3不會放p0表,而是放p1分區)

wKiom1jd-ofRHClJAAM3f78rbew094.jpg

3)到存放數據庫表文件的地方看一下

wKioL1jd-ojS6jQsAAFccCA_9XE820.jpg

wKioL1jd-ojDnzy6AADACjvOgS4457.jpg

4)從information_schema系統庫中的partitions表中查看分區信息

wKiom1jd-ony8_FkAAF7igAUSM8003.jpg

wKioL1jd-oqgjhVMAAI00lhZ-is409.jpg

wKiom1jd-oqiOR6QAAI3P7MNNkU869.jpg

wKioL1jd-pHyLhuxAAI5sqrO8_E793.jpg

wKiom1jd-pnCQF6XAAI03RYAXmY529.jpg

5)從某個分區中查詢數據

wKiom1jd-puzjB-oAAD9mrXhGAQ134.jpg

6)新增分區

mysql>alter table 庫名.表名 add partition (partition 新增的分區名 values less than (n));

注意:n爲條件是爲具體的數字或者是maxvalues

7)刪除分區

注意:當刪除了一個分區,也同時刪除了該分區中全部的數據。

wKioL1jd-p2SEJcuAADOqcrCVVs914.jpg

8)分區的合併

wKioL1jd-qGCyXnNAAF5VYTIpbw804.jpg

wKiom1jd-qTheVVOAAELxMjq-hI359.jpg

wKioL1jd-qjyyGGQAAE5XrUGAX8525.jpg

未分區表和分區表性能測試

1)建立一個未分區的表

wKiom1jd-qqxiBlHAACiCf3fSPA799.jpg

2)建立分區表,按日期的年份拆分

wKioL1jd-rLB5o83AAKEtaidnaw800.jpg

3)經過存儲過程插入100萬條測試數據

建立存儲過程:

wKiom1jd-rWhD2FAAAFlxV3ZOA8031.jpg

注意:RAND()函數在0和1之間產生一個隨機數,若是一個整數參數N被指定,它被用做種子值。每一個種子產生的隨機數序列是不一樣的。

執行存儲過程load_part_tab向bdqn.tab1表插入數據:

退出去mysql的交互式模式,而後在進入交互式模式,執行下面的命令

wKioL1jd-rfzrGoSAADBAynMuKw075.jpg

4)向test2.tab2表中插入數據

wKiom1jd-rngO-16AADcnwL_XVY235.jpg

5)測試SQL性能

wKiom1jd-uiARaxCAAChU-CwI9c297.jpg

wKiom1jd-urhL0LvAACdiWXlN04596.jpg

總結:結果代表分區表比未分區表的執行時間少不少。

6)經過explain語句來分析執行狀況

wKioL1jd-u7C1sBKAAE6s-b_y_g003.jpg

wKioL1jd-vGzqrleAADpRgusFSs675.jpg

總結:explain語句顯示了SQL查詢要處理的記錄數目能夠看出分區表比未分區表的明顯掃描的記錄要少不少。

7)建立索引後狀況測試

wKiom1jd-vKivqbUAAGLfwvszz8084.jpg

wKioL1jd-3fQLm0AAAGBW9skel0698.jpg

總結:建立索引後分區表比未分區表相差不大(可是數據量越大差異會明顯些)

 

Mysql分區的類型

1、range分區(範圍分區)

做用:把一個連續的列值中的多行分配給分區,列區間連續而且不重疊。

例子:

wKiom1jd-xHyQ5xbAAclTD1Ellw613.jpg

總結:p0到p3分區都是按順序進行定義,從最低到最高,不容許從最高到最低,處於分界點的值自動放入下一個分區,好比store_id是16的自動放入p3分區,可是若是插入store_id是21的數據,就會報錯,由於沒有包含21的分區,爲了不這種錯誤,咱們通常都把range分區的最後一個分區設置爲maxvalue分區,把前面全部分區都不包括的該值分到maxvalue分區,避免報錯。

若是創表的時候沒有建立maxvalue分區,則用以下命令添加便可:

wKioL1jd-xGhfLocAACNlL9CE5E504.jpg

2、list分區(列表分區)

做用:基於列值匹配一個離散值集合中某個值來進行選擇

例子:

wKiom1jd-xPwzvVyAARNc_Y6FVo638.jpg

總結:list分區依據地區把數據很容易的按地區劃分開,好比公司打算把西部的店面所有出售,則就只須要把pwest刪除便可,很是方便。注意的地方是若是視圖插入的store_id列值不屬於任何一個分區,mysql會報錯,插入失敗。list分區沒有range分區類型的maxvalue(最大值)分區,要匹配的列值必須是建立表時幾個分區已經有的值。

使用下面的語句刪除pwest分區,它與具備一樣做用的DELETE (刪除)查詢

「deletefrom employees2 where store_id in (7,8,15,16);」比起來,要有效得多。

wKiom1jd-xOCtj3RAADQemdAPgE479.jpg

用另外一種刪除方法刪除pnorch分區

wKioL1jd-xST7ovkAACl9mQs9tQ323.jpg

3、hash分區

做用:對錶的一個或多個列的hash key進行計算,最後經過這個hash 碼不一樣數值對應的數據區域進行分區。

例子:

wKiom1jd-xXCzlwuAAB44u9QKuE850.jpg

總結:hash分區不須要指定分區的集合,mysql會自動完成分區工做,用戶只須要定一個列值或表達式,以及分區的數量,默認分區的數量是1(即不劃分分區)。++

上圖中的那個例子說明:b列的數值取年份÷分區數量,而後去餘數。好比分區的數量是4,則餘數則只能有0、一、二、3,自動把餘數爲0的放p0分區,餘數爲1的放p1分區等等。其中mysql會自動建立p0、p一、p二、p3分區,名字就叫p0、p一、p二、p3。

查一個表中某個指定分區中的全部數據:

wKioL1jd-xaDJz0eAALhihejegE209.jpg

從information_schema庫中的partitions表裏面查詢bdqn.employess3表的每一個分區的具體狀況:

wKioL1jd-xaBeW63AAFmfiKCsG0365.jpg

wKiom1jd-xfjamTWAAIyaQsaALQ614.jpg

wKiom1jd-xiCQqKkAAI8JZzoMkA239.jpg

wKioL1jd-xiAP48bAAJHf2nRhAQ155.jpg

4、key分區

做用:與hash分區很類似,不一樣的是hash分區是用戶自定義函數進行分區。而key使用mysql數據庫提供的函數進行分區。NDB cluster使用md5函數來分區,對於其餘存儲引擎mysql使用內部的hash函數。

例子:

wKioL1jd-xmTzpzkAAChDGQOEcg640.jpg

總結:range、list、hash、key四種分區中,分區的條件必須是整數,若是不是整數須要經過函數將其轉換成整數。

 

5、columns分區

說明:mysql5.5版本開始支持columns分區,可視爲range和list分區的進化,columns分區能夠直接使用非整數數據進行分區。

Columns分區支持如下數據類型:

  • 全部×××,如INT SMALLINTTINYINT BIGINT。FLOAT和DECIMAL則不支持。

  • 日期類型,如DATE和DATETIME。其他日期類型不支持。

  • 字符串類型,如CHAR、VARCHAR、BINARY和VARBINARY。BLOB和TEXT類型不支持。

  • COLUMNS可使用多個列進行分區。

wKiom1jd-xvgxhugAAV_TiAIk1s293.jpg

wKioL1jd-xzizSR2AAKkxu-1vfE344.jpg

分區時,將不一樣分區放到不一樣存儲位置:

①建表前,提早建立好存儲目錄,並受權給mysql:

wKiom1jd-x3Qlej-AAFIe6XxZr8282.jpg

②建立表

使用mysql默認的存儲引擎inodb時候,只須要指定data directory 就能夠,由於inodb的數據和索引在一個文件中。可是建立表格時指定engine=myisam時,修改分區的存儲位置,須要同時指定datadirectory 和 index directory。

wKioL1jd-x6T4adYAAGgNsRjh54839.jpg

wKiom1jd-x7B5ESIAAEfbBtWELY889.jpg

wKioL1jd-x7QWpXpAAEEAGeRkQ0408.jpg

總結:把一個表的全部數據經過分區劃分到不一樣目錄(目錄在不一樣磁盤上),能夠提升I/O性能,提升磁讀寫能力,讓幾個磁盤都能同時工做,提升mysql的性能。

相關文章
相關標籤/搜索