Mysql Table Partition (表分區)



當遇到mysql的數據量大的時候,我們會想到很多解決方案:分庫分表(垂直切分,水平切分),讀寫分離,加redis分佈式緩存,部分大數據遷移到mongodb等等。但是這些都是大的改動,改動量很大,風險也很大,effort也很大。如果mysql只是某一張表比較大,其他表數據量都不大的時候,我們就應該在這一張表上下功夫,不應該在整個數據庫存儲架構上做出大的改變。mysql在5.1之後引入了Table Partition的概念,因此,某張表數據量大的可以進行分區。

什麼是分表?

分表是將一個大表按照一定的規則分解成多張具有獨立存儲空間的實體表,我們可以稱爲子表,每個表都對應三個文件,MYD數據文件,.MYI索引文件,.frm表結構文件。這些子表可以分佈在同一塊磁盤上,也可以在不同的機器上。app讀寫的時候根據事先定義好的規則得到對應的子表名,然後去操作它。從表面意思上看呢,就是把一張表分成N多個小表。
分表是一個表分成多個表,所以如果在同一個庫中分表,zb_payment應該會分成多個不同名字的表zb_payment1,zb_payment2等等。但是如果是分庫的同時分表,也就是分庫分表,這些zb_payment表都會分在不同的數據庫上,也就是數據庫集羣,那麼是相同的名字zb_payment也是沒問題的。

什麼是分區?

分區和分表相似,都是按照規則分解表。不同在於分表將大表分解爲若干個獨立的實體表,而分區是將數據分段劃分在多個位置存放,可以是同一塊磁盤也可以在不同的機器。分區後,表面上還是一張表,但數據散列到多個位置了。app讀寫的時候操作的還是大表名字,db自動去組織分區的數據。從表面意思上看呢, 就是把一張表的數據分成N多個區塊,這些區塊可以在同一個磁盤上,也可以在不同的磁盤上。




mysql分表和分區有什麼區別呢?

1,實現方式上

a),mysql的分表是真正的分表,一張表分成很多表後,每一個小表都是完正的一張表,都對應三個文件,一個.MYD數據文件,.MYI索引文件,.frm表結構文件。

Sql代碼
- [root@BlackGhost test]# ls |grep user
- alluser.MRG
- alluser.frm
- user1.MYD
- user1.MYI
- user1.frm
- user2.MYD
- user2.MYI
- user2.frm

簡單說明一下,上面的分表呢是利用了merge存儲引擎(分表的一種),alluser是總表,下面有二個分表,user1,user2。他們二個都是獨立的表,取數據的時候,我們可以通過總表來取。這裏總表是沒有.MYD,.MYI這二個文件的,也就是說,總表他不是一張表,沒有數據,數據都放在分表裏面。我們來看看.MRG到底是什麼東西


Sql代碼
- [root@BlackGhost test]# cat alluser.MRG |more
- user1
- user2
- #INSERT_METHOD=LAST


從上面我們可以看出,alluser.MRG裏面就存了一些分表的關係,以及插入數據的方式。可以把總表理解成一個外殼,或者是聯接池。

b),分區不一樣,一張大表進行分區後,他還是一張表,不會變成二張表,但是他存放數據的區塊變多了。

Sql代碼
- [root@BlackGhost test]# ls |grep aa
- aa#P#p1.MYD
- aa#P#p1.MYI
- aa#P#p3.MYD
- aa#P#p3.MYI
- aa.frm
- aa.par

從上面我們可以看出,aa這張表,分爲二個區,p1和p3,本來是三個區,被我刪了一個區。我們都知道一張表對應三個文件.MYD,.MYI,.frm。分區呢根據一定的規則把數據文件和索引文件進行了分割,還多出了一個.par文件,打開.par文件後你可以看出他記錄了,這張表的分區信息,根分表中的.MRG有點像。分區後,還是一張,而不是多張表。

2,數據處理上

a),分表後,數據都是存放在分表裏,總表只是一個外殼,存取數據發生在一個一個的分表裏面。看下面的例子:

select * from alluser where id='12'表面上看,是對錶alluser進行操作的,其實不是的。是對alluser裏面的分表進行了操作。

b),分區呢,不存在分表的概念,分區只不過把存放數據的文件分成了許多小塊,分區後的表呢,還是一張表。數據處理還是由自己來完成。

3,提高性能上

a),分表後,單表的併發能力提高了,磁盤I/O性能也提高了。併發能力爲什麼提高了呢,因爲查尋一次所花的時間變短了,如果出現高併發的話,總表可以根據不同的查詢,將併發壓力分到不同的小表裏面。磁盤I/O性能怎麼搞高了呢,本來一個非常大的.MYD文件現在也分攤到各個小表的.MYD中去了。

