mysql的分表分區

爲什麼要分表和分區?
我們的數據庫數據越來越大,隨之而來的是單個表中數據太多。以至於查詢書讀變慢,而且由於表的鎖機制導致應用操作也搜到嚴重影響,出現了數據庫性能瓶頸。
mysql中有一種機制是表鎖定和行鎖定,是爲了保證數據的完整性。表鎖定表示你們都不能對這張表進行操作,必須等我對錶操作完才行。行鎖定也一樣,別的sql必須等我對這條數據操作完了,才能對這條數據進行操作。當出現這種情況時,我們可以考慮分表或分區。
1、分表
什麼是分表?
分表是將一個大表按照一定的規則分解成多張具有獨立存儲空間的實體表,每個表都對應三個文件,MYD數據文件,.MYI索引文件,.frm表結構文件。這些表可以分佈在同一塊磁盤上,也可以在不同的機器上。app讀寫的時候根據事先定義好的規則得到對應的表名,然後去操作它。
將單個數據庫表進行拆分,拆分成多個數據表,然後用戶訪問的時候,根據一定的算法(如用hash的方式,也可以用求餘(取模)的方式),讓用戶訪問不同的表,這樣數據分散到多個數據表中,減少了單個數據表的訪問壓力。提升了數據庫訪問性能。分表的目的就在於此,減小數據庫的負擔,縮短查詢時間。

Mysql分表分爲垂直切分和水平切分
垂直切分是指數據表列的拆分,把一張列比較多的表拆分爲多張表
通常我們按以下原則進行垂直拆分:
把不常用的字段單獨放在一張表;
把text,blob(binary large object,二進制大對象)等大字段拆分出來放在附表中;
經常組合查詢的列放在一張表中;
垂直拆分更多時候就應該在數據表設計之初就執行的步驟,然後查詢的時候用jion關鍵起來即可。

