mysql 分区表优化_Sql优化之Mysql表分区
一??分區(qū)表適用于以下場(chǎng)景
1:表非常大以至于無(wú)法全部放在內(nèi)存中,或者只在標(biāo)的最后部分有熱點(diǎn)數(shù)據(jù),其他均是歷史數(shù)據(jù)
2:分區(qū)表的數(shù)據(jù)更容易維護(hù)。例如想批量刪除大量數(shù)據(jù)可以使用清除整個(gè)分區(qū)的方式。另外還可以對(duì)一個(gè)獨(dú)立分區(qū)進(jìn)行優(yōu)化、檢查、修復(fù)等操作。
3:分區(qū)表的數(shù)據(jù)可以分布在不同的物理設(shè)備上,從而高效的利用多個(gè)硬件設(shè)備
4:可以使用分區(qū)表來(lái)避免某些特殊的瓶頸。例如InnoDB的單個(gè)索引的互斥訪問(wèn)、ext3文件系統(tǒng)的inode鎖競(jìng)爭(zhēng)等。
5:如果需要,還可以備份和恢復(fù)獨(dú)立的分區(qū),這在非常大的數(shù)據(jù)集的場(chǎng)景下效果非常好。
二? 分區(qū)原理以及限制
mysql數(shù)據(jù)庫(kù)中的數(shù)據(jù)是以文件的形勢(shì)存在磁盤(pán)上的,默認(rèn)放在/mysql/data下面(可以通過(guò)my.cnf中的datadir來(lái)查看),一張表主要對(duì)應(yīng)著三個(gè)文件,一個(gè)是frm存放表結(jié)構(gòu)的,一個(gè)是myd存放表數(shù)據(jù)的,
一個(gè)是myi存表索引的。如果一張表的數(shù)據(jù)量太大的話,那么myd,myi就會(huì)變的很大,查找數(shù)據(jù)就會(huì)變的很慢,這個(gè)時(shí)候我們可以利用mysql的分區(qū)功能,
在物理上將這一張表對(duì)應(yīng)的三個(gè)文件,分割成許多個(gè)小塊,這樣呢,我們查找一條數(shù)據(jù)時(shí),就不用全部查找了,只要知道這條數(shù)據(jù)在哪一塊,然后在那一塊找就行了。
如果表的數(shù)據(jù)太大,可能一個(gè)磁盤(pán)放不下,這個(gè)時(shí)候,我們可以把數(shù)據(jù)分配到不同的磁盤(pán)里面去。
分區(qū)表的限制
分區(qū)表的原理
SELECT 查詢(xún):當(dāng)查詢(xún)一個(gè)分區(qū)表的時(shí)候,分區(qū)層先打開(kāi)并鎖住所有底層表,優(yōu)化器先判斷是否可以過(guò)濾部分分區(qū),然后再調(diào)用對(duì)應(yīng)的存儲(chǔ)引擎接口訪問(wèn)各個(gè)分區(qū)的數(shù)據(jù);
INSERT操作:當(dāng)寫(xiě)入一條數(shù)據(jù)時(shí),分區(qū)層先打開(kāi)并鎖住所有的底層表,然后確定那個(gè)分區(qū)接收這條數(shù)據(jù),然后再將記錄寫(xiě)入對(duì)應(yīng)的底層表;
DELETE操作:當(dāng)刪除一條記錄時(shí),分區(qū)層現(xiàn)代開(kāi)并鎖住所有的底層表,然后確定數(shù)據(jù)對(duì)應(yīng)的分區(qū),最后對(duì)相應(yīng)底層進(jìn)行刪除操作;
UPDATE操作:當(dāng)更新一條記錄時(shí),分區(qū)層先打開(kāi)并鎖住所有的底層表,MYSQL先確定需要更新的記錄在哪個(gè)分區(qū),然后取出數(shù)據(jù)并更新,在判斷更新后的數(shù)據(jù)應(yīng)該放在哪個(gè)分區(qū),最后對(duì)底層表進(jìn)行寫(xiě)入操作,并對(duì)原數(shù)據(jù)所在的底層表進(jìn)行刪除操作。
當(dāng)然其中有些操作是支持過(guò)濾的。例如當(dāng)刪除一條記錄時(shí),MYSQL 需要先找到這條記錄,如果where條件恰好和分區(qū)表達(dá)式匹配,就可以將所有不包含這條記錄的分區(qū)都過(guò)濾掉。同樣的操作對(duì)于update同樣有效。如果是insert操作,其本身就是只命中一個(gè)分區(qū) ,其他分區(qū)都會(huì)被過(guò)濾掉。MYSQL先確定這條記錄屬于哪個(gè)分區(qū),再將記錄寫(xiě)入對(duì)應(yīng)的底層分區(qū)表,無(wú)須對(duì)任何其他分區(qū)進(jìn)行操作。(雖然每個(gè)操作都會(huì)“”“先打開(kāi)并鎖住所有的底層表”,但是并不是說(shuō)分區(qū)表在處理過(guò)程中是鎖住全表的,如果存儲(chǔ)引擎能夠自己實(shí)現(xiàn)行級(jí)鎖,例如InnoDB,則會(huì)在分區(qū)層釋放對(duì)應(yīng)表鎖)。
分區(qū)表的類(lèi)型:MYSQL支持多種分區(qū)表
最常見(jiàn)的就是根據(jù)范圍進(jìn)行分區(qū),每個(gè)分區(qū)存儲(chǔ)落在某個(gè)范圍的記錄,分區(qū)表達(dá)式可以是列,也可以是列的表達(dá)式。下面的例子是將每一年的銷(xiāo)售額存放在不同的分區(qū)里。
三? ? 創(chuàng)建分區(qū)操作
RANGE分區(qū):
mysql> CREATE TABLE `operation_log` (
-> `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
-> `cid` mediumint(7) unsigned NOT NULL,
-> `accountid` mediumint(8) NOT NULL DEFAULT '0' ,
-> `flag` tinyint(1) unsigned NOT NULL DEFAULT '0',
-> `addtime` int(11) unsigned NOT NULL,
-> `device` tinyint(1) unsigned NOT NULL DEFAULT '1' ,
-> PRIMARY KEY (`id`,`addtime`),
-> KEY `idx_accountid_addtime` (`accountid`,`addtime`),
-> KEY `idx_accountid_flag` (`accountid`,`flag`),
->) ENGINE=InnoDB AUTO_INCREMENT=50951039 DEFAULT CHARSET=utf8 COMMENT='操作記錄'
->/*!50100 PARTITION BY RANGE (addtime)
->(PARTITION `2013-05` VALUES LESS THAN (1370016000) ENGINE = InnoDB,
-> PARTITION `2013-06` VALUES LESS THAN (1372608000) ENGINE = InnoDB,
-> PARTITION `2013-07` VALUES LESS THAN (1375286400) ENGINE = InnoDB,
-> PARTITION `2013-08` VALUES LESS THAN (1377964800) ENGINE = InnoDB,
-> PARTITION `2013-09` VALUES LESS THAN MAXVALUE ENGINE = InnoDB) */;
1 row in set (0.00 sec)
( LESS THAN MAXVALUE考慮到可能的最大值)
list分區(qū)
//這種方式失敗
mysql> CREATE TABLE IF NOT EXISTS `list_part` (
-> `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用戶(hù)ID',
-> `province_id` int(2) NOT NULL DEFAULT 0 COMMENT '省',
-> `name` varchar(50) NOT NULL DEFAULT '' COMMENT '名稱(chēng)',
-> `sex` int(1) NOT NULL DEFAULT '0' COMMENT '0為男,1為女',
-> PRIMARY KEY (`id`)
-> ) ENGINE=INNODB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1
-> PARTITION BY LIST (province_id) (
-> PARTITION p0 VALUES IN (1,2,3,4,5,6,7,8),
-> PARTITION p1 VALUES IN (9,10,11,12,16,21),
-> PARTITION p2 VALUES IN (13,14,15,19),
-> PARTITION p3 VALUES IN (17,18,20,22,23,24)
-> );
ERROR 1503 (HY000): A PRIMARY KEY must include all columns in the table's partitioning function
//這種方式成功
mysql> CREATE TABLE IF NOT EXISTS `list_part` (
-> `id` int(11) NOT NULL COMMENT '用戶(hù)ID',
-> `province_id` int(2) NOT NULL DEFAULT 0 COMMENT '省',
-> `name` varchar(50) NOT NULL DEFAULT '' COMMENT '名稱(chēng)',
-> `sex` int(1) NOT NULL DEFAULT '0' COMMENT '0為男,1為女'
-> ) ENGINE=INNODB DEFAULT CHARSET=utf8
-> PARTITION BY LIST (province_id) (
-> PARTITION p0 VALUES IN (1,2,3,4,5,6,7,8),
-> PARTITION p1 VALUES IN (9,10,11,12,16,21),
-> PARTITION p2 VALUES IN (13,14,15,19),
-> PARTITION p3 VALUES IN (17,18,20,22,23,24)
-> );
Query OK, 0 rows affected (0.33 sec)
上面的這個(gè)創(chuàng)建list分區(qū)時(shí),如果有主銉的話,分區(qū)時(shí)主鍵必須在其中,不然就會(huì)報(bào)錯(cuò)。如果我不用主鍵,分區(qū)就創(chuàng)建成功了,一般情況下,一個(gè)張表肯定會(huì)有一個(gè)主鍵,這算是一個(gè)分區(qū)的局限性
hash分區(qū)
mysql> CREATE TABLE IF NOT EXISTS `hash_part` (
-> `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '評(píng)論ID',
-> `comment` varchar(1000) NOT NULL DEFAULT '' COMMENT '評(píng)論',
-> `ip` varchar(25) NOT NULL DEFAULT '' COMMENT '來(lái)源IP',
-> PRIMARY KEY (`id`)
-> ) ENGINE=INNODB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1
-> PARTITION BY HASH(id)
-> PARTITIONS 3;
Query OK, 0 rows affected (0.06 sec)
key分區(qū)
mysql> CREATE TABLE IF NOT EXISTS `key_part` (
-> `news_id` int(11) NOT NULL COMMENT '新聞ID',
-> `content` varchar(1000) NOT NULL DEFAULT '' COMMENT '新聞內(nèi)容',
-> `u_id` varchar(25) NOT NULL DEFAULT '' COMMENT '來(lái)源IP',
-> `create_time` DATE NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '時(shí)間'
-> ) ENGINE=INNODB DEFAULT CHARSET=utf8
-> PARTITION BY LINEAR HASH(YEAR(create_time))
-> PARTITIONS 3;
Query OK, 0 rows affected (0.07 sec)
增加子分區(qū)操作
子分區(qū)是分區(qū)表中每個(gè)分區(qū)的再次分割,子分區(qū)既可以使用HASH希分區(qū),也可以使用KEY分區(qū)。這 也被稱(chēng)為復(fù)合分區(qū)(composite partitioning)
1. 如果一個(gè)分區(qū)中創(chuàng)建了子分區(qū),其他分區(qū)也要有子分區(qū)
2. 如果創(chuàng)建了了分區(qū),每個(gè)分區(qū)中的子分區(qū)數(shù)必有相同
3. 同一分區(qū)內(nèi)的子分區(qū),名字不相同,不同分區(qū)內(nèi)的子分區(qū)名子可以相同(5.1.50不適用)
1
2
3
4
5
6
7
8
9
10
11
12
13
mysql> CREATE TABLE IF NOT EXISTS `sub_part` (
-> `news_id` int(11) NOT NULL COMMENT '新聞ID',
-> `content` varchar(1000) NOT NULL DEFAULT '' COMMENT '新聞內(nèi)容',
-> `u_id` int(11) NOT NULL DEFAULT 0s COMMENT '來(lái)源IP',
-> `create_time` DATE NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '時(shí)間'
-> ) ENGINE=INNODB DEFAULT CHARSET=utf8
-> PARTITION BY RANGE(YEAR(create_time))
-> SUBPARTITION BY HASH(TO_DAYS(create_time))(
-> PARTITION p0 VALUES LESS THAN (1990)(SUBPARTITION s0,SUBPARTITION s1,SUBPARTITION s2),
-> PARTITION p1 VALUES LESS THAN (2000)(SUBPARTITION s3,SUBPARTITION s4,SUBPARTITION good),
-> PARTITION p2 VALUES LESS THAN MAXVALUE(SUBPARTITION tank0,SUBPARTITION tank1,SUBPARTITION tank3)
-> );
Query OK, 0 rows affected (0.07 sec)
分區(qū)管理
增加分區(qū)操作(針對(duì)設(shè)置MAXVALUE)
range添加分區(qū)
1
2
3
mysql>alter table operation_log add partition(partition `2013-10` values less than (1383235200)); --->適用于沒(méi)有設(shè)置MAXVALUE的分區(qū)添加
ERROR 1481 (HY000):MAXVALUE can only be used in last partition definition
mysql>alter table operation_log REORGANIZE partition `2013-09` into (partition `2013-09` values less than (1380556800),partition `2013-10` values less than (1383235200),partition `2013-11` values less than maxvalue);
list添加分區(qū)
1
2
3
mysql> alter table list_part add partition(partition p4 values in (25,26,28));
Query OK, 0 rows affected (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 0
hash重新分區(qū)
1
2
3
mysql> alter table list_part add partition(partition p4 values in (25,26,28));
Query OK, 0 rows affected (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 0
key重新分區(qū)
1
2
3
mysql> alter table key_part add partition partitions 4;
Query OK, 1 row affected (0.06 sec)//有數(shù)據(jù)也會(huì)被重新分配
Records: 1 Duplicates: 0 Warnings: 0
子分區(qū)添加新分區(qū),雖然我沒(méi)有指定子分區(qū),但是系統(tǒng)會(huì)給子分區(qū)命名的
1
2
3
mysql> alter table sub1_part add partition(partition p3 values less than MAXVALUE);
Query OK, 0 rows affected (0.02 sec)
Records: 0 Duplicates: 0 Warnings: 0
刪除分區(qū)操作
alter table user drop partition2013-05;
分區(qū)表其他操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
重建分區(qū)(官方:與先drop所有記錄然后reinsert是一樣的效果;用于整理表碎片)
alter table operation_log rebuild partition `2014-01`;
重建多個(gè)分區(qū)
alter table operation_log rebuild partition `2014-01`,`2014-02`;
過(guò)程如下:
pro
優(yōu)化分區(qū)(如果刪除了一個(gè)分區(qū)的大量記錄或者對(duì)一個(gè)分區(qū)的varchar blob text數(shù)據(jù)類(lèi)型的字段做了許多更新,此時(shí)可以對(duì)分區(qū)進(jìn)行優(yōu)化以回收未使用的空間和整理分區(qū)數(shù)據(jù)文件)
alter table operation_log optimize partition `2014-01`;
優(yōu)化的操作相當(dāng)于check partition,analyze partition 和repair patition
分析分區(qū)
alter table operation_log analyze partition `2014-01`;
修復(fù)分區(qū)
alter table operation_log repair partition `2014-01`;
檢查分區(qū)
alter table operation_log check partition `2014-01`;
總結(jié)
以上是生活随笔為你收集整理的mysql 分区表优化_Sql优化之Mysql表分区的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: python gevent缺点_pyth
- 下一篇: mysql带c的命令_mysql命令整理