1、并行數(shù)據(jù)庫(kù)
1.1、并行數(shù)據(jù)庫(kù)的體系結(jié)構(gòu)
并行機(jī)的出現(xiàn),催生了并行數(shù)據(jù)庫(kù)的出現(xiàn),不對(duì),應(yīng)該是關(guān)系運(yùn)算本來就是高度可并行的。對(duì)數(shù)據(jù)庫(kù)系統(tǒng)性能的度量主要有兩種方式:(1)吞吐量(Throughput),在給定的時(shí)間段里所能完成的任務(wù)數(shù)量;(2)響應(yīng)時(shí)間(Response time),單個(gè)任務(wù)從提交到完成所需要的時(shí)間。對(duì)于處理大量小事務(wù)的系統(tǒng),通過并行地處理許多事務(wù)可以提高它的吞吐量。對(duì)于處理大事務(wù)的系統(tǒng),通過并行的執(zhí)行事務(wù)的子任務(wù),可以縮短系統(tǒng)晌應(yīng)時(shí)間。
并行機(jī)有三種基本的體系結(jié)構(gòu),相應(yīng)的,并行數(shù)據(jù)庫(kù)的體系結(jié)構(gòu)也可以大概分為三類:
共享內(nèi)存(share memeory):所有處理器共享一個(gè)公共的存儲(chǔ)器;
共享磁盤(share disk):所有處理器共享公共的磁盤;這種結(jié)構(gòu)有時(shí)又叫做集群(cluster);
無共享(share nothing):所有處理器既不共享內(nèi)存,也不共享磁盤。
如圖所示:
1.1.1、 共享內(nèi)存
該結(jié)構(gòu)包括多個(gè)處理器、一個(gè)全局共享的內(nèi)存(主存儲(chǔ)器)和多個(gè)磁盤存儲(chǔ),各個(gè)處理器通過高速通訊網(wǎng)絡(luò)(Interconnection Network)與共享內(nèi)存連接,并均可直接訪問系統(tǒng)中的一個(gè)、多個(gè)或全部的磁盤存儲(chǔ),在系統(tǒng)中,所有的內(nèi)存和磁盤存儲(chǔ)均由多個(gè)處理器共享。
這種結(jié)構(gòu)的優(yōu)點(diǎn)在于,處理器之間的通信效率極高,訪問內(nèi)存的速度要比消息通信機(jī)制要快很多。這種結(jié)構(gòu)的缺點(diǎn)在于,處理器的規(guī)模不能超過32個(gè)或者64個(gè),因?yàn)榭偩€或互邊網(wǎng)絡(luò)是由所有的處理器共享,它會(huì)變成瓶頸。當(dāng)處理器數(shù)量到達(dá)某一個(gè)點(diǎn)時(shí),再增加處理器已經(jīng)沒有什么好處。
共享內(nèi)存結(jié)構(gòu)通常在每個(gè)處理器上有很大的高速緩存,從而減少對(duì)內(nèi)存的訪問。但是,這些高速緩存必須保持一致,也就是緩存一致性(cache-coherency)的問題。
1.1.2、 共享磁盤
該結(jié)構(gòu)由多個(gè)具有獨(dú)立內(nèi)存(主存儲(chǔ)器)的處理器和多個(gè)磁盤存儲(chǔ)構(gòu)成,各個(gè)處理器相互之間沒有任何直接的信息和數(shù)據(jù)的交換,多個(gè)處理器和磁盤存儲(chǔ)由高速通信網(wǎng)絡(luò)連接,每個(gè)處理器都可以讀寫全部的磁盤存儲(chǔ)。
共享磁盤與共享內(nèi)存結(jié)構(gòu)相比,有以下一些優(yōu)點(diǎn):(1)每個(gè)處理器都有自己的存儲(chǔ)器,存儲(chǔ)總線不再是瓶頸;(2)以一種較經(jīng)濟(jì)的方式提供了容錯(cuò)性(fault tolerence),如果一個(gè)處器發(fā)生故障,其它處理器可以代替工作。
該結(jié)構(gòu)的主要問題不是在于可擴(kuò)展性問題,雖然存儲(chǔ)總線不是瓶頸,但是,與磁盤之間的連接又成了瓶頸。
運(yùn)行Rdb的DEC集群是共享磁盤的體系結(jié)構(gòu)的早期商用化產(chǎn)品之一(DEC后來被Compaq公司收購(gòu),再后來,Oracle又從Compaq手中取得Rdb,發(fā)展成現(xiàn)在的Oracle RAC)。
1.1.3、 無共享
該結(jié)構(gòu)由多個(gè)完全獨(dú)立的處理節(jié)點(diǎn)構(gòu)成,每個(gè)處理節(jié)點(diǎn)具有自己獨(dú)立的處理器、獨(dú)立的內(nèi)存(主存儲(chǔ)器)和獨(dú)立的磁盤存儲(chǔ),多個(gè)處理節(jié)點(diǎn)在處理器級(jí)由高速通信網(wǎng)絡(luò)連接,系統(tǒng)中的各個(gè)處理器使用自己的內(nèi)存獨(dú)立地處理自己的數(shù)據(jù)。
這 種結(jié)構(gòu)中,每一個(gè)處理節(jié)點(diǎn)就是一個(gè)小型的數(shù)據(jù)庫(kù)系統(tǒng),多個(gè)節(jié)點(diǎn)一起構(gòu)成整個(gè)的分布式的并行數(shù)據(jù)庫(kù)系統(tǒng)。由于每個(gè)處理器使用自己的資源處理自己的數(shù)據(jù),不存 在內(nèi)存和磁盤的爭(zhēng)用,提高的整體性能。另外這種結(jié)構(gòu)具有優(yōu)良的可擴(kuò)展性——只需增加額外的處理節(jié)點(diǎn),就可以以接近線性的比例增加系統(tǒng)的處理能力。
這種結(jié)構(gòu)中,由于數(shù)據(jù)是各個(gè)處理器私有的,因此系統(tǒng)中數(shù)據(jù)的分布就需要特殊的處理,以盡量保證系統(tǒng)中各個(gè)節(jié)點(diǎn)的負(fù)載基本平衡,但在目前的數(shù)據(jù)庫(kù)領(lǐng)域,這個(gè)數(shù)據(jù)分布問題已經(jīng)有比較合理的解決方案。
由于數(shù)據(jù)是分布在各個(gè)處理節(jié)點(diǎn)上的,因此,使用這種結(jié)構(gòu)的并行數(shù)據(jù)庫(kù)系統(tǒng),在擴(kuò)展時(shí)不可避免地會(huì)導(dǎo)致數(shù)據(jù)在整個(gè)系統(tǒng)范圍內(nèi)的重分布(Re-Distribution)問題。
Shared-Nothing結(jié)構(gòu)的典型代表是Teradata(并行數(shù)據(jù)庫(kù)的先驅(qū)),值得一提的是,MySQL NDB Cluster也使用了這種結(jié)構(gòu)。
1.2、I/O并行(I/O Parallelism)
I/O并行的最簡(jiǎn)單形式是通過對(duì)關(guān)系劃分,放置到多個(gè)磁盤上來縮減從磁盤讀取關(guān)系的時(shí)間。并行數(shù)據(jù)庫(kù)中數(shù)據(jù)劃分最通用的形式是水平劃分(horizontal portioning),一個(gè)關(guān)系中的元組被劃分到多個(gè)磁盤。
1.2.1、常用劃分技術(shù)
假定將數(shù)據(jù)劃分到n個(gè)磁盤D0,D1,…,Dn中。
(1) 輪轉(zhuǎn)法(round-bin)。對(duì)關(guān)系順序掃描,將第i個(gè)元組存儲(chǔ)到標(biāo)號(hào)為Di%n的磁盤上;該方式保證了元組在多個(gè)磁盤上均勻分布。
(2) 散列劃分(hash partion)。選定一個(gè)值域?yàn)閧0, 1, …,n-1}的散列函數(shù),對(duì)關(guān)系中的元組基于劃分屬性進(jìn)行散列。如果散列函數(shù)返回i,則將其存儲(chǔ)到第i個(gè)磁盤。
(3) 范圍劃分(range partion)。
由于將關(guān)系存儲(chǔ)到多個(gè)磁盤,讀寫時(shí)能同時(shí)進(jìn)行,劃分(partion)能大大提高系統(tǒng)的讀寫性能。數(shù)據(jù)的存取可以分為以下幾類:
(1) 掃描整個(gè)關(guān)系;
(2) 點(diǎn)查詢(point query),如name = “hustcat”;
(3) 范圍查詢(range query),如 20 < age < 30。
不同的劃分技術(shù),對(duì)這些存取類型的效率是不同的:
輪轉(zhuǎn)法適合順序掃描關(guān)系,對(duì)點(diǎn)查詢和范圍查詢的處理較復(fù)雜。
散列劃分特別適合點(diǎn)查詢,速度最快。
范圍劃分對(duì)點(diǎn)查詢、范圍查詢以及順序掃描都支持較好,所以適用性很廣。但是,這種方式存在一個(gè)問題——執(zhí)行偏斜(execution skew),也就是說某些范圍的元組較多,使得大量的I/O出現(xiàn)在某幾個(gè)磁盤。
1.3、查詢間并行(interquery parallism)
查詢間并行指的是不同的查詢或事務(wù)間并行的執(zhí)行。這種形式的并行可以提高事務(wù)的吞吐量,然而,單個(gè)事務(wù)并不能執(zhí)行得更快(即響應(yīng)時(shí)間不能減少)。查詢間的并行主要用于擴(kuò)展事務(wù)處理系統(tǒng),在單位時(shí)間內(nèi)能夠處理更多的事務(wù)。
查詢間并行是數(shù)據(jù)庫(kù)系統(tǒng)最易實(shí)現(xiàn)的一種并行,在共享內(nèi)存的并行系統(tǒng)(如SMP)中尤其這樣。為單處理器設(shè)計(jì)的數(shù)據(jù)庫(kù)系統(tǒng)可以不用修改,或者很少修改就能用到共享內(nèi)存的體系結(jié)構(gòu)。
在共享磁盤和無共享的體系結(jié)構(gòu)中,實(shí)現(xiàn)查詢間并行要更復(fù)雜一些。各個(gè)處理需要協(xié)調(diào)來進(jìn)行封鎖、日志操作等等,這就需要處理器之間的傳遞消息。并行數(shù)據(jù)庫(kù)系統(tǒng)必須保證兩個(gè)處理器不會(huì)同時(shí)更新同一數(shù)據(jù)。而且,處理器訪問數(shù)據(jù)時(shí),系統(tǒng)必須保證處理器緩存的數(shù)據(jù)是最新的數(shù)據(jù),即緩存一致性問題。
1.4、查詢內(nèi)并行(intraquery parallism)
查詢內(nèi)并行是指單個(gè)查詢要在多個(gè)處理器和磁盤上同時(shí)進(jìn)行。為了理解,來考慮一個(gè)對(duì)某關(guān)系進(jìn)行排序的查詢。假設(shè)關(guān)系已經(jīng)基于某個(gè)屬性進(jìn)行了范圍劃分,存儲(chǔ)于多個(gè)磁盤上,并且劃分是基于劃分屬性的。則排序操作可以如下進(jìn)行:對(duì)每個(gè)分區(qū)并行的排序,然后將各個(gè)已經(jīng)有序的分區(qū)合并到一起。
單個(gè)查詢的執(zhí)行可以有兩種并行方式:
(1) 操作內(nèi)并行(Intraoperation parallism):通過并行的執(zhí)行每一個(gè)運(yùn)算,如排序、選擇、連接等,來加快一個(gè)查詢的處理速度。
(2) 操作間并行(Interoperation parallism):通過并行的執(zhí)行一個(gè)查詢中的多個(gè)不同的運(yùn)算,來加速度一個(gè)查詢的處理速度。
注意兩者間的區(qū)別,前者可以認(rèn)為多個(gè)處理器同時(shí)執(zhí)行一個(gè)運(yùn)算,而后者是多個(gè)處理器同時(shí)執(zhí)行不同的運(yùn)算。
這兩種形式之間的并行是互相補(bǔ)充的,并且可以同時(shí)存在于一個(gè)查詢中。通常由于一個(gè)查詢中的運(yùn)算數(shù)目相對(duì)于元組數(shù)目是較小的,所以當(dāng)并行度增加時(shí),第一種方式取得的效果更顯著。
2、MySQL的分區(qū)(partion)
2.1、MySQL分區(qū)概述
在MySQL中,InnoDB存儲(chǔ)引擎長(zhǎng)期支持表空間的概念,并且MySQL服務(wù)器甚至在分區(qū)引入之前,就能配置為存儲(chǔ)不同的數(shù)據(jù)庫(kù)使用不同的物理路徑。分區(qū)(partion)更進(jìn)一步,它允許你通過設(shè)置各種規(guī)則將一個(gè)表的各個(gè)分區(qū)跨文件系統(tǒng)存儲(chǔ)。實(shí)際上,不同位置的不同表分區(qū)是作為一個(gè)單獨(dú)的表來存儲(chǔ)的。用戶所選擇的、實(shí)現(xiàn)數(shù)據(jù)分割的規(guī)則被稱為分區(qū)函數(shù)(partioning function),這在MySQL中它可以是模數(shù),或者是簡(jiǎn)單的匹配一個(gè)連續(xù)的數(shù)值區(qū)間或數(shù)值列表,或者是一個(gè)內(nèi)部HASH函數(shù),或一個(gè)線性HASH函數(shù)。
最常見是的水平分區(qū)(horizontal partitioning),也就是將表的不同的元組分配到不同的物理分區(qū)上。目前,MySQL 5.1還不支持垂直分區(qū)(vertical partitioning),即將表的不同列分配到不同的物理分區(qū)。你可以使用MySQL支持的大多數(shù)存儲(chǔ)引擎來創(chuàng)建表的分區(qū),在MySQL 5.1中,同一個(gè)表的各個(gè)分區(qū)必須使用相同的存儲(chǔ)引擎,比如,你不能對(duì)一個(gè)分區(qū)使用MyISAM,而對(duì)另一個(gè)分區(qū)使用InnoDB。但是,你可以對(duì)同一個(gè)數(shù)據(jù)庫(kù)的不同的表使用不同的存儲(chǔ)引擎。
要為某個(gè)分區(qū)表配置一個(gè)專門的存儲(chǔ)引擎,必須且只能使用[STORAGE] ENGINE 選項(xiàng),這如同為非分區(qū)表配置存儲(chǔ)引擎一樣。但是,必須記住[STORAGE] ENGINE(和其他的表選項(xiàng))必須列在用在CREATE TABLE語(yǔ)句中的其他任何分區(qū)選項(xiàng)之前。下面的例子給出了怎樣創(chuàng)建一個(gè)通過HASH分成6個(gè)分區(qū)、使用InnoDB存儲(chǔ)引擎的表:
CREATE TABLE ti (id INT, amount DECIMAL(7,2), tr_date DATE)
ENGINE=INNODB
PARTITION BY HASH( MONTH(tr_date) )
PARTITIONS 6;
注:分區(qū)必須對(duì)一個(gè)表的所有數(shù)據(jù)和索引;不能只對(duì)數(shù)據(jù)分區(qū)而不對(duì)索引分區(qū),反之亦然,同時(shí)也不能只對(duì)表的一部分進(jìn)行分區(qū)。
分區(qū)對(duì)數(shù)據(jù)庫(kù)管理系統(tǒng)實(shí)現(xiàn)并行處理有著重要的影響,如果對(duì)數(shù)據(jù)進(jìn)行分區(qū),則很容易進(jìn)行并行處理,但是,MySQL還沒有充分利用分區(qū)的這種并行優(yōu)勢(shì),而這也是它改進(jìn)的方向 (這種分治思想深深的影響著并行計(jì)算,而且在并行計(jì)算方面具有天然優(yōu)勢(shì))。MySQL的分區(qū),會(huì)給系統(tǒng)帶來以下一些優(yōu)點(diǎn):
與單個(gè)磁盤或文件系統(tǒng)分區(qū)相比,單個(gè)表可以存儲(chǔ)更多的數(shù)據(jù)。
對(duì)于那些已經(jīng)失去保存意義的數(shù)據(jù),通??梢酝ㄟ^刪除與那些數(shù)據(jù)有關(guān)的分區(qū),很容易地刪除那些數(shù)據(jù)。相反地,在某些情況下,添加新數(shù)據(jù)的過程又可以通過為那些新數(shù)據(jù)專門增加一個(gè)新的分區(qū),來很方便地實(shí)現(xiàn)。
對(duì)于帶Where的條件查詢語(yǔ)句,可以得到更大的優(yōu)化;只需要查詢某些分區(qū),而不用掃描全部分區(qū)。
還有其它一些優(yōu)點(diǎn),不過MySQL 5.1還不支持:
一些聚合函數(shù),比如SUM() 和COUNT(),能夠很容易的并行執(zhí)行;
通過并行I/O,可以大大提高查詢的吞吐量。
注:實(shí)際上,分區(qū)不論是對(duì)I/O并行,還是查詢內(nèi)并行,都有著重要的影響。只不過MySQL在這方面做得還不夠多(不過,正在改進(jìn)),而Oracle對(duì)于查詢內(nèi)并行,做了很多工作。
2.2、分區(qū)類型
MySQL 5.1中可用的分區(qū)類型包括:
RANGE分區(qū)(portioning):根據(jù)列值所屬的范圍區(qū)間,將元組分配到各個(gè)分區(qū)。
LIST分區(qū):類似于按RANGE分區(qū),區(qū)別在于LIST分區(qū)是基于列值匹配一個(gè)離散值集合中的某個(gè)值來進(jìn)行選擇。
HASH分區(qū):根據(jù)用戶定義的函數(shù)的返回值來進(jìn)行選擇的分區(qū),該表達(dá)式使用將要插入到表中的這些行的列值進(jìn)行計(jì)算。這個(gè)函數(shù)可以包含MySQL 中有效的、產(chǎn)生非負(fù)整數(shù)值的任何表達(dá)式。
KEY分區(qū):類似于按HASH分區(qū),區(qū)別在于KEY分區(qū)只支持計(jì)算一列或多列,且MySQL 服務(wù)器提供其自身的哈希函數(shù)。
2.2.1、范圍分區(qū)
范圍分區(qū)是通過計(jì)算表達(dá)式的值所屬的范圍區(qū)間,對(duì)元組進(jìn)行分區(qū)。這些區(qū)間要求連續(xù)且不能相互重疊,使用VALUES LESS THAN操作符來進(jìn)行定義。在下面的幾個(gè)例子中,假定你創(chuàng)建了一個(gè)如下的一個(gè)表,該表保存有20家音像店的職員記錄,這20家音像店的編號(hào)從1到20。
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
);
你可以根據(jù)需要對(duì)該表進(jìn)行各種分區(qū),比如,你可以通過store_id來進(jìn)行分區(qū):
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)
);
很容易確定數(shù)據(jù)(72, 'Michael', 'Widenius', '1998-06-25', NULL, 13)被插入分區(qū)p2;但是,如果一條數(shù)據(jù)的store_id = 21,會(huì)怎么樣呢?由于沒有規(guī)則處理大于20的情況,所以服務(wù)器會(huì)報(bào)錯(cuò)。你可以通過如下方式來處理這種情況:
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 MAXVALUE
);
MAXVALUE 表示最大的可能的整數(shù)值。現(xiàn)在,store_id 列值大于或等于16(定義了的最高值)的所有行都將保存在分區(qū)p3中。在將來的某個(gè)時(shí)候,當(dāng)商店數(shù)已經(jīng)增長(zhǎng)到25, 30, 或更多 ,可以使用ALTER TABLE語(yǔ)句為商店21-25, 26-30,等等增加新的分區(qū)
RANGE分區(qū)在如下場(chǎng)合特別有用:
(1) 當(dāng)需要?jiǎng)h除“舊的”數(shù)據(jù)時(shí)。 在上面的例子中,你只需簡(jiǎn)單地使用 “ALTER TABLE employees DROP PARTITION p0;”來刪除所有在1991年前就已經(jīng)停止工作的雇員相對(duì)應(yīng)的所有行。對(duì)于有大量行的表,這比運(yùn)行一個(gè)如“DELETE FROM employees WHERE YEAR(separated) <= 1990;”這樣的一個(gè)DELETE查詢要有效得多。
(2) 經(jīng)常依賴于分區(qū)屬性進(jìn)行查詢。例如,當(dāng)執(zhí)行一個(gè)如“SELECT COUNT(*) FROM employees WHERE YEAR(separated) = 2000 GROUP BY store_id;”這樣的查詢時(shí),MySQL可以很迅速地確定只有分區(qū)p2需要掃描,這是因?yàn)橛嘞碌姆謪^(qū)不可能包含有符合該WHERE子句的任何記錄。注:這種優(yōu)化還沒有在MySQL 5.1源程序中啟用,但是,有關(guān)工作正在進(jìn)行中。
范圍分區(qū)的缺點(diǎn)就是容易出現(xiàn)執(zhí)行偏斜,這會(huì)影響系統(tǒng)性能。
2.2.2、HASH分區(qū)
HASH分區(qū)主要用來確保數(shù)據(jù)在預(yù)先確定數(shù)目的分區(qū)中平均分布。在RANGE和LIST分區(qū)中,必須明確指定一個(gè)給定的列值或列值集合應(yīng)該保存在哪個(gè)分區(qū)中;而在HASH分區(qū)中,MySQL 自動(dòng)完成這些工作,你所要做的只是基于將要被哈希的列值指定一個(gè)列值或表達(dá)式,以及指定被分區(qū)的表將要被分割成的分區(qū)數(shù)量。
你可以通過要在CREATE TABLE 語(yǔ)句上添加一個(gè)“PARTITION BY HASH (expr)”子句,其中“expr”是一個(gè)返回一個(gè)整數(shù)的表達(dá)式。它可以僅僅是字段類型為MySQL 整型的一列的名字。此外,你很可能需要在后面再添加一個(gè)“PARTITIONS num”子句,其中num 是一個(gè)非負(fù)的整數(shù),它表示表將要被分割成分區(qū)的數(shù)量。比如:
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 HASH(store_id)
PARTITIONS 4;
如果沒有PARTITIONS語(yǔ)句,默認(rèn)分區(qū)數(shù)為1。但是,PARTITIONS后面沒有數(shù)字,系統(tǒng)會(huì)報(bào)錯(cuò)。
相對(duì)于范圍分區(qū),HASH分區(qū)更可能保證數(shù)據(jù)均衡分布。
2.2.3、子分區(qū)(Subpartitioning)
子分區(qū),也叫做復(fù)合分區(qū)(composite partitioning),是對(duì)分區(qū)表的每個(gè)分區(qū)的進(jìn)一步分割。例如,
CREATE TABLE ts (id INT, purchased DATE)
PARTITION BY RANGE( YEAR(purchased) )
SUBPARTITION BY HASH( TO_DAYS(purchased) )
SUBPARTITIONS 2 (
PARTITION p0 VALUES LESS THAN (1990),
PARTITION p1 VALUES LESS THAN (2000),
PARTITION p2 VALUES LESS THAN MAXVALUE
);
表ts 有3個(gè)RANGE分區(qū)。這3個(gè)分區(qū)中的每一個(gè)分區(qū)——p0, p1, 和 p2 ——又被進(jìn)一步分成了2個(gè)子分區(qū)。實(shí)際上,整個(gè)表被分成了3 * 2 = 6個(gè)分區(qū)。但是,由于PARTITION BY RANGE子句的作用,這些分區(qū)的頭2個(gè)只保存“purchased”列中值小于1990的那些記錄。
在MySQL 5.1中,對(duì)于已經(jīng)通過RANGE或LIST分區(qū)了的表再進(jìn)行分區(qū)。子分區(qū)既可以使用HASH希分區(qū),也可以使用KEY分區(qū)。
為了對(duì)個(gè)別的子分區(qū)指定選項(xiàng),使用SUBPARTITION 子句來明確定義子分區(qū)也是可能的。例如,創(chuàng)建在前面例子中給出的同一個(gè)表的、一個(gè)更加詳細(xì)的方式如下:
CREATE TABLE ts (id INT, purchased DATE)
PARTITION BY RANGE( YEAR(purchased) )
SUBPARTITION BY HASH( TO_DAYS(purchased) ) (
PARTITION p0 VALUES LESS THAN (1990) (
SUBPARTITION s0,
SUBPARTITION s1
),
PARTITION p1 VALUES LESS THAN (2000) (
SUBPARTITION s2,
SUBPARTITION s3
),
PARTITION p2 VALUES LESS THAN MAXVALUE (
SUBPARTITION s4,
SUBPARTITION s5
)
);
一些注意點(diǎn):
(1) 每個(gè)分區(qū)的子分區(qū)數(shù)必須相同;
(2) 如果在一個(gè)分區(qū)表上的任何分區(qū)上使用SUBPARTITION 來明確定義任何子分區(qū),那么就必須定義所有的子分區(qū);
(3) 每個(gè)SUBPARTITION子句必須包含一個(gè)子分區(qū)的名稱;
(4) MySQL 5.1.7及之前的版本,每個(gè)分區(qū)的子分區(qū)的名稱必須唯一,但是在整個(gè)表中,沒有必要唯一。從MySQL 5.1.8開始,子分區(qū)的名稱在整個(gè)表中都必須唯一。
子分區(qū)可以用于特別大的表,在多個(gè)磁盤間分配數(shù)據(jù)和索引。假設(shè)有6個(gè)磁盤,分別為/disk0, /disk1, /disk2等,對(duì)于如下例子:
CREATE TABLE ts (id INT, purchased DATE)
PARTITION BY RANGE( YEAR(purchased) )
SUBPARTITION BY HASH( TO_DAYS(purchased) ) (
PARTITION p0 VALUES LESS THAN (1990) (
SUBPARTITION s0
DATA DIRECTORY = '/disk0/data'
INDEX DIRECTORY = '/disk0/idx',
SUBPARTITION s1
DATA DIRECTORY = '/disk1/data'
INDEX DIRECTORY = '/disk1/idx'
),
PARTITION p1 VALUES LESS THAN (2000) (
SUBPARTITION s2
DATA DIRECTORY = '/disk2/data'
INDEX DIRECTORY = '/disk2/idx',
SUBPARTITION s3
DATA DIRECTORY = '/disk3/data'
INDEX DIRECTORY = '/disk3/idx'
),
PARTITION p2 VALUES LESS THAN MAXVALUE (
SUBPARTITION s4
DATA DIRECTORY = '/disk4/data'
INDEX DIRECTORY = '/disk4/idx',
SUBPARTITION s5
DATA DIRECTORY = '/disk5/data'
INDEX DIRECTORY = '/disk5/idx'
)
);
3、體驗(yàn)分區(qū)
下面通過例子來體驗(yàn)分區(qū):
(1)創(chuàng)建如下分區(qū)表:
CREATE TABLE part_tab
( c1 int default NULL,
c2 varchar(30) default NULL,
c3 date default NULL
) engine=myisam
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 );
(2)創(chuàng)建一個(gè)不分區(qū)的表:
create table no_part_tab
(c1 int(11) default NULL,
c2 varchar(30) default NULL,
c3 date default NULL
) engine=myisam;
(1) 創(chuàng)建一個(gè)生成8000行數(shù)據(jù)的存儲(chǔ)過程:
delimiter //
CREATE PROCEDURE load_part_tab()
begin
declare v int default 0;
while v < 8000000
do
insert into part_tab
values (v,'testing partitions',adddate('1995-01-01',(rand(v)*36520) mod 3652));
set v = v + 1;
end while;
end
//
(2) 調(diào)用存儲(chǔ)過程,生成數(shù)據(jù):
mysql> delimiter ;
mysql> call load_part_tab();
Query OK, 1 row affected (6 min 35.39 sec)
(5)
mysql> insert into no_part_tab select * from part_tab;
Query OK, 8000000 rows affected (40.98 sec)
Records: 8000000 Duplicates: 0 Warnings: 0
數(shù)據(jù)準(zhǔn)備好了,下面開始測(cè)試:
(6)
mysql> select count(*) from no_part_tab where
-> c3 > date '1995-01-01' and c3 < date '1995-12-31';
+----------+
| count(*) |
+----------+
| 795181 |
+----------+
1 row in set (4.23 sec)
mysql> select count(*) from part_tab where
-> c3 > date '1995-01-01' and c3 < date '1995-12-31';
+----------+
| count(*) |
+----------+
| 795181 |
+----------+
1 row in set (0.55 sec)
速度差異很明顯;下面看一下查詢計(jì)劃:
(8)
mysql> explain select count(*) from no_part_tab where
-> c3 > date '1995-01-01' and c3 < date '1995-12-31'\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: no_part_tab
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 8000000
Extra: Using where
1 row in set (0.00 sec)
mysql> explain select count(*) from part_tab where
-> c3 > date '1995-01-01' and c3 < date '1995-12-31'\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: part_tab
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 8000000 #why ???
Extra: Using where
1 row in set (0.00 sec)
查詢計(jì)劃的輸出不太對(duì),我用的5.1.36版,也許是一個(gè)BUG。但是,這不影響我們對(duì)速度的體驗(yàn)。
附SQL語(yǔ)句:
代碼
主要參考:《MySQL Manual》