水平拆分是指數據錶行的拆分,把一張的表的數據拆成多張表來存放。
水平拆分原則
通常情況下,我們使用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分表,分爲主表和子表,主表類似於一個殼子,邏輯上封裝了子表,實際上數據都是存儲在子表中的。
我們可以通過主表插入和查詢數據,如果清楚分表規律,也可以直接操作子表。
下面我們來實現一個簡單的利用merge存儲引擎來實現分表的演示:
創建一個完整表存儲着所有的成員信息(表名爲member)
mysql> drop database IF EXISTS test;
mysql> use test;
create table member(
id bigint auto_increment primary key,
name varchar(20),
sex tinyint not null default ‘0’
)engine=myisam default charset=utf8 auto_increment=1;
加入點數據:
mysql> insert into member(name,sex) values(‘tom1’,1);
mysql> insert into member(name,sex) select name,sex from member;
第二條語句多執行幾次就有了很多數據
mysql> select * from member;
+—-+——+—–+
| id | name | sex |
+—-+——+—–+
| 1 | tom1 | 1 |
| 2 | tom1 | 1 |
| 3 | tom1 | 1 |
| 4 | tom1 | 1 |
| 5 | tom1 | 1 |
| 6 | tom1 | 1 |
| 7 | tom1 | 1 |
| 8 | tom1 | 1 |
| 9 | tom1 | 1 |
| 10 | tom1 | 1 |
| 11 | tom1 | 1 |
| 12 | tom1 | 1 |
| 13 | tom1 | 1 |
| 14 | tom1 | 1 |
| 15 | tom1 | 1 |
| 16 | tom1 | 1 |
+—-+——+—–+
下面我們進行分表,這裏我們把member分兩個表tb_member1,tb_member2
mysql> use test;
DROP table IF EXISTS tb_member1;
create table tb_member1(
id bigint primary key ,
name varchar(20),
sex tinyint not null default ‘0’
)ENGINE=MyISAM DEFAULT CHARSET=utf8 ;
DROP table IF EXISTS tb_member2;
create table tb_member2(
idbigint primary key,
name varchar(20),
sex tinyint not null default ‘0’
)ENGINE=MyISAM DEFAULT CHARSET=utf8;
//創建tb_member2也可以用下面的語句 create table tb_member2 like tb_member1;
創建主表tb_member
DROP table IF EXISTS tb_member;
create table tb_member(
id bigint primary key ,
name varchar(20),
sex tinyint not null default ‘0’
) ENGINE=MERGE UNION=(tb_member1,tb_member2) INSERT_METHOD=LAST CHARSET=utf8 ;
注:INSERT_METHOD,此參數INSERT_METHOD = NO 表示該表不能做任何寫入操作只作爲查詢使用,INSERT_METHOD = LAST表示插入到最後的一張表裏面。INSERT_METHOD = first表示插入到第一張表裏面。
查看一下tb_member表的結構:
mysql>desc tb_member;
mysql>desc tb_member;
+——-+————-+——+—–+———+—————————————–+
| Field | Type | Null | Key | Default | Extra |
+——-+————-+——+—–+———+—————————————–+
| id | bigint(20) | NO | PRI | NULL | auto_increment |
| name | varchar(20) | YES | | NULL | |
| sex | tinyint(4) | NO | | 0 | |
+——-+————-+——+—–+———+——————————————+
3 rows in set (0.00 sec)
注:查看子表與主表的字段定義要一致
接下來,我們把數據分到兩個分表中去:
mysql> insert into tb_member1(id,name,sex) select id,name,sex from member where id%2=0;
mysql> insert into tb_member2(id,name,sex) select id,name,sex from member where id%2=1;
查看兩個子表的數據:
mysql> select * from tb_member1;
+—-+——+—–+
| id | name | sex |
+—-+——+—–+
| 16 | tom1 | 1 |
| 14 | tom1 | 1 |
| 12 | tom1 | 1 |
| 10 | tom1 | 1 |
| 8 | tom1 | 1 |
| 6 | tom1 | 1 |
| 4 | tom1 | 1 |
| 2 | tom1 | 1 |
+—-+——+—–+
8 rows in set (0.00 sec)

mysql> select * from tb_member2;
+—-+——+—–+
| id | name | sex |
+—-+——+—–+
| 3 | tom1 | 1 |
| 1 | tom1 | 1 |
| 5 | tom1 | 1 |
| 7 | tom1 | 1 |
| 9 | tom1 | 1 |
| 11 | tom1 | 1 |
| 13 | tom1 | 1 |
| 15 | tom1 | 1 |
+—-+——+—–+
8 rows in set (0.00 sec)
查看一下主表的數據:
mysql> select * from tb_member;
+—-+——+—–+
| id | name | sex |
+—-+——+—–+
| 16 | tom1 | 1 |
| 14 | tom1 | 1 |
| 12 | tom1 | 1 |
| 10 | tom1 | 1 |
| 8 | tom1 | 1 |
| 6 | tom1 | 1 |
| 4 | tom1 | 1 |
| 2 | tom1 | 1 |
| 15 | tom1 | 1 |
| 13 | tom1 | 1 |
| 11 | tom1 | 1 |
| 9 | tom1 | 1 |
| 7 | tom1 | 1 |
| 5 | tom1 | 1 |
| 3 | tom1 | 1 |
| 1 | tom1 | 1 |
+—-+——+—–+
16 rows in set (0.00 sec)