b),mysql提出了分區的概念,我覺得就想突破磁盤I/O瓶頸,想提高磁盤的讀寫能力,來增加mysql性能。
在這一點上,分區和分表的測重點不同,分表重點是存取數據時,如何提高mysql併發能力上;而分區呢,如何突破磁盤的讀寫能力,從而達到提高mysql性能的目的。

4),實現的難易度上

a),分表的方法有很多,用merge來分表,是最簡單的一種方式。這種方式根分區難易度差不多,並且對程序代碼來說可以做到透明的。如果是用其他分表方式就比分區麻煩了。

b),分區實現是比較簡單的,建立分區表,根建平常的表沒什麼區別,並且對開代碼端來說是透明的。



mysql分表和分區有什麼聯繫呢?

1,都能提高mysql的性高,在高併發狀態下都有一個良好的表面。

2,分表和分區不矛盾,可以相互配合的,對於那些大訪問量,並且表數據比較多的表,我們可以採取分表和分區結合的方式(如果merge這種分表方式,不能和分區配合的話,可以用其他的分表試),訪問量不大,但是表數據很多的表,我們可以採取分區的方式等。




查看當前mysql是否有表分區?
1.分區一般用於非常大的表,採用“分而治之”的策略,將一個很大的對象分成多個小對象進行管理,每個分區都是一個獨立的對象。

分區使用分區鍵將數據根據範圍值,特定列值或HASH值等規則分佈在不同的分區中。查看當前MySQL是否支持分區,如下所示。

mysql> show variables like '%partition%';



表分區類型?

主要分爲以下四種:

RANGE:基於一個連續的區間範圍,將數據分配到不同的分區。

LIST:基於枚舉出的值列表分區

HASH:基於給定的分區數,將數據分配到不同的分區

KEY:類似於HASH分區,但不允許用戶自定義表達式

<1>RANGE分區

RANGE分區利用取值範圍來進行分區,區間必須連續且不重疊,使用values less than進行分區定義,分區鍵必須是INT,或者表達式返回INT。

create table users_par(
  id int not null,
  usrName varchar(50) not null,
  usrEmail varchar(50) not null,
  age int not null,
  regDate date not null
)engine=InnoDB default charset=utf8
partition by range(age)(
  partition p0 values less than(20),
  partition p1 values less than(30),
  partition p2 values less than maxvalue --防止插入大於30歲的用戶,出現錯誤
);

如果是將註冊日期作爲分區鍵,則須要使用日期處理函數轉換爲整型,例如year(regDate),to_days(regDate),to_seconds(regDate),且只支持這三個函數。

或者使用RANGE COLUMNS分區,則不需要轉換日期,如下所示

create table users_par(
  id int not null,
  usrName varchar(50) not null,
  usrEmail varchar(50) not null,
  age int not null,
  regDate date not null
)engine=InnoDB default charset=utf8
partition by range columns(regDate)(
  partition p0 values less than('2005-05-05'),
  partition p1 values less than('2009-09-09'),
  partition p2 values less than('2015-05-05'),
);

RANGE分區特別適用於刪除過期數據或者某範圍數據,只需要alter table tbl_name truncate partition partition_name即可,比delete語句效率要高很多,還有就是經常使用分區鍵的查詢,可以提高查詢性能,因爲只需掃描某些分區就OK,如下所示。

可以通過explain來看這個select query去拿個partition查詢
mysql> explain partitions select * from users_par where age=30


<2>LIST分區

LIST分區是建立離散的值列表來實現特定的值屬於哪個分區,使用partition by list來實現,values in 來定義。

CREATE TABLE `student` (
`sno` int(11) NOT NULL,
`sname` varchar(30) NOT NULL,
`sclass` varchar(10) NOT NULL,
`sage` int(11) NOT NULL,
`sgender` varchar(6) DEFAULT NULL
)ENGINE=InnoDB DEFAULT CHARSET=utf8
PARTITION BY LIST COLUMNS(sgender)(
PARTITION p0 VALUES IN ('男'),
PARTITION p1 VALUES IN ('女')
);

如果試圖插入的值不在分區值列表中,插入語句將會報錯,將要匹配的值必須在分區值列表中找到。

<3>COLUMNS分區

columns分區是對range,list分區的補充,彌補了後兩者只支持整型數分區(或者通過轉換爲整型數),使得支持數據類型增加很多(所有整數類型,日期時間類型,字符類型),還支持多列分區。
columns分區可細分爲range columns分區和list columns分區,多列分區示例:

create table range_columns(
  a int not null,
  b int not null
)
partition by range columns(a,b)(
  partition p0 values less than(0,10),
  partition p1 values less than(10,20),
  partition p2 values less than(10,30),
  partition p3 values less than(maxvalue,maxvalue)
);

在多列分區表上插入數據時,採用元組的比較,即多列排序,先根據field1排序,再根據field2排序,根據排序結果來來分區存儲數據。


mysql> insert into range_columns values (0,9);


<4>HASH分區

HASH主要是爲了讓數據在設定個數的分區中儘可能分佈平均,執行哈希分區時,mysql會對分區鍵執行哈希函數,以確定數據放在哪個分區中。HASH分區分爲常規HASH分區和線性HASH分區,前者使用取模算法,後者使用線性2的冪的運算規則。HASH分區示例如下,

create table hash_par(
  id int not null,
  name varchar(50) not null
)
partition by hash(id) partitions 4; ----如果要指定爲線性hash,可以使用partition by linear hash

插入一個id爲31的數據,如下所示


mysql> insert into hash_par values (31,'zhumuxian');

HASH分區儘可能讓數據平均地分佈在每個分區上,提高了查詢效率,但是增加了分區管理的代價,比如以前有5個分區,現在要加上一個分區,算法有mod(expr,5)變成(expr,6),原來5個分區的數據大部分要重新結算重新分區。雖然使用線性HASH分區會降低分區管理的代價,但是數據卻沒有常規HASH分佈得那麼均勻。

<5>KEY分區

KEY分區類似與HASH分區,但是不能自定義表達式,不過支持分區鍵的類型很多,除卻Text,Blob等文本類型。KEY分區的設置如下:

create table hash_par(
  id int not null,
  name varchar(50) not null
)
partition by key(id) partitions 4;

KEY分區的分區鍵可以不指定,默認會使用表的主鍵作爲分區鍵,如果沒有主鍵,就使用唯一鍵,兩者都沒有的話就必須指定分區鍵了。




分區時NULL值的處理?

一般情況下,MYSQL把NULL當成零值後者最小值來處理。

在RANGE分區中,NULL會被當成最小值處理;LIST分區中,NULL值必須出現在枚舉值列表中,否則報錯;HASH/KEY分區中,NULL值被當成零值處理。


mysql> insert into student values (4010404,'zhumuxian','A1114',20,'男');
Query OK, 1 row affected (0.04 sec)



mysql> insert into student values (4010405,'zhongzhaoxi','A1114',20,NULL);
ERROR 1526 (HY000): Table has no partition for value from column_list




分區管理?

RANGE和LIST分區在刪除,添加,重新定義等分區管理上非常類似,如下所示。

刪除分區(alter table tbl_name drop partition partition_name),分區被刪除後,該分區的數據一起被刪除。


mysql> alter table users_par drop partition p0;
Query OK, 0 rows affected (0.24 sec)
Records: 0 Duplicates: 0 Warnings: 0


添加分區(alter table tbl_name add partition)


mysql> alter table users_par add partition (partition p0 values less than (20));
ERROR 1481 (HY000): MAXVALUE can only be used in last partition definition

--這裏報錯是因爲添加分區必須在原分區的最大端添加,在爲LIST分區添加分區時,新分區的值列表的值不能包含任意一個現有分區中值列表中的值,否則報錯

mysql> alter table student add partition (partition p2 values in ('男'));
ERROR 1495 (HY000): Multiple definition of same constant in list partitioning



重新定義分區(alter table tbl_name reorganize partition partition_name into),可以將一個分區拆開成多個,反之可以合併多個成一個或多個。


mysql> alter table users_par reorganize partition p1 into (partition p0 values less than (20),partition p1 values less t
han(30));
Query OK, 1 row affected (0.05 sec)
Records: 1 Duplicates: 0 Warnings: 0


需要注意的是:RANGE和LIST分區在重新定義時,只能重新定義相鄰的分區,不可以跳過分區,並且重新定義的分區區間必須和原分區區間一致,也不可以改變分區的類型。

HASH和KEY分區的管理

減少分區數量,使用coaleace關鍵字


mysql> alter table hash_par coalesce partition 2;
Query OK, 1 row affected (0.04 sec)
Records: 1 Duplicates: 0 Warnings: 0


增加分區數量


mysql> alter table hash_par add partition partitions 2;
Query OK, 1 row affected (0.04 sec)
Records: 1 Duplicates: 0 Warnings: 0


MySQL分區有利於查詢優化,快速刪除過期數據,提高查詢吞吐量等。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章