mysql> select * from tb_member where id=3;
+—-+——+—–+
| id | name | sex |
+—-+——+—–+
| 3 | tom1 | 1 |
+—-+——+—–+
1 row in set (0.00 sec)
注意:總表只是一個外殼,存取數據發生在一個一個的子表裏面。
注意:每個子表都有自已獨立的相關表文件,而主表只是一個殼,並沒有完整的相關表文件
[root@localhost ~]# ls -l /usr/local/mysql/data/test/tb_member*
-rw-r—–. 1 mysqlmysql 8614 Sep 15 21:49 /usr/local/mysql/data/test/tb_member1.frm
-rw-r—–. 1 mysqlmysql 320 Sep 16 00:02 /usr/local/mysql/data/test/tb_member1.MYD
-rw-r—–. 1 mysqlmysql 2048 Sep 16 00:43 /usr/local/mysql/data/test/tb_member1.MYI
-rw-r—–. 1 mysqlmysql 8614 Sep 15 21:50 /usr/local/mysql/data/test/tb_member2.frm
-rw-r—–. 1 mysqlmysql 180 Sep 16 00:02 /usr/local/mysql/data/test/tb_member2.MYD
-rw-r—–. 1 mysqlmysql 2048 Sep 16 00:43 /usr/local/mysql/data/test/tb_member2.MYI
-rw-r—–. 1 mysqlmysql 8614 Sep 16 21:12 /usr/local/mysql/data/test/tb_member3.frm
-rw-r—–. 1 mysqlmysql 0 Sep 16 21:12 /usr/local/mysql/data/test/tb_member3.MYD
-rw-r—–. 1 mysqlmysql 1024 Sep 16 21:12 /usr/local/mysql/data/test/tb_member3.MYI
-rw-r—–. 1 mysqlmysql 8614 Sep 16 21:14 /usr/local/mysql/data/test/tb_member.frm
-rw-r—–. 1 mysqlmysql 53 Sep 16 21:14 /usr/local/mysql/data/test/tb_member.MRG

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及以採用後,則採用如下方式進行查看
mysql> show plugins;
在顯示結果中,可以看到partition是ACTIVE的,表示支持分區

下面我們先演示一個按照範圍(range)方式的表分區
創建range分區表
mysql> use test2;
mysql> create table if not exist user (
->id int not null auto_increment,
->name varchar(30) not null default ”,
->sex int(1) not null default ‘0’,
->primary key(id)
-> )default charset=utf8 auto_increment=1
->partition by range(id) (
->partition p0 values less than (3),
->partition p1 values less than (6),
->partition p2 values less than (9),
->partition p3 values less than (12),
->partition p4 values less than maxvalue
-> );
插入些數據
mysql> insert into test2.user(name,sex)values (‘tom1’,’0’);
mysql> insert into test2.user(name,sex)values (‘tom2’,’1’);
mysql> insert into test2.user(name,sex)values (‘tom3’,’1’);
mysql> insert into test2.user(name,sex)values (‘tom4’,’0’);
mysql> insert into test2.user(name,sex)values (‘tom5’,’0’);
mysql> insert into test2.user(name,sex)values (‘tom6’,’1’);
mysql> insert into test2.user(name,sex)values (‘tom7’,’1’);
mysql> insert into test2.user(name,sex)values (‘tom8’,’1’);
mysql> insert into test2.user(name,sex)values (‘tom9’,’1’);
mysql> insert into test2.user(name,sex)values (‘tom10’,’1’);
mysql> insert into test2.user(name,sex)values (‘tom11’,’1’);
mysql> insert into test2.user(name,sex)values (‘tom12’,’1’);
mysql> insert into test2.user(name,sex)values (‘tom13’,’1’);
mysql> insert into test2.user(name,sex)values (‘tom14’,’1’);
到存放數據庫表文件的地方看一下
[root@localhost ~]# ls -l /usr/local/mysql/data/test2/user*
-rw-r—–. 1 mysqlmysql 8614 Sep 16 21:46 /usr/local/mysql/data/test2/user.frm
-rw-r—–. 1 mysqlmysql 98304 Sep 16 21:48 /usr/local/mysql/data/test2/user#P#p0.ibd
-rw-r—–. 1 mysqlmysql 98304 Sep 16 21:48 /usr/local/mysql/data/test2/user#P#p1.ibd
-rw-r—–. 1 mysqlmysql 98304 Sep 16 21:49 /usr/local/mysql/data/test2/user#P#p2.ibd
-rw-r—–. 1 mysqlmysql 98304 Sep 16 21:49 /usr/local/mysql/data/test2/user#P#p3.ibd
-rw-r—–. 1 mysqlmysql 98304 Sep 16 21:49 /usr/local/mysql/data/test2/user#P#p4.ibd
mysql> select count(id) as count from user;
+——-+
| count |
+——-+
| 14 |
+——-+
1 row in set (0.00 sec)
從information_schema系統庫中的partitions表中查看分區信息
mysql> select * from information_schema.partitions where table_schema=’test2’ and table_name=’user’\G;
從某個分區中查詢數據
mysql> select * from test2.user partition(p0);
新增分區
mysql> alter table test2.user add partition (partition partionname values less than (n));
刪除分區
當刪除了一個分區,也同時刪除了該分區中所有的數據。
ALTER TABLE test2.user DROP PARTITION p3;
分區的合併
下面的SQL,將p1 – p3合併爲2個分區p01– p02
mysql> alter table test2.user
-> reorganize partition p1,p2,p3 into
-> (partition p01 values less than (8),
->partition p02 values less than (12)
-> );
[root@localhost ~]# ls -l /usr/local/mysql/data/test2/user*
-rw-r—–. 1 mysqlmysql 8614 Sep 16 22:06 /usr/local/mysql/data/test2/user.frm
-rw-r—–. 1 mysqlmysql 98304 Sep 16 22:06 /usr/local/mysql/data/test2/user#P#p01.ibd
-rw-r—–. 1 mysqlmysql 98304 Sep 16 22:06 /usr/local/mysql/data/test2/user#P#p02.ibd
-rw-r—–. 1 mysqlmysql 98304 Sep 16 21:48 /usr/local/mysql/data/test2/user#P#p0.ibd
-rw-r—–. 1 mysqlmysql 98304 Sep 16 21:49 /usr/local/mysql/data/test2/user#P#p4.ibd
mysql> select * from test2.user partition(p01);
+—-+——+—–+
| id | name | sex |
+—-+——+—–+
| 3 | tom3 | 1 |
| 4 | tom4 | 0 |
| 5 | tom5 | 0 |
| 6 | tom6 | 1 |
| 7 | tom7 | 1 |
+—-+——+—–+
5 rows in set (0.00 sec)
未分區表和分區表性能測試
創建一個未分區的表
mysql> create table test2.tab1(c1 int,c2 varchar(30),c3 date);
創建分區表,按日期的年份拆分
mysql> CREATE TABLE test2.tab2 ( c1 int, c2 varchar(30) , c3 date )
PARTITION BY RANGE (year(c3)) (PARTITION p0 VALUES LESS THAN (1995),
PARTITION p1 VALUES LESS THAN (1996) , PARTITION p2 VALUES LESS THAN (1997) ,
PARTITION p3 VALUES LESS THAN (1998) , PARTITION p4 VALUES LESS THAN (1999) ,
PARTITION p5 VALUES LESS THAN (2000) , PARTITION p6 VALUES LESS THAN (2001) ,
PARTITION p7 VALUES LESS THAN (2002) , PARTITION p8 VALUES LESS THAN (2003) ,
PARTITION p9 VALUES LESS THAN (2004) , PARTITION p10 VALUES LESS THAN (2010),
PARTITION p11 VALUES LESS THAN MAXVALUE );
注意:最後一行,考慮到可能的最大值
通過存儲過程插入100萬條測試數據
創建存儲過程:
mysql> delimiter

//mysql>CREATEPROCEDUREloadparttab()begindeclarevintdefault0;whilev<2000000doinsertintotest2.tab1values(v,testingpartitions,adddate(19950101,(rand(v)36520)mod3652));setv=v+1;endwhile;end

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

執行存儲過程load_part_tab向test2.tab1表插入數據
mysql> delimiter ;
mysql> call load_part_tab();
向test2.tab2表中插入數據
mysql> insert into test2.tab2 select * from test2.tab1;
測試SQL性能
mysql> select count(*) from test2.tab1 where c3 > ‘1995-01-01’ and c3 < ‘1995-12-31’;
+———-+
| count(*) |
+———-+
| 219642 |
+———-+
1 row in set (0.84 sec)
mysql>select count(*) from test2.tab2 where c3 > ‘1995-01-01’ and c3 < ‘1995-12-31’;
+———-+
| count(*) |
+———-+
| 219642 |
+———-+
1 row in set (0.09 sec)
結果表明分區表比未分區表的執行時間少很多。
通過explain語句來分析執行情況
mysql> flush tables;
mysql> explain select count(*) from test2.tab1 where c3 > ‘1995-01-01’ and c3 < ‘1995-12-31’\G;
***************** 1. row *****************
id: 1
select_type: SIMPLE
table: tab1
partitions: NULL
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 2001552
filtered: 11.11
Extra: Using where
1 row in set, 1 warning (0.00 sec)
mysql> explain select count(*) from test2.tab2 where c3 > ‘1995-01-01’ and c3 < ‘1995-12-31’\G;
***************** 1. row *****************
id: 1
select_type: SIMPLE
table: tab2
partitions: p1
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 220206
filtered: 11.11
Extra: Using where
1 row in set, 1 warning (0.00 sec)
explain語句顯示了SQL查詢要處理的記錄數目可以看出分區表比未分區表的明顯掃描的記錄要少很多。
創建索引後情況測試
mysql> create index idx_of_c3 on test2.tab1(c3);
Query OK, 0 rows affected (5.07 sec)
Records: 0 Duplicates: 0 Warnings: 0

mysql> create index idx_of_c3 on test2.tab2(c3);
Query OK, 0 rows affected (4.87 sec)
Records: 0 Duplicates: 0 Warnings: 0

mysql> flush tables;
mysql> select count(*) from test2.tab1 where c3 > ‘1996-01-01’ and c3 < ‘1996-12-31’;
+———-+
| count(*) |
+———-+
| 220264 |
+———-+
1 row in set (0.12 sec)
重啓mysqld服務
mysql> select count(*) from test2.tab2 where c3 > ‘1996-01-01’ and c3 < ‘1996-12-31’;
+———-+
| count(*) |
+———-+
| 220264 |
+———-+
1 row in set (0.11 sec)
創建索引後分區表比未分區表相差不大(數據量越大差別會明顯些)

mysql分區的類型
1.RANGE分區
基於屬於一個給定連續區間的列值,把多行分配給分區。這些區間要連續且不能相互重疊,使用VALUES LESS THAN操作符來進行定義。以下是實例。
CREATE TABLE employees (
id INT NOT NULL,
fname VARCHAR(30),
lname VARCHAR(30),
hired DATE NOT NULL DEFAULT ‘1970-01-01’,
separated DATE NOT NULL DEFAULT ‘9999-12-31’,
job_code INT NOT NULL,
store_id INT NOT NULL
)
partition BY RANGE (store_id) (
partition p0 VALUES LESS THAN (6),
partition p1 VALUES LESS THAN (11),
partition p2 VALUES LESS THAN (16),
partition p3 VALUES LESS THAN (21)
);
按照這種分區方案,在商店1到5工作的僱員相對應的所有行被保存在分區P0中,商店6到10的僱員保存在P1中,依次類推。注意,每個分區都是按順序進行定義,從最低到最高。
對於包含數據(72, ‘Michael’, ‘Widenius’, ‘1998-06-25’, NULL, 13)的一個新行,可以很容易地確定它將插入到p2分區中,但是如果增加了一個編號爲第21的商店,將會發生什麼呢?在這種方案下,由於沒有規則把store_id大於20的商店包含在內,服務器將不知道把該行保存在何處,將會導致錯誤。要避免這種錯誤,可以創建maxvalue分區,所有不在指定範圍內的記錄都會被存儲到maxvalue所在的分區中。
mysql> alter table test2.user add partition (partition p4 values less than maxvalue);
2.LIST分區
類似於按RANGE分區,區別在於LIST分區是基於列值匹配一個離散值集合中的某個值來進行選擇。
LIST分區通過使用“PARTITION BY LIST(expr)”來實現,其中“expr” 是某列值或一個基於某個列值、並返回一個整數值的表達式,然後通過“VALUES IN (value_list)”的方式來定義每個分區,其中“value_list”是一個通過逗號分隔的整數列表。
要按照屬於同一個地區商店的行保存在同一個分區中的方式來分割表,可以使用下面的“CREATE TABLE”語句:
CREATE TABLE employees (
id INT NOT NULL,
fname VARCHAR(30),
lname VARCHAR(30),
hired DATE NOT NULL DEFAULT ‘1970-01-01’,
separated DATE NOT NULL DEFAULT ‘9999-12-31’,
job_code INT,
store_id INT
)
PARTITION BY LIST(store_id)
PARTITION pNorth VALUES IN (3,5,6,9,17),
PARTITION pEast VALUES IN (1,2,10,11,19,20),
PARTITION pWest VALUES IN (4,12,13,14,18),
PARTITION pCentral VALUES IN (7,8,15,16)
);
這使得在表中增加或刪除指定地區的僱員記錄變得容易起來。例如,假定西區的所有音像店都賣給了其他公司。那麼與在西區音像店工作僱員相關的所有記錄(行)可以使用查詢“ALTER TABLE employees DROP PARTITION pWest;”來進行刪除,它與具有同樣作用的DELETE (刪除)查詢“DELETE query DELETE FROM employees WHERE store_id IN (4,12,13,14,18);”比起來,要有效得多。
要點:如果試圖插入列值不在分區值列表中的一行時,那麼“INSERT”查詢將失敗並報錯。例如,假定LIST分區的採用上面的方案,下面的插入將失敗:
INSERT INTO employees VALUES(224, ‘Linus’, ‘Torvalds’, ‘2002-05-01’, ‘2004-10-12’, 42, 21);
這是因爲“store_id”列值21不能在用於定義分區pNorth, pEast, pWest,或pCentral的值列表中找到。要重點注意的是,LIST分區沒有類似如“VALUES LESS THAN MAXVALUE”這樣的包含其他值在內的定義。將要匹配的任何值都必須在值列表中找到。
3.HASH分區
這種模式允許DBA通過對錶的一個或多個列的Hash Key進行計算,最後通過這個Hash碼不同數值對應的數據區域進行分區。
hash分區的目的是將數據均勻的分佈到預先定義的各個分區中,保證各分區的數據量大致一致。在RANGE和LIST分區中,必須明確指定一個給定的列值或列值集合應該保存在哪個分區中;而在HASH分區中,MYSQL自動完成這些工作,用戶所要定一個列值或者表達式,以及指定被分區的表將要被分割成的分區數量。
mysql> create table t_hash( a int(11), b datetime) partition by hash(year(b)) partitions 4;
hash的分區函數頁需要返回一個整數值。partitions子句中的值是一個非負整數,不加的partitions子句的話,默認爲分區數爲1。
mysql> insert into t_hash values(1,’2010-04-01’);
該記錄會被放入分區p2中。因爲插入2010-04-01進入表t_hash,那麼
MOD(YEAR(‘2010-04-01’),4)=2

mysql> select * from information_schema.partitions where table_schema=’test2’ and table_name=’t_hash’\G;
***************** 1. row *****************
TABLE_CATALOG: def
TABLE_SCHEMA: test2
TABLE_NAME: t_hash
PARTITION_NAME: p0
SUBPARTITION_NAME: NULL
PARTITION_ORDINAL_POSITION: 1
SUBPARTITION_ORDINAL_POSITION: NULL
PARTITION_METHOD: HASH
SUBPARTITION_METHOD: NULL
PARTITION_EXPRESSION: year(b)
SUBPARTITION_EXPRESSION: NULL
PARTITION_DESCRIPTION: NULL
TABLE_ROWS: 0
AVG_ROW_LENGTH: 0
DATA_LENGTH: 16384
MAX_DATA_LENGTH: NULL
INDEX_LENGTH: 0
DATA_FREE: 0
CREATE_TIME: 2016-09-16 22:48:59
UPDATE_TIME: 2016-09-17 23:36:22
CHECK_TIME: NULL
CHECKSUM: NULL
PARTITION_COMMENT:
NODEGROUP: default
TABLESPACE_NAME: NULL
***************** 2. row *****************
TABLE_CATALOG: def
TABLE_SCHEMA: test2
TABLE_NAME: t_hash
PARTITION_NAME: p1
SUBPARTITION_NAME: NULL
PARTITION_ORDINAL_POSITION: 2
SUBPARTITION_ORDINAL_POSITION: NULL
PARTITION_METHOD: HASH
SUBPARTITION_METHOD: NULL
PARTITION_EXPRESSION: year(b)
SUBPARTITION_EXPRESSION: NULL
PARTITION_DESCRIPTION: NULL
TABLE_ROWS: 0
AVG_ROW_LENGTH: 0
DATA_LENGTH: 16384
MAX_DATA_LENGTH: NULL
INDEX_LENGTH: 0
DATA_FREE: 0
CREATE_TIME: 2016-09-16 22:48:59
UPDATE_TIME: 2016-09-17 23:36:22
CHECK_TIME: NULL
CHECKSUM: NULL
PARTITION_COMMENT:
NODEGROUP: default
TABLESPACE_NAME: NULL
***************** 3. row *****************
TABLE_CATALOG: def
TABLE_SCHEMA: test2
TABLE_NAME: t_hash
PARTITION_NAME: p2
SUBPARTITION_NAME: NULL
PARTITION_ORDINAL_POSITION: 3
SUBPARTITION_ORDINAL_POSITION: NULL
PARTITION_METHOD: HASH
SUBPARTITION_METHOD: NULL
PARTITION_EXPRESSION: year(b)
SUBPARTITION_EXPRESSION: NULL
PARTITION_DESCRIPTION: NULL
TABLE_ROWS: 1
AVG_ROW_LENGTH: 16384
DATA_LENGTH: 16384
MAX_DATA_LENGTH: NULL
INDEX_LENGTH: 0
DATA_FREE: 0
CREATE_TIME: 2016-09-16 22:48:59
UPDATE_TIME: 2016-09-17 23:23:26
CHECK_TIME: NULL
CHECKSUM: NULL
PARTITION_COMMENT:
NODEGROUP: default
TABLESPACE_NAME: NULL
***************** 4. row *****************
TABLE_CATALOG: def
TABLE_SCHEMA: test2
TABLE_NAME: t_hash
PARTITION_NAME: p3
SUBPARTITION_NAME: NULL
PARTITION_ORDINAL_POSITION: 4
SUBPARTITION_ORDINAL_POSITION: NULL
PARTITION_METHOD: HASH
SUBPARTITION_METHOD: NULL
PARTITION_EXPRESSION: year(b)
SUBPARTITION_EXPRESSION: NULL
PARTITION_DESCRIPTION: NULL
TABLE_ROWS: 0
AVG_ROW_LENGTH: 0
DATA_LENGTH: 16384
MAX_DATA_LENGTH: NULL
INDEX_LENGTH: 0
DATA_FREE: 0
CREATE_TIME: 2016-09-16 22:48:59
UPDATE_TIME: 2016-09-17 23:23:26
CHECK_TIME: NULL
CHECKSUM: NULL
PARTITION_COMMENT:
NODEGROUP: default
TABLESPACE_NAME: NULL
4 rows in set (0.00 sec)
可以看到P2分區有一條記錄。當前這個例子並不能把數據均勻的分佈到各個分區,因爲按照YEAR函數進行的,該值本身是離散的。如果對連續的值進行HASH分區,如自增長的主鍵,則可以較好地將數據平均分佈。
請思考:
mysql> insert into t_hash values(1,’2012-04-01’);
記錄會插入哪個分區?

4.key分區
key分區和hash分區相似,不同在於hash分區是用戶自定義函數進行分區,key分區使用mysql數據庫提供的函數進行分區,NDB cluster使用MD5函數來分區,對於其他存儲引擎mysql使用內部的hash函數。
mysql> create table t_key( a int(11), b datetime) partition by key(b) partitions 4;
上面的RANGE、LIST、HASH、KEY四種分區中,分區的條件必須是整形,如果不是整形需要通過函數將其轉換爲整形。
5.columns分區
mysql-5.5開始支持COLUMNS分區,可視爲RANGE和LIST分區的進化,COLUMNS分區可以直接使用非整形數據進行分區。COLUMNS分區支持以下數據類型:
  所有整形,如INT SMALLINT TINYINT BIGINT。FLOAT和DECIMAL則不支持。
  日期類型,如DATE和DATETIME。其餘日期類型不支持。
  字符串類型,如CHAR、VARCHAR、BINARY和VARBINARY。BLOB和TEXT類型不支持。
  COLUMNS可以使用多個列進行分區。

mysql分表和分區有什麼區別呢
1、實現方式上
a) mysql的分表是真正的分表,一張表分成很多表後,每一個小表都是完整的一張表,都對應三個文件,一個.MYD數據文件,.MYI索引文件,.frm表結構文件。
b) 分區不一樣,一張大表進行分區後,他還是一張表,不會變成二張表,但是他存放數據的區塊變多了
2、數據處理上
a)分表後,數據都是存放在分表裏,總表只是一個外殼,存取數據發生在一個一個的分表裏面。
b)分區呢,不存在分表的概念,分區只不過把存放數據的文件分成了許多小塊,分區後的表呢,還是一張表,數據處理還是由自己來完成。
3、提高性能上
a)分表後,單表的併發能力提高了,磁盤I/O性能也提高了。併發能力爲什麼提高了呢,因爲查尋一次所花的時間變短了,如果出現高併發的話,總表可以根據不同的查詢,將併發壓力分到不同的小表裏面。
b)mysql提出了分區的概念,主要是想突破磁盤I/O瓶頸,想提高磁盤的讀寫能力,來增加mysql性能。
在這一點上,分區和分表的測重點不同,分表重點是存取數據時,如何提高mysql併發能力上;而分區呢,如何突破磁盤的讀寫能力,從而達到提高mysql性能的目的。
4、實現的難易度上
a)分表的方法有很多,用merge來分表,是最簡單的一種方式。這種方式跟分區難易度差不多,並且對程序代碼來說可以做到透明的。如果是用其他分表方式就比分區麻煩了。
b)分區實現是比較簡單的,建立分區表,根建平常的表沒什麼區別,並且對開代碼端來說是透明的。
mysql分表和分區有什麼聯繫?
1.都能提高mysql的性高,在高併發狀態下都有一個良好的表現。
2.分表和分區不矛盾,可以相互配合的,對於那些大訪問量,並且表數據比較多的表,我們可以採取分表和分區結合的方式,訪問量不大,但是表數據很多的表,我們可以採取分區的方式等。
3.分表技術是比較麻煩的,需要手動去創建子表,app服務端讀寫時候需要計算子表名。採用merge好一些,但也要創建子表和配置子表間的union關係。
4.表分區相對於分表,操作方便,不需要創建子表。

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