獲取關(guān)于分區(qū)的信息
? ? ? 本章討論MySQL 5.1.中實現(xiàn)的分區(qū)趾徽。關(guān)于分區(qū)和分區(qū)概念的介紹可以在18.1節(jié)匪蝙,“MySQL中的分區(qū)概述”中找到逗宜。MySQL 5.1 支持哪幾種類型的分區(qū)枕荞,在18.2節(jié)线欲,“分區(qū)類型” 中討論造烁。關(guān)于子分區(qū)在18.2.5節(jié)否过,“子分區(qū)” 中討論。現(xiàn)有分區(qū)表中分區(qū)的增加惭蟋、刪除和修改的方法在18.3節(jié)苗桂,“分區(qū)管理” 中介紹。 和分區(qū)表一同使用的表維護命令在18.3.3節(jié)告组,“分區(qū)維護” 中介紹煤伟。
? ? ? 請注意:MySQL 5.1中的分區(qū)實現(xiàn)仍然很新(pre-alpha品質(zhì)),此時還不是可生產(chǎn)的(not production-ready)木缝。 同樣便锨,許多也適用于本章:在這里描述的一些功能還沒有實際上實現(xiàn)(分區(qū)維護和重新分區(qū)命令),其他的可能還沒有完全如所描述的那樣實現(xiàn)(例如, 用于分區(qū)的數(shù)據(jù)目錄(DATA DIRECTORY)和索引目錄(INDEX DIRECTORY)選項受到Bug #13520) 不利的影響). 我們已經(jīng)設(shè)法在本章中標(biāo)出這些差異我碟。在提出缺陷報告前放案,我們鼓勵參考下面的一些資源:
? ? ? MySQL 5.1的二進制版本目前還不可用;但是矫俺,可以從BitKeeper知識庫中獲得源碼吱殉。要激活分區(qū),需要使用--with-分區(qū)選項編譯服務(wù)器厘托。關(guān)于建立MySQL 的更多信息友雳,請參見2.8節(jié),“使用源碼分發(fā)版安裝MySQL”铅匹。如果在編譯一個激活分區(qū)的MySQL 5.1創(chuàng)建中碰到問題押赊,可以在MySQL分區(qū)論壇中查找解決辦法,如果在論壇中已經(jīng)貼出的文章中沒有找到問題的解決辦法包斑,可以在上面尋找?guī)椭?br>
18.1. MySQL中的分區(qū)概述
? ? ? 本節(jié)提供了關(guān)于MySQL 5.1.分區(qū)在概念上的概述流礁。
? ? ? SQL標(biāo)準(zhǔn)在數(shù)據(jù)存儲的物理方面沒有提供太多的指南涕俗。SQL語言的使用獨立于它所使用的任何數(shù)據(jù)結(jié)構(gòu)或圖表、表崇棠、行或列下的介質(zhì)咽袜。但是,大部分高級數(shù)據(jù)庫管理系統(tǒng)已經(jīng)開發(fā)了一些根據(jù)文件系統(tǒng)枕稀、硬件或者這兩者來確定將要用于存儲特定數(shù)據(jù)塊物理位置的方法询刹。在MySQL中,InnoDB存儲引擎長期支持表空間的概念萎坷,并且MySQL服務(wù)器甚至在分區(qū)引入之前凹联,就能配置為存儲不同的數(shù)據(jù)庫使用不同的物理路徑(關(guān)于如何配置的解釋,請參見7.6.1節(jié)哆档,“使用符號鏈接”)蔽挠。
? ? ? 分區(qū)又把這個概念推進了一步,它允許根據(jù)可以設(shè)置為任意大小的規(guī)則瓜浸,跨文件系統(tǒng)分配單個表的多個部分澳淑。實際上,表的不同部分在不同的位置被存儲為單獨的表插佛。用戶所選擇的杠巡、實現(xiàn)數(shù)據(jù)分割的規(guī)則被稱為分區(qū)函數(shù),這在MySQL中它可以是模數(shù)雇寇,或者是簡單的匹配一個連續(xù)的數(shù)值區(qū)間或數(shù)值列表氢拥,或者是一個內(nèi)部HASH函數(shù),或一個線性HASH函數(shù)锨侯。函數(shù)根據(jù)用戶指定的分區(qū)類型來選擇嫩海,把用戶提供的表達式的值作為參數(shù)。該表達式可以是一個整數(shù)列值囚痴,或一個作用在一個或多個列值上并返回一個整數(shù)的函數(shù)叁怪。這個表達式的值傳遞給分區(qū)函數(shù),分區(qū)函數(shù)返回一個表示那個特定記錄應(yīng)該保存在哪個分區(qū)的序號深滚。這個函數(shù)不能是常數(shù)骂束,也不能是任意數(shù)。它不能包含任何查詢成箫,但是實際上可以使用MySQL 中任何可用的SQL表達式,只要該表達式返回一個小于MAXVALUE(最大可能的正整數(shù))的正數(shù)值旨枯。分區(qū)函數(shù)的例子可以在本章后面關(guān)于分區(qū)類型的討論中找到 (請參見18.2節(jié)蹬昌,“分區(qū)類型” ),也可在13.1.5節(jié)攀隔,“CREATE TABLE語法”的分區(qū)語法描述中找到皂贩。
? ? ? 當(dāng)二進制碼變成可用時(也就是說栖榨,5.1 -max 二進制碼將通過--with-partition 建立),分區(qū)支持就將包含在MySQL 5.1的-max 版本中明刷。如果MySQL二進制碼是使用分區(qū)支持建立的婴栽,那么激活它不需要任何其他的東西 (例如,在my.cnf 文件中辈末,不需要特殊的條目)愚争。可以通過使用SHOW VARIABLES命令來確定MySQL是否支持分區(qū)挤聘,例如:
mysql> SHOW VARIABLES LIKE '%partition%'; +-----------------------+-------+| Variable_name? ? ? ? | Value |+-----------------------+-------+| have_partition_engine | YES? |+-----------------------+-------+1 row in set (0.00 sec)
? ? ? 在如上列出的一個正確的SHOW VARIABLES 命令所產(chǎn)生的輸出中轰枝,如果沒有看到變量have_partition_engine的值為YES,那么MySQL的版本就不支持分區(qū)组去。(注意:在顯示任何有關(guān)分區(qū)支持信息的命令SHOW ENGINES的輸出中鞍陨,不會給出任何信息;必須使用SHOW VARIABLES命令來做出這個判斷)从隆。
? ? ? 對于創(chuàng)建了分區(qū)的表诚撵,可以使用你的MySQL 服務(wù)器所支持的任何存儲引擎;MySQL 分區(qū)引擎在一個單獨的層中運行键闺,并且可以和任何這樣的層進行相互作用寿烟。在MySQL 5.1版中,同一個分區(qū)表的所有分區(qū)必須使用同一個存儲引擎艾杏;例如韧衣,不能對一個分區(qū)使用MyISAM,而對另一個使用InnoDB购桑。但是畅铭,這并不妨礙在同一個 MySQL 服務(wù)器中,甚至在同一個數(shù)據(jù)庫中勃蜘,對于不同的分區(qū)表使用不同的存儲引擎硕噩。
? ? ? 要為某個分區(qū)表配置一個專門的存儲引擎,必須且只能使用[STORAGE] ENGINE 選項缭贡,這如同為非分區(qū)表配置存儲引擎一樣炉擅。但是,必須記住[STORAGE] ENGINE(和其他的表選項)必須列在用在CREATE TABLE語句中的其他任何分區(qū)選項之前阳惹。下面的例子給出了怎樣創(chuàng)建一個通過HASH分成6個分區(qū)谍失、使用InnoDB存儲引擎的表:
CREATE TABLE ti (id INT, amount DECIMAL(7,2), tr_date DATE)? ? ENGINE=INNODB? ? PARTITION BY HASH(MONTH(tr_date))? ? PARTITIONS 6;
? ? ? (注釋:每個PARTITION 子句可以包含一個 [STORAGE] ENGINE 選項,但是在MySQL 5.1版本中莹汤,這沒有作用)快鱼。
? ? ? 創(chuàng)建分區(qū)的臨時表也是可能的;但是,這種表的生命周期只有當(dāng)前MySQL 的會話的時間那么長抹竹。對于非分區(qū)的臨時表线罕,這也是一樣的。
? ? ? 注釋:分區(qū)適用于一個表的所有數(shù)據(jù)和索引窃判;不能只對數(shù)據(jù)分區(qū)而不對索引分區(qū)钞楼,反之亦然,同時也不能只對表的一部分進行分區(qū)袄琳。
? ? ? 可以通過使用用來創(chuàng)建分區(qū)表的CREATE TABLE語句的PARTITION子句的DATA DIRECTORY(數(shù)據(jù)路徑)和INDEX DIRECTORY(索引路徑)選項询件,為每個分區(qū)的數(shù)據(jù)和索引指定特定的路徑。此外跨蟹,MAX_ROWS和MIN_ROWS選項可以用來設(shè)定最大和最小的行數(shù)雳殊,它們可以各自保存在每個分區(qū)里。關(guān)于這些選項的更多信息窗轩,請參見18.3節(jié)夯秃,“分區(qū)管理”。注釋:這個特殊的功能由于Bug #13250的原因痢艺,目前還不能實用仓洼。在第一個5.1二進制版本投入使用時,我們應(yīng)該已經(jīng)把這個問題解決了堤舒。
? ? ? 分區(qū)的一些優(yōu)點包括:
? ? ? ·? ? ? ? 與單個磁盤或文件系統(tǒng)分區(qū)相比色建,可以存儲更多的數(shù)據(jù)。
? ? ? ·? ? ? ? 對于那些已經(jīng)失去保存意義的數(shù)據(jù)舌缤,通郴粒可以通過刪除與那些數(shù)據(jù)有關(guān)的分區(qū),很容易地刪除那些數(shù)據(jù)国撵。相反地陵吸,在某些情況下,添加新數(shù)據(jù)的過程又可以通過為那些新數(shù)據(jù)專門增加一個新的分區(qū)介牙,來很方便地實現(xiàn)壮虫。
? ? ? 通常和分區(qū)有關(guān)的其他優(yōu)點包括下面列出的這些。MySQL 分區(qū)中的這些功能目前還沒有實現(xiàn)环础,但是在我們的優(yōu)先級列表中囚似,具有高的優(yōu)先級;我們希望在5.1的生產(chǎn)版本中线得,能包括這些功能饶唤。
? ? ? ·? ? ? ? 一些查詢可以得到極大的優(yōu)化,這主要是借助于滿足一個給定WHERE 語句的數(shù)據(jù)可以只保存在一個或多個分區(qū)內(nèi)贯钩,這樣在查找時就不用查找其他剩余的分區(qū)厌小。因為分區(qū)可以在創(chuàng)建了分區(qū)表后進行修改,所以在第一次配置分區(qū)方案時還不曾這么做時忽刽,可以重新組織數(shù)據(jù)漂坏,來提高那些常用查詢的效率。
? ? ? ·? ? ? ? 涉及到例如SUM() 和 COUNT()這樣聚合函數(shù)的查詢谓罗,可以很容易地進行并行處理粱哼。這種查詢的一個簡單例子如 “SELECT salesperson_id, COUNT(orders) as order_total FROM sales GROUP BY salesperson_id;”檩咱。通過“并行”揭措, 這意味著該查詢可以在每個分區(qū)上同時進行,最終結(jié)果只需通過總計所有分區(qū)得到的結(jié)果刻蚯。
? ? ? ·? ? ? ? 通過跨多個磁盤來分散數(shù)據(jù)查詢绊含,來獲得更大的查詢吞吐量。
? ? ? 要經(jīng)常檢查本頁和本章炊汹,因為它將隨MySQL 5.1后續(xù)的分區(qū)進展而更新躬充。
18.2. 分區(qū)類型
18.2.1. RANGE分區(qū)
18.2.2. LIST分區(qū)
18.2.3. HASH分區(qū)
18.2.4. KEY分區(qū)
18.2.5. 子分區(qū)
18.2.6. MySQL分區(qū)處理NULL值的方式
? ? ? 本節(jié)討論在MySQL 5.1中可用的分區(qū)類型。這些類型包括:
? ? ? ·? ? ? ? RANGE 分區(qū):基于屬于一個給定連續(xù)區(qū)間的列值讨便,把多行分配給分區(qū)充甚。參見18.2.1節(jié),“RANGE分區(qū)”霸褒。
? ? ? ·? ? ? ? LIST 分區(qū):類似于按RANGE分區(qū)伴找,區(qū)別在于LIST分區(qū)是基于列值匹配一個離散值集合中的某個值來進行選擇。參見18.2.2節(jié)废菱,“LIST分區(qū)”技矮。
? ? ? ·? ? ? ? HASH分區(qū):基于用戶定義的表達式的返回值來進行選擇的分區(qū),該表達式使用將要插入到表中的這些行的列值進行計算殊轴。這個函數(shù)可以包含MySQL 中有效的衰倦、產(chǎn)生非負整數(shù)值的任何表達式。參見18.2.3節(jié)梳凛,“HASH分區(qū)”耿币。
? ? ? ·? ? ? ? KEY 分區(qū):類似于按HASH分區(qū),區(qū)別在于KEY分區(qū)只支持計算一列或多列韧拒,且MySQL 服務(wù)器提供其自身的哈希函數(shù)淹接。必須有一列或多列包含整數(shù)值。參見18.2.4節(jié)叛溢,“KEY分區(qū)”塑悼。
? ? ? 無論使用何種類型的分區(qū),分區(qū)總是在創(chuàng)建時就自動的順序編號楷掉,且從0開始記錄厢蒜,記住這一點非常重要。當(dāng)有一新行插入到一個分區(qū)表中時,就是使用這些分區(qū)編號來識別正確的分區(qū)斑鸦。例如愕贡,如果你的表使用4個分區(qū),那么這些分區(qū)就編號為0, 1, 2, 和3巷屿。對于RANGE和LIST分區(qū)類型固以,確認每個分區(qū)編號都定義了一個分區(qū),很有必要嘱巾。對HASH分區(qū)憨琳,使用的用戶函數(shù)必須返回一個大于0的整數(shù)值。對于KEY分區(qū)旬昭,這個問題通過MySQL服務(wù)器內(nèi)部使用的 哈希函數(shù)自動進行處理篙螟。
? ? ? 分區(qū)的名字基本上遵循其他MySQL 標(biāo)識符應(yīng)當(dāng)遵循的原則,例如用于表和數(shù)據(jù)庫名字的標(biāo)識符问拘。但是應(yīng)當(dāng)注意遍略,分區(qū)的名字是不區(qū)分大小寫的。例如场梆,下面的CREATE TABLE語句將會產(chǎn)生如下的錯誤:
mysql> CREATE TABLE t2 (val INT)? ? -> PARTITION BY LIST(val)(? ? ->? ? PARTITION mypart VALUES IN (1,3,5),? ? ->? ? PARTITION MyPart VALUES IN (2,4,6)? ? -> );錯誤1488 (HY000): 表的所有分區(qū)必須有唯一的名字墅冷。
? ? ? 這是因為MySQL認為分區(qū)名字mypart和MyPart沒有區(qū)別。
? ? ? 注釋:在下面的章節(jié)中或油,我們沒有必要提供可以用來創(chuàng)建每種分區(qū)類型語法的所有可能形式寞忿,這些信息可以在13.1.5節(jié),“CREATE TABLE語法” 中找到顶岸。
? ? ? 18.2.1. RANGE分區(qū)
? ? ? 按照RANGE分區(qū)的表是通過如下一種方式進行分區(qū)的腔彰,每個分區(qū)包含那些分區(qū)表達式的值位于一個給定的連續(xù)區(qū)間內(nèi)的行。這些區(qū)間要連續(xù)且不能相互重疊辖佣,使用VALUES LESS THAN操作符來進行定義霹抛。在下面的幾個例子中,假定你創(chuàng)建了一個如下的一個表卷谈,該表保存有20家音像店的職員記錄杯拐,這20家音像店的編號從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ù)你的需要端逼,這個表可以有多種方式來按照區(qū)間進行分區(qū)。一種方式是使用store_id 列污淋。例如顶滩,你可能決定通過添加一個PARTITION BY RANGE子句把這個表分割成4個區(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))寸爆;
? ? ? 按照這種分區(qū)方案礁鲁,在商店1到5工作的雇員相對應(yīng)的所有行被保存在分區(qū)P0中盐欺,商店6到10的雇員保存在P1中,依次類推仅醇。注意冗美,每個分區(qū)都是按順序進行定義,從最低到最高着憨。這是PARTITION BY RANGE 語法的要求墩衙;在這點上,它類似于C或Java中的“switch ... case”語句甲抖。
? ? ? 對于包含數(shù)據(jù)(72, 'Michael', 'Widenius', '1998-06-25', NULL, 13)的一個新行,可以很容易地確定它將插入到p2分區(qū)中心铃,但是如果增加了一個編號為第21的商店准谚,將會發(fā)生什么呢?在這種方案下去扣,由于沒有規(guī)則把store_id大于20的商店包含在內(nèi)柱衔,服務(wù)器將不知道把該行保存在何處,將會導(dǎo)致錯誤愉棱。 要避免這種錯誤唆铐,可以通過在CREATE TABLE語句中使用一個“catchall” 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 MAXVALUE)奔滑;
? ? ? MAXVALUE 表示最大的可能的整數(shù)值“瘢現(xiàn)在,store_id 列值大于或等于16(定義了的最高值)的所有行都將保存在分區(qū)p3中朋其。在將來的某個時候王浴,當(dāng)商店數(shù)已經(jīng)增長到25, 30, 或更多 ,可以使用ALTER TABLE語句為商店21-25, 26-30,等等增加新的分區(qū) (關(guān)于如何實現(xiàn)的詳細信息參見18.3節(jié)梅猿,“分區(qū)管理” )氓辣。
? ? ? 在幾乎一樣的結(jié)構(gòu)中,你還可以基于雇員的工作代碼來分割表袱蚓,也就是說钞啸,基于job_code 列值的連續(xù)區(qū)間。例如——假定2位數(shù)字的工作代碼用來表示普通(店內(nèi)的)工人喇潘,三個數(shù)字代碼表示辦公室和支持人員体斩,四個數(shù)字代碼表示管理層,你可以使用下面的語句創(chuàng)建該分區(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 (job_code) (? ? PARTITION p0 VALUES LESS THAN (100),? ? PARTITION p1 VALUES LESS THAN (1000),? ? PARTITION p2 VALUES LESS THAN (10000))响蓉;
? ? ? 在這個例子中, 店內(nèi)工人相關(guān)的所有行將保存在分區(qū)p0中硕勿,辦公室和支持人員相關(guān)的所有行保存在分區(qū)p1中,管理層相關(guān)的所有行保存在分區(qū)p2中枫甲。
? ? ? 在VALUES LESS THAN 子句中使用一個表達式也是可能的源武。這里最值得注意的限制是MySQL 必須能夠計算表達式的返回值作為LESS THAN (<)比較的一部分扼褪;因此,表達式的值不能為NULL 粱栖。由于這個原因话浇,雇員表的hired, separated, job_code,和store_id列已經(jīng)被定義為非空(NOT NULL)。
? ? ? 除了可以根據(jù)商店編號分割表數(shù)據(jù)外闹究,你還可以使用一個基于兩個DATE (日期)中的一個的表達式來分割表數(shù)據(jù)幔崖。例如,假定你想基于每個雇員離開公司的年份來分割表渣淤,也就是說赏寇,YEAR(separated)的值。實現(xiàn)這種分區(qū)模式的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 RANGE (YEAR(separated)) (? ? PARTITION p0 VALUES LESS THAN (1991),? ? PARTITION p1 VALUES LESS THAN (1996),? ? PARTITION p2 VALUES LESS THAN (2001),? ? PARTITION p3 VALUES LESS THAN MAXVALUE)价认;
? ? ? 在這個方案中嗅定,在1991年前雇傭的所有雇員的記錄保存在分區(qū)p0中,1991年到1995年期間雇傭的所有雇員的記錄保存在分區(qū)p1中用踩, 1996年到2000年期間雇傭的所有雇員的記錄保存在分區(qū)p2中渠退,2000年后雇傭的所有工人的信息保存在p3中。
? ? ? RANGE分區(qū)在如下場合特別有用:
? ? ? ·? ? ? ? 當(dāng)需要刪除“舊的”數(shù)據(jù)時脐彩。如果你使用上面最近的那個例子給出的分區(qū)方案碎乃,你只需簡單地使用 “ALTER TABLE employees DROP PARTITION p0;”來刪除所有在1991年前就已經(jīng)停止工作的雇員相對應(yīng)的所有行惠奸。(更多信息請參見13.1.2節(jié)梅誓,“ALTER TABLE語法” 和 18.3節(jié),“分區(qū)管理”)晨川。對于有大量行的表证九,這比運行一個如“DELETE FROM employees WHERE YEAR(separated) <= 1990;”這樣的一個DELETE查詢要有效得多共虑。
? ? ? ·? ? ? ? 想要使用一個包含有日期或時間值愧怜,或包含有從一些其他級數(shù)開始增長的值的列。
? ? ? ·? ? ? ? 經(jīng)常運行直接依賴于用于分割表的列的查詢妈拌。例如拥坛,當(dāng)執(zhí)行一個如“SELECT COUNT(*) FROM employees WHERE YEAR(separated) = 2000 GROUP BY store_id;”這樣的查詢時尘分,MySQL可以很迅速地確定只有分區(qū)p2需要掃描猜惋,這是因為余下的分區(qū)不可能包含有符合該WHERE子句的任何記錄。注釋:這種優(yōu)化還沒有在MySQL 5.1源程序中啟用培愁,但是著摔,有關(guān)工作正在進行中。
? ? ? 18.2.2. LIST分區(qū)
? ? ? MySQL中的LIST分區(qū)在很多方面類似于RANGE分區(qū)定续。和按照RANGE分區(qū)一樣谍咆,每個分區(qū)必須明確定義禾锤。它們的主要區(qū)別在于,LIST分區(qū)中每個分區(qū)的定義和選擇是基于某列的值從屬于一個值列表集中的一個值摹察,而RANGE分區(qū)是從屬于一個連續(xù)區(qū)間值的集合恩掷。LIST分區(qū)通過使用“PARTITION BY LIST(expr)”來實現(xiàn),其中“expr” 是某列值或一個基于某個列值供嚎、并返回一個整數(shù)值的表達式黄娘,然后通過“VALUES IN (value_list)”的方式來定義每個分區(qū),其中“value_list”是一個通過逗號分隔的整數(shù)列表克滴。
? ? ? 注釋:在MySQL 5.1中逼争,當(dāng)使用LIST分區(qū)時,有可能只能匹配整數(shù)列表劝赔。
? ? ? 不像按照RANGE定義分區(qū)的情形氮凝,LIST分區(qū)不必聲明任何特定的順序。關(guān)于LIST分區(qū)更詳細的語法信息望忆,請參考13.1.5節(jié),“CREATE TABLE語法” 竿秆。
? ? ? 對于下面給出的例子启摄,我們假定將要被分區(qū)的表的基本定義是通過下面的“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);
? ? ? (這和18.2.1節(jié)幽钢,“RANGE分區(qū)” 中的例子中使用的是同一個表)歉备。
? ? ? 假定有20個音像店,分布在4個有經(jīng)銷權(quán)的地區(qū)匪燕,如下表所示:
地區(qū)
商店ID 號
北區(qū)
3, 5, 6, 9, 17
東區(qū)
1, 2, 10, 11, 19, 20
西區(qū)
4, 12, 13, 14, 18
中心區(qū)
7, 8, 15, 16
? ? ? 要按照屬于同一個地區(qū)商店的行保存在同一個分區(qū)中的方式來分割表蕾羊,可以使用下面的“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));
? ? ? 這使得在表中增加或刪除指定地區(qū)的雇員記錄變得容易起來帽驯。例如龟再,假定西區(qū)的所有音像店都賣給了其他公司。那么與在西區(qū)音像店工作雇員相關(guān)的所有記錄(行)可以使用查詢“ALTER TABLE employees DROP PARTITION pWest尼变;”來進行刪除利凑,它與具有同樣作用的DELETE (刪除)查詢“DELETE query DELETE FROM employees WHERE store_id IN (4,12,13,14,18);”比起來嫌术,要有效得多哀澈。
? ? ? 要點:如果試圖插入列值(或分區(qū)表達式的返回值)不在分區(qū)值列表中的一行時,那么“INSERT”查詢將失敗并報錯度气。例如割按,假定LIST分區(qū)的采用上面的方案,下面的查詢將失斄准:
INSERT INTO employees VALUES? ? (224, 'Linus', 'Torvalds', '2002-05-01', '2004-10-12', 42, 21);
? ? ? 這是因為“store_id”列值21不能在用于定義分區(qū)pNorth, pEast, pWest,或pCentral的值列表中找到适荣。要重點注意的是现柠,LIST分區(qū)沒有類似如“VALUES LESS THAN MAXVALUE”這樣的包含其他值在內(nèi)的定義。將要匹配的任何值都必須在值列表中找到束凑。
? ? ? LIST分區(qū)除了能和RANGE分區(qū)結(jié)合起來生成一個復(fù)合的子分區(qū)晒旅,與HASH和KEY分區(qū)結(jié)合起來生成復(fù)合的子分區(qū)也是可能的。 關(guān)于這方面的討論汪诉,請參考18.2.5節(jié)废恋,“子分區(qū)”。
? ? ? 18.2.3. HASH分區(qū)
18.2.3.1. LINEAR HASH分區(qū)
? ? ? HASH分區(qū)主要用來確保數(shù)據(jù)在預(yù)先確定數(shù)目的分區(qū)中平均分布扒寄。在RANGE和LIST分區(qū)中鱼鼓,必須明確指定一個給定的列值或列值集合應(yīng)該保存在哪個分區(qū)中;而在HASH分區(qū)中该编,MySQL 自動完成這些工作迄本,你所要做的只是基于將要被哈希的列值指定一個列值或表達式,以及指定被分區(qū)的表將要被分割成的分區(qū)數(shù)量课竣。
? ? ? 要使用HASH分區(qū)來分割一個表嘉赎,要在CREATE TABLE 語句上添加一個“PARTITION BY HASH (expr)”子句,其中“expr”是一個返回一個整數(shù)的表達式于樟。它可以僅僅是字段類型為MySQL 整型的一列的名字公条。此外,你很可能需要在后面再添加一個“PARTITIONS num”子句迂曲,其中num 是一個非負的整數(shù)靶橱,它表示表將要被分割成分區(qū)的數(shù)量。
? ? ? 例如路捧,下面的語句創(chuàng)建了一個使用基于“store_id”列進行 哈希處理的表关霸,該表被分成了4個分區(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,? ? store_id INT)PARTITION BY HASH(store_id)PARTITIONS 4;
? ? ? 如果沒有包括一個PARTITIONS子句杰扫,那么分區(qū)的數(shù)量將默認為1队寇。 例外: 對于NDB Cluster(簇)表,默認的分區(qū)數(shù)量將與簇數(shù)據(jù)節(jié)點的數(shù)量相同涉波,這種修正可能是考慮任何MAX_ROWS 設(shè)置英上,以便確保所有的行都能合適地插入到分區(qū)中。(參見第17章:MySQL簇)啤覆。
? ? ? 如果在關(guān)鍵字“PARTITIONS”后面沒有加上分區(qū)的數(shù)量苍日,將會出現(xiàn)語法錯誤。
? ? ? “expr”還可以是一個返回一個整數(shù)的SQL表達式窗声。例如相恃,也許你想基于雇用雇員的年份來進行分區(qū)。這可以通過下面的語句來實現(xiàn):
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(YEAR(hired))PARTITIONS 4笨觅;
? ? ? “expr”還可以是MySQL 中有效的任何函數(shù)或其他表達式拦耐,只要它們返回一個既非常數(shù)耕腾、也非隨機數(shù)的整數(shù)。(換句話說杀糯,它既是變化的但又是確定的)扫俺。但是應(yīng)當(dāng)記住,每當(dāng)插入或更新(或者可能刪除)一行固翰,這個表達式都要計算一次狼纬;這意味著非常復(fù)雜的表達式可能會引起性能問題,尤其是在執(zhí)行同時影響大量行的運算(例如批量插入)的時候骂际。
? ? ? 最有效率的哈希函數(shù)是只對單個表列進行計算疗琉,并且它的值隨列值進行一致地增大或減小,因為這考慮了在分區(qū)范圍上的“修剪”歉铝。也就是說盈简,表達式值和它所基于的列的值變化越接近,MySQL就可以越有效地使用該表達式來進行HASH分區(qū)太示。
? ? ? 例如柠贤,“date_col” 是一個DATE(日期)類型的列,那么表達式TO_DAYS(date_col)就可以說是隨列“date_col”值的變化而發(fā)生直接的變化类缤,因為列“date_col”值的每個變化种吸,表達式的值也將發(fā)生與之一致的變化。而表達式Y(jié)EAR(date_col)的變化就沒有表達式TO_DAYS(date_col)那么直接呀非,因為不是列“date_col”每次可能的改變都能使表達式Y(jié)EAR(date_col)發(fā)生同等的改變。即便如此镜盯,表達式Y(jié)EAR(date_col)也還是一個用于 哈希函數(shù)的岸裙、好的候選表達式,因為它隨列date_col的一部分發(fā)生直接變化速缆,并且列date_col的變化不可能引起表達式Y(jié)EAR(date_col)不成比例的變化降允。
? ? ? 作為對照,假定有一個類型為整型(INT)的艺糜、列名為“int_col”的列【缍現(xiàn)在考慮表達式“POW(5-int_col,3) + 6”。這對于哈希函數(shù)就是一個不好的選擇破停,因為“int_col”值的變化并不能保證表達式產(chǎn)生成比例的變化翅楼。列 “int_col”的值發(fā)生一個給定數(shù)目的變化,可能會引起表達式的值產(chǎn)生一個很大不同的變化真慢。例如毅臊,把列“int_col”的值從5變?yōu)?,表達式的值將產(chǎn)生“-1”的改變黑界,但是把列“int_col”的值從6變?yōu)?時管嬉,表達式的值將產(chǎn)生“-7”的變化皂林。
? ? ? 換句話說,如果列值與表達式值之比的曲線圖越接近由等式“y=nx(其中n為非零的常數(shù))描繪出的直線蚯撩,則該表達式越適合于 哈希础倍。這是因為,表達式的非線性越嚴重胎挎,分區(qū)中數(shù)據(jù)產(chǎn)生非均衡分布的趨勢也將越嚴重沟启。
? ? ? 理論上講,對于涉及到多列的表達式呀癣,“修剪(pruning)”也是可能的美浦,但是要確定哪些適于 哈希是非常困難和耗時的∠罾福基于這個原因浦辨,實際上不推薦使用涉及到多列的哈希表達式。
? ? ? 當(dāng)使用了“PARTITION BY HASH”時沼沈,MySQL將基于用戶函數(shù)結(jié)果的模數(shù)來確定使用哪個編號的分區(qū)流酬。換句話,對于一個表達式“expr”列另,將要保存記錄的分區(qū)編號為N 芽腾,其中“N = MOD(expr, num)”。例如页衙,假定表t1 定義如下摊滔,它有4個分區(qū):
CREATE TABLE t1 (col1 INT, col2 CHAR(5), col3 DATE)? ? PARTITION BY HASH( YEAR(col3) )? ? PARTITIONS 4;
? ? ? 如果插入一個col3列值為'2005-09-15'的記錄到表t1中店乐,那么保存該條記錄的分區(qū)確定如下:
MOD(YEAR('2005-09-01'),4)=? MOD(2005,4)=? 1
? ? ? MySQL 5.1 還支持一個被稱為“l(fā)inear hashing(線性哈希功能)”的變量艰躺,它使用一個更加復(fù)雜的算法來確定新行插入到已經(jīng)分區(qū)了的表中的位置。關(guān)于這種算法的描述眨八,請參見18.2.3.1節(jié)腺兴,“LINEAR HASH分區(qū)” 。
? ? ? 每當(dāng)插入或更新一條記錄廉侧,用戶函數(shù)都要計算一次页响。當(dāng)刪除記錄時,用戶函數(shù)也可能要進行計算段誊,這取決于所處的環(huán)境闰蚕。
? ? ? 注釋:如果將要分區(qū)的表有一個唯一的鍵,那么用來作為HASH用戶函數(shù)的自變數(shù)或者主鍵的column_list的自變數(shù)的任意列都必須是那個鍵的一部分连舍。
? ? ? 18.2.3.1. LINEAR HASH分區(qū)
? ? ? MySQL還支持線性哈希功能陪腌,它與常規(guī)哈希的區(qū)別在于,線性哈希功能使用的一個線性的2的冪(powers-of-two)運算法則,而常規(guī) 哈希使用的是求哈希函數(shù)值的模數(shù)诗鸭。
? ? ? 線性哈希分區(qū)和常規(guī)哈希分區(qū)在語法上的唯一區(qū)別在于染簇,在“PARTITION BY” 子句中添加“LINEAR”關(guān)鍵字,如下面所示:
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 LINEAR HASH(YEAR(hired))PARTITIONS 4强岸;
? ? ? 假設(shè)一個表達式expr, 當(dāng)使用線性哈希功能時锻弓,記錄將要保存到的分區(qū)是num 個分區(qū)中的分區(qū)N,其中N是根據(jù)下面的算法得到:
? ? ? 1.? ? 找到下一個大于num.的蝌箍、2的冪青灼,我們把這個值稱為V ,它可以通過下面的公式得到:
2.? ? ? ? ? V = POWER(2, CEILING(LOG(2, num)))
? ? ? (例如妓盲,假定num是13杂拨。那么LOG(2,13)就是3.7004397181411。 CEILING(3.7004397181411)就是4悯衬,則V = POWER(2,4), 即等于16)弹沽。
? ? ? 3.? ? 設(shè)置 N = F(column_list) & (V - 1).
? ? ? 4.? ? 當(dāng) N >= num:
? ? ? ·? ? ? ? 設(shè)置 V = CEIL(V / 2)
? ? ? ·? ? ? ? 設(shè)置 N = N & (V - 1)
? ? ? 例如,假設(shè)表t1筋粗,使用線性哈希分區(qū)且有4個分區(qū)策橘,是通過下面的語句創(chuàng)建的:
CREATE TABLE t1 (col1 INT, col2 CHAR(5), col3 DATE)? ? PARTITION BY LINEAR HASH( YEAR(col3) )? ? PARTITIONS 6;
? ? ? 現(xiàn)在假設(shè)要插入兩行記錄到表t1中,其中一條記錄col3列值為'2003-04-14'娜亿,另一條記錄col3列值為'1998-10-19'炼列。第一條記錄將要保存到的分區(qū)確定如下:
V = POWER(2, CEILING(LOG(2,7))) = 8N = YEAR('2003-04-14') & (8 - 1)? = 2003 & 7? = 3 (3 >= 6 為假(FALSE): 記錄將被保存到#3號分區(qū)中)
? ? ? 第二條記錄將要保存到的分區(qū)序號計算如下:
V = 8N = YEAR('1998-10-19') & (8-1)? = 1998 & 7? = 6 (6 >= 4 為真(TRUE): 還需要附加的步驟) N = 6 & CEILING(5 / 2)? = 6 & 3? = 2 (2 >= 4 為假(FALSE): 記錄將被保存到#2分區(qū)中)
? ? ? 按照線性哈希分區(qū)的優(yōu)點在于增加刊棕、刪除、合并和拆分分區(qū)將變得更加快捷抖苦,有利于處理含有極其大量(1000吉)數(shù)據(jù)的表熊赖。它的缺點在于妖啥,與使用常規(guī)HASH分區(qū)得到的數(shù)據(jù)分布相比钉蒲,各個分區(qū)間數(shù)據(jù)的分布不大可能均衡浓利。
? ? ? 18.2.4. KEY分區(qū)
? ? ? 按照KEY進行分區(qū)類似于按照HASH分區(qū),除了HASH分區(qū)使用的用戶定義的表達式够挂,而KEY分區(qū)的 哈希函數(shù)是由MySQL 服務(wù)器提供。MySQL 簇(Cluster)使用函數(shù)MD5()來實現(xiàn)KEY分區(qū)藕夫;對于使用其他存儲引擎的表孽糖,服務(wù)器使用其自己內(nèi)部的 哈希函數(shù),這些函數(shù)是基于與PASSWORD()一樣的運算法則毅贮。
? ? ? “CREATE TABLE ... PARTITION BY KEY”的語法規(guī)則類似于創(chuàng)建一個通過HASH分區(qū)的表的規(guī)則办悟。它們唯一的區(qū)別在于使用的關(guān)鍵字是KEY而不是HASH,并且KEY分區(qū)只采用一個或多個列名的一個列表滩褥。
? ? ? 通過線性KEY分割一個表也是可能的病蛉。下面是一個簡單的例子:
CREATE TABLE tk (? ? col1 INT NOT NULL,? ? col2 CHAR(5),? ? col3 DATE) PARTITION BY LINEAR KEY (col1)PARTITIONS 3;
? ? ? 在KEY分區(qū)中使用關(guān)鍵字LINEAR和在HASH分區(qū)中使用具有同樣的作用,分區(qū)的編號是通過2的冪(powers-of-two)算法得到,而不是通過模數(shù)算法铺然。關(guān)于該算法及其蘊涵式的描述請參考 18.2.3.1節(jié)俗孝,“LINEAR HASH分區(qū)” 。
? ? ? 18.2.5. 子分區(qū)
? ? ? 子分區(qū)是分區(qū)表中每個分區(qū)的再次分割魄健。例如赋铝,考慮下面的CREATE TABLE 語句:
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個RANGE分區(qū)沽瘦。這3個分區(qū)中的每一個分區(qū)——p0, p1, 和 p2 ——又被進一步分成了2個子分區(qū)革骨。實際上,整個表被分成了3 * 2 = 6個分區(qū)析恋。但是良哲,由于PARTITION BY RANGE子句的作用,這些分區(qū)的頭2個只保存“purchased”列中值小于1990的那些記錄助隧。
? ? ? 在MySQL 5.1中筑凫,對于已經(jīng)通過RANGE或LIST分區(qū)了的表再進行子分區(qū)是可能的。子分區(qū)既可以使用HASH希分區(qū)喇颁,也可以使用KEY分區(qū)漏健。這也被稱為復(fù)合分區(qū)(composite partitioning)。
? ? ? 為了對個別的子分區(qū)指定選項橘霎,使用SUBPARTITION 子句來明確定義子分區(qū)也是可能的蔫浆。例如,創(chuàng)建在前面例子中給出的同一個表的姐叁、一個更加詳細的方式如下:
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? ? ? ? )? ? );
? ? ? 幾點要注意的語法項:
? ? ? ·? ? ? ? 每個分區(qū)必須有相同數(shù)量的子分區(qū)瓦盛。
? ? ? ·? ? ? ? 如果在一個分區(qū)表上的任何分區(qū)上使用SUBPARTITION 來明確定義任何子分區(qū),那么就必須定義所有的子分區(qū)外潜。換句話說原环,下面的語句將執(zhí)行失敗:
·? ? ? ? ? ? ? ? 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),·? ? ? ? ? ? ? ? ? ? ? ? PARTITION p2 VALUES LESS THAN MAXVALUE·? ? ? ? ? ? ? ? ? ? ? ? (·? ? ? ? ? ? ? ? ? ? ? ? ? ? SUBPARTITION s2,·? ? ? ? ? ? ? ? ? ? ? ? ? ? SUBPARTITION s3·? ? ? ? ? ? ? ? ? ? ? ? )·? ? ? ? ? ? ? ? ? ? )处窥;
? ? ? 即便這個語句包含了一個SUBPARTITIONS 2子句嘱吗,但是它仍然會執(zhí)行失敗。
? ? ? ·? ? ? ? 每個SUBPARTITION 子句必須包括 (至少)子分區(qū)的一個名字滔驾。否則谒麦,你可能要對該子分區(qū)設(shè)置任何你所需要的選項,或者允許該子分區(qū)對那些選項采用其默認的設(shè)置哆致。
? ? ? ·? ? ? ? 在每個分區(qū)內(nèi)绕德,子分區(qū)的名字必須是唯一的,但是在整個表中摊阀,沒有必要保持唯一耻蛇。例如踪蹬,下面的CREATE TABLE 語句是有效的:
·? ? ? ? ? ? ? ? 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 s0,·? ? ? ? ? ? ? ? ? ? ? ? ? ? SUBPARTITION s1·? ? ? ? ? ? ? ? ? ? ? ? ),·? ? ? ? ? ? ? ? ? ? ? ? PARTITION p2 VALUES LESS THAN MAXVALUE·? ? ? ? ? ? ? ? ? ? ? ? (·? ? ? ? ? ? ? ? ? ? ? ? ? ? SUBPARTITION s0,·? ? ? ? ? ? ? ? ? ? ? ? ? ? SUBPARTITION s1·? ? ? ? ? ? ? ? ? ? ? ? )·? ? ? ? ? ? ? ? ? ? );
? ? ? 子分區(qū)可以用于特別大的表臣咖,在多個磁盤間分配數(shù)據(jù)和索引跃捣。假設(shè)有6個磁盤,分別為/disk0亡哄, /disk1枝缔, /disk2等。現(xiàn)在考慮下面的例子:
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 s0? ? ? ? ? ? ? ? DATA DIRECTORY = '/disk2/data'? ? ? ? ? ? ? ? INDEX DIRECTORY = '/disk2/idx',? ? ? ? ? ? SUBPARTITION s1? ? ? ? ? ? ? ? DATA DIRECTORY = '/disk3/data'? ? ? ? ? ? ? ? INDEX DIRECTORY = '/disk3/idx'? ? ? ? ),? ? ? ? PARTITION p2 VALUES LESS THAN MAXVALUE? ? ? ? (? ? ? ? ? ? SUBPARTITION s0? ? ? ? ? ? ? ? DATA DIRECTORY = '/disk4/data'? ? ? ? ? ? ? ? INDEX DIRECTORY = '/disk4/idx',? ? ? ? ? ? SUBPARTITION s1? ? ? ? ? ? ? ? DATA DIRECTORY = '/disk5/data'? ? ? ? ? ? ? ? INDEX DIRECTORY = '/disk5/idx'? ? ? ? )? ? )蚊惯;
? ? ? 在這個例子中愿卸,每個RANGE分區(qū)的數(shù)據(jù)和索引都使用一個單獨的磁盤。還可能有許多其他的變化截型;下面是另外一個可能的例子:
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 s0a? ? ? ? ? ? ? ? DATA DIRECTORY = '/disk0'? ? ? ? ? ? ? ? INDEX DIRECTORY = '/disk1',? ? ? ? ? ? SUBPARTITION s0b? ? ? ? ? ? ? ? DATA DIRECTORY = '/disk2'? ? ? ? ? ? ? ? INDEX DIRECTORY = '/disk3'? ? ? ? ),? ? ? ? PARTITION p1 VALUES LESS THAN (2000)? ? ? ? (? ? ? ? ? ? SUBPARTITION s1a? ? ? ? ? ? ? ? DATA DIRECTORY = '/disk4/data'? ? ? ? ? ? ? ? INDEX DIRECTORY = '/disk4/idx',? ? ? ? ? ? SUBPARTITION s1b? ? ? ? ? ? ? ? DATA DIRECTORY = '/disk5/data'? ? ? ? ? ? ? ? INDEX DIRECTORY = '/disk5/idx'? ? ? ? ),? ? ? ? PARTITION p2 VALUES LESS THAN MAXVALUE? ? ? ? (? ? ? ? ? ? SUBPARTITION s2a,? ? ? ? ? ? SUBPARTITION s2b? ? ? ? )? ? )趴荸;
? ? ? 在這個例子中,存儲的分配如下:
? ? ? ·? ? ? ? 購買日期在1990年前的記錄占了大量的存儲空間宦焦,所以把它分為了四個部分進行存儲发钝,組成p0分區(qū)的兩個子分區(qū)(s0a 和s0b)的數(shù)據(jù)和索引都分別用一個單獨的磁盤進行存儲。換句話說:
? ? ? o? ? ? ? 子分區(qū)s0a 的數(shù)據(jù)保存在磁盤/disk0中波闹。
? ? ? o? ? ? ? 子分區(qū)s0a 的索引保存在磁盤/disk1中酝豪。
? ? ? o? ? ? ? 子分區(qū)s0b 的數(shù)據(jù)保存在磁盤/disk2中。
? ? ? o? ? ? ? 子分區(qū)s0b 的索引保存在磁盤/disk3中精堕。
? ? ? ·? ? ? ? 保存購買日期從1990年到1999年間的記錄(分區(qū)p1)不需要保存購買日期在1990年之前的記錄那么大的存儲空間孵淘。這些記錄分在2個磁盤(/disk4和/disk5)上保存,而不是4個磁盤:
? ? ? o? ? ? ? 屬于分區(qū)p1的第一個子分區(qū)(s1a)的數(shù)據(jù)和索引保存在磁盤/disk4上 — 其中數(shù)據(jù)保存在路徑/disk4/data下歹篓,索引保存在/disk4/idx下瘫证。
? ? ? o? ? ? ? 屬于分區(qū)p1的第二個子分區(qū)(s1b)的數(shù)據(jù)和索引保存在磁盤/disk5上 — 其中數(shù)據(jù)保存在路徑/disk5/data下,索引保存在/disk5/idx下庄撮。
? ? ? ·? ? ? ? 保存購買日期從2000年到現(xiàn)在的記錄(分區(qū)p2)不需要前面兩個RANGE分區(qū)那么大的空間背捌。當(dāng)前,在默認的位置能夠足夠保存所有這些記錄洞斯。
? ? ? 將來毡庆,如果從2000年開始后十年購買的數(shù)量已經(jīng)達到了默認的位置不能夠提供足夠的保存空間時,相應(yīng)的記錄(行)可以通過使用“ALTER TABLE ... REORGANIZE PARTITION”語句移動到其他的位置烙如。關(guān)于如何實現(xiàn)的說明么抗,請參見18.3節(jié),“分區(qū)管理” 厅翔。
? ? ? 18.2.6. MySQL分區(qū)處理NULL值的方式
? ? ? MySQL 中的分區(qū)在禁止空值(NULL)上沒有進行處理,無論它是一個列值還是一個用戶定義表達式的值搀突。一般而言刀闷,在這種情況下MySQL 把NULL視為0。如果你希望回避這種做法,你應(yīng)該在設(shè)計表時不允許空值甸昏;最可能的方法是顽分,通過聲明列“NOT NULL”來實現(xiàn)這一點。
? ? ? 在本節(jié)中施蜜,我們提供了一些例子卒蘸,來說明當(dāng)決定一個行應(yīng)該保存到哪個分區(qū)時,MySQL 是如何處理NULL值的翻默。
? ? ? 如果插入一行到按照RANGE或LIST分區(qū)的表缸沃,該行用來確定分區(qū)的列值為NULL,分區(qū)將把該NULL值視為0修械。例如趾牧,考慮下面的兩個表,表的創(chuàng)建和插入記錄如下:
mysql> CREATE TABLE tnlist (? ? ->? ? id INT,? ? ->? ? name VARCHAR(5)? ? -> )? ? -> PARTITION BY LIST(id) (? ? ->? ? PARTITION p1 VALUES IN (0),? ? ->? ? PARTITION p2 VALUES IN (1)? ? -> );Query OK, 0 rows affected (0.09 sec) mysql> CREATE TABLE tnrange (? ? ->? ? id INT,? ? ->? ? name VARCHAR(5)? ? -> )? ? -> PARTITION BY RANGE(id) (? ? ->? ? PARTITION p1 VALUES LESS THAN (1),? ? ->? ? PARTITION p2 VALUES LESS THAN MAXVALUE? ? -> );Query OK, 0 rows affected (0.09 sec) mysql> INSERT INTO tnlist VALUES (NULL, 'bob');Query OK, 1 row affected (0.00 sec) mysql> INSERT INTO tnrange VALUES (NULL, 'jim');Query OK, 1 row affected (0.00 sec) mysql> SELECT * FROM tnlist;+------+------+| id? | name |+------+------+| NULL | bob? |+------+------+1 row in set (0.00 sec) mysql> SELECT * FROM tnrange;+------+------+| id? | name |+------+------+| NULL | jim? |+------+------+1 row in set (0.00 sec)
? ? ? 在兩個表中肯污,id列沒有聲明為“NOT NULL”翘单,這意味著它們允許Null值”脑可以通過刪除這些分區(qū)哄芜,然后重新運行SELECT 語句,來驗證這些行被保存在每個表的p1分區(qū)中:
mysql> ALTER TABLE tnlist DROP PARTITION p1;Query OK, 0 rows affected (0.16 sec) mysql> ALTER TABLE tnrange DROP PARTITION p1;Query OK, 0 rows affected (0.16 sec) mysql> SELECT * FROM tnlist;Empty set (0.00 sec) mysql> SELECT * FROM tnrange;Empty set (0.00 sec)
? ? ? 在按HASH和KEY分區(qū)的情況下柬唯,任何產(chǎn)生NULL值的表達式都視同好像它的返回值為0认臊。我們可以通過先創(chuàng)建一個按HASH分區(qū)的表,然后插入一個包含有適當(dāng)值的記錄权逗,再檢查對文件系統(tǒng)的作用美尸,來驗證這一點。假定有使用下面的語句在測試數(shù)據(jù)庫中創(chuàng)建了一個表tnhash:
CREATE TABLE tnhash (? ? id INT,? ? name VARCHAR(5))PARTITION BY HASH(id)PARTITIONS 2斟薇;
? ? ? 假如Linux 上的MySQL 的一個RPM安裝师坎,這個語句在目錄/var/lib/mysql/test下創(chuàng)建了兩個.MYD文件,這兩個文件可以在bash shell中查看堪滨,結(jié)果如下:
/var/lib/mysql/test> ls *.MYD -l-rw-rw----? 1 mysql mysql 0 2005-11-04 18:41 tnhash_p0.MYD-rw-rw----? 1 mysql mysql 0 2005-11-04 18:41 tnhash_p1.MYD
? ? ? 注意:每個文件的大小為0字節(jié)】杪現(xiàn)在在表tnhash 中插入一行id列值為NULL的行,然后驗證該行已經(jīng)被插入:
mysql> INSERT INTO tnhash VALUES (NULL, 'sam');Query OK, 1 row affected (0.00 sec) mysql> SELECT * FROM tnhash;+------+------+| id? | name |+------+------+| NULL | sam? |+------+------+1 row in set (0.01 sec)
? ? ? 回想一下袱箱,對于任意的整數(shù)N,NULL MOD N 的值總是等于NULL遏乔。這個結(jié)果在確定正確的分區(qū)方面被認為是0》⒈剩回到系統(tǒng)shell(仍然假定bash用于這個目的) 盟萨,通過再次列出數(shù)據(jù)文件,可以看出值被成功地插入到第一個分區(qū)(默認名稱為p0)中:
var/lib/mysql/test> ls *.MYD -l-rw-rw----? 1 mysql mysql 20 2005-11-04 18:44 tnhash_p0.MYD-rw-rw----? 1 mysql mysql? 0 2005-11-04 18:41 tnhash_p1.MYD
? ? ? 可以看出INSERT語句只修改了文件tnhash_p0.MYD了讨,它在磁盤上的尺寸增加了捻激,而沒有影響其他的文件制轰。
? ? ? 假定有下面的一個表:
CREATE TABLE tndate (? ? id INT,? ? dt DATE)PARTITION BY RANGE( YEAR(dt) ) (? ? PARTITION p0 VALUES LESS THAN (1990),? ? PARTITION p1 VALUES LESS THAN (2000),? ? PARTITION p2 VALUES LESS THAN MAXVALUE);
? ? ? 像其他的MySQL函數(shù)一樣胞谭,YEAR(NULL)返回NULL值垃杖。一個dt列值為NULL的行,其分區(qū)表達式的計算結(jié)果被視為0丈屹,該行被插入到分區(qū)p0中调俘。
18.3. 分區(qū)管理
18.3.1. RANGE和LIST分區(qū)的管理
18.3.2. HASH和KEY分區(qū)的管理
18.3.3. 分區(qū)維護
18.3.4. 獲取關(guān)于分區(qū)的信息
? ? ? MySQL 5.1 提供了許多修改分區(qū)表的方式。添加旺垒、刪除彩库、重新定義、合并或拆分已經(jīng)存在的分區(qū)是可能的袖牙。所有這些操作都可以通過使用ALTER TABLE 命令的分區(qū)擴展來實現(xiàn)(關(guān)于語法的定義侧巨,請參見13.1.2節(jié),“ALTER TABLE語法” )鞭达。也有獲得分區(qū)表和分區(qū)信息的方式司忱。在本節(jié),我們討論下面這些主題:
? ? ? ·? ? ? ? 按RANGE或LIST分區(qū)的表的分區(qū)管理的有關(guān)信息畴蹭,請參見18.3.1節(jié)坦仍,“RANGE和LIST分區(qū)的管理”。
? ? ? ·? ? ? ? 關(guān)于HASH和KEY分區(qū)管理的討論叨襟,請參見18.3.2節(jié)繁扎,“HASH和KEY分區(qū)的管理”。
? ? ? ·? ? ? ? MySQL 5.1中提供的糊闽、獲得關(guān)于分區(qū)表和分區(qū)信息的機制的討論梳玫,請參見18.3.4節(jié),“獲取關(guān)于分區(qū)的信息” 右犹。
? ? ? ·? ? ? ? 關(guān)于執(zhí)行分區(qū)維護操作的討論提澎,請參見18.3.3節(jié),“分區(qū)維護”念链。
? ? ? 注釋:在MySQL 5.1中盼忌,一個分區(qū)表的所有分區(qū)都必須有子分區(qū)同樣的名字,并且一旦表已經(jīng)創(chuàng)建掂墓,再改變子分區(qū)是不可能的谦纱。
? ? ? 要點:當(dāng)前,從5.1系列起建立的MySQL 服務(wù)器就把“ALTER TABLE ... PARTITION BY ...”作為有效的語法君编,但是這個語句目前還不起作用跨嘉。我們期望MySQL 5.1達到生產(chǎn)狀態(tài)時,能夠按照下面的描述實現(xiàn)該語句的功能吃嘿。
? ? ? 要改變一個表的分區(qū)模式祠乃,只需要使用帶有一個“partition_options”子句的ALTER TABLE 的命令窘游。這個子句和與創(chuàng)建一個分區(qū)表的CREATE TABLE命令一同使用的子句有相同的語法,并且總是以關(guān)鍵字PARTITION BY 開頭跳纳。例如,假設(shè)有一個使用下面CREATE TABLE語句建立的按照RANGE分區(qū)的表:
CREATE TABLE trb3 (id INT, name VARCHAR(50), purchased DATE)? ? PARTITION BY RANGE(YEAR(purchased))? ? (? ? ? ? PARTITION p0 VALUES LESS THAN (1990),? ? ? ? PARTITION p1 VALUES LESS THAN (1995),? ? ? ? PARTITION p2 VALUES LESS THAN (2000),? ? ? ? PARTITION p3 VALUES LESS THAN (2005)? ? )贪嫂;
? ? ? 現(xiàn)在寺庄,要把這個表按照使用id列值作為鍵的基礎(chǔ),通過KEY分區(qū)把它重新分成兩個分區(qū)力崇,可以使用下面的語句:
ALTER TABLE trb3 PARTITION BY KEY(id) PARTITIONS 2斗塘;
? ? ? 這和先刪除這個表、然后使用“CREATE TABLE trb3 PARTITION BY KEY(id) PARTITIONS 2亮靴;”重新創(chuàng)建這個表具有同樣的效果馍盟。
? ? ? 18.3.1. RANGE和LIST分區(qū)的管理
? ? ? 關(guān)于如何添加和刪除分區(qū)的處理,RANGE和LIST分區(qū)非常相似茧吊≌炅耄基于這個原因,我們在本節(jié)討論這兩種分區(qū)的管理搓侄。關(guān)于HASH和KEY分區(qū)管理的信息瞄桨,請參見18.3.2節(jié),“HASH和KEY分區(qū)的管理”讶踪。刪除一個RANGE或LIST分區(qū)比增加一個分區(qū)要更加簡單易懂芯侥,所以我們先討論前者。
? ? ? 從一個按照RANGE或LIST分區(qū)的表中刪除一個分區(qū)乳讥,可以使用帶一個DROP PARTITION子句的ALTER TABLE命令來實現(xiàn)柱查。這里有一個非常基本的例子云石,假設(shè)已經(jīng)使用下面的CREATE TABLE和INSERT語句創(chuàng)建了一個按照RANGE分區(qū)的表唉工,并且已經(jīng)插入了10條記錄:
mysql> CREATE TABLE tr (id INT, name VARCHAR(50), purchased DATE)? ? ->? ? PARTITION BY RANGE(YEAR(purchased))? ? ->? ? (? ? ->? ? ? ? PARTITION p0 VALUES LESS THAN (1990),? ? ->? ? ? ? PARTITION p1 VALUES LESS THAN (1995),? ? ->? ? ? ? PARTITION p2 VALUES LESS THAN (2000),? ? ->? ? ? ? PARTITION p3 VALUES LESS THAN (2005)? ? ->? ? );Query OK, 0 rows affected (0.01 sec) mysql> INSERT INTO tr VALUES? ? ->? ? (1, 'desk organiser', '2003-10-15'),? ? ->? ? (2, 'CD player', '1993-11-05'),? ? ->? ? (3, 'TV set', '1996-03-10'),? ? ->? ? (4, 'bookcase', '1982-01-10'),? ? ->? ? (5, 'exercise bike', '2004-05-09'),? ? ->? ? (6, 'sofa', '1987-06-05'),? ? ->? ? (7, 'popcorn maker', '2001-11-22'),? ? ->? ? (8, 'aquarium', '1992-08-04'),? ? ->? ? (9, 'study desk', '1984-09-16'),? ? ->? ? (10, 'lava lamp', '1998-12-25');Query OK, 10 rows affected (0.01 sec)? ? ? ? ? ? ? ?
? ? ? 可以通過使用下面的命令查看那些記錄已經(jīng)插入到了分區(qū)p2中:
mysql> SELECT * FROM tr? ? -> WHERE purchased BETWEEN '1995-01-01' AND '1999-12-31';+------+-----------+------------+| id? | name? ? ? | purchased? |+------+-----------+------------+|? ? 3 | TV set? ? | 1996-03-10 ||? 10 | lava lamp | 1998-12-25 |+------+-----------+------------+2 rows in set (0.00 sec)
? ? ? 要刪除名字為p2的分區(qū),執(zhí)行下面的命令:
mysql> ALTER TABLE tr DROP PARTITION p2;Query OK, 0 rows affected (0.03 sec)
? ? ? 記住下面一點非常重要:當(dāng)刪除了一個分區(qū)留晚,也同時刪除了該分區(qū)中所有的數(shù)據(jù)酵紫。可以通過重新運行前面的SELECT查詢來驗證這一點:
mysql> SELECT * FROM tr WHERE purchased? ? -> BETWEEN '1995-01-01' AND '1999-12-31';Empty set (0.00 sec)
? ? ? 如果希望從所有分區(qū)刪除所有的數(shù)據(jù)错维,但是又保留表的定義和表的分區(qū)模式奖地,使用TRUNCATE TABLE命令。(請參見13.2.9節(jié)赋焕,“TRUNCATE語法”)参歹。
? ? ? 如果希望改變表的分區(qū)而又不丟失數(shù)據(jù),使用“ALTER TABLE ... REORGANIZE PARTITION”語句隆判。參見下面的內(nèi)容犬庇,或者在13.1.2節(jié)僧界,“ALTER TABLE語法” 中參考關(guān)于REORGANIZE PARTITION的信息。
? ? ? 如果現(xiàn)在執(zhí)行一個SHOW CREATE TABLE命令臭挽,可以觀察到表的分區(qū)結(jié)構(gòu)是如何被改變的:
mysql> SHOW CREATE TABLE tr\G*************************** 1. row ***************************? ? ? Table: trCreate Table: CREATE TABLE `tr` (? `id` int(11) default NULL,? `name` varchar(50) default NULL,? `purchased` date default NULL) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY RANGE (YEAR(purchased)) (? PARTITION p0 VALUES LESS THAN (1990) ENGINE = MyISAM,? PARTITION p1 VALUES LESS THAN (1995) ENGINE = MyISAM,? PARTITION p3 VALUES LESS THAN (2005) ENGINE = MyISAM)1 row in set (0.01 sec)
? ? ? 如果插入購買日期列的值在'1995-01-01'和 '2004-12-31'之間(含)的新行到已經(jīng)修改后的表中時捂襟,這些行將被保存在分區(qū)p3中』斗澹可以通過下面的方式來驗證這一點:
mysql> INSERT INTO tr VALUES (11, 'pencil holder', '1995-07-12');Query OK, 1 row affected (0.00 sec) mysql> SELECT * FROM tr WHERE purchased? ? -> BETWEEN '1995-01-01' AND '2004-12-31';+------+----------------+------------+| id? | name? ? ? ? ? | purchased? |+------+----------------+------------+|? 11 | pencil holder? | 1995-07-12 ||? ? 1 | desk organiser | 2003-10-15 ||? ? 5 | exercise bike? | 2004-05-09 ||? ? 7 | popcorn maker? | 2001-11-22 |+------+----------------+------------+4 rows in set (0.00 sec) mysql> ALTER TABLE tr DROP PARTITION p3;Query OK, 0 rows affected (0.03 sec) mysql> SELECT * FROM tr WHERE purchased? ? -> BETWEEN '1995-01-01' AND '2004-12-31';Empty set (0.00 sec)
? ? ? 注意:由“ALTER TABLE ... DROP PARTITION”語句引起的葬荷、從表中刪除的行數(shù)并沒有被服務(wù)器報告出來,就好像通過同等的DELETE查詢操作一樣纽帖。
? ? ? 刪除LIST分區(qū)使用和刪除RANGE分區(qū)完全相同的“ALTER TABLE ... DROP PARTITION”語法宠漩。但是,在對其后使用這個表的影響方面懊直,還是有重大的區(qū)別:在這個表中扒吁,再也不能插入這么一些行,這些行的列值包含在定義已經(jīng)刪除了的分區(qū)的值列表中 (有關(guān)示例室囊,請參見18.2.2節(jié)雕崩,“LIST分區(qū)” )。
? ? ? 要增加一個新的RANGE或LIST分區(qū)到一個前面已經(jīng)分區(qū)了的表融撞,使用“ALTER TABLE ... ADD PARTITION”語句晨逝。對于使用RANGE分區(qū)的表,可以用這個語句添加新的區(qū)間到已有分區(qū)的序列的前面或后面懦铺。例如捉貌,假設(shè)有一個包含你所在組織的全體成員數(shù)據(jù)的分區(qū)表,該表的定義如下:
CREATE TABLE members (? ? id INT,? ? fname VARCHAR(25),? ? lname VARCHAR(25),? ? dob DATE)PARTITION BY RANGE(YEAR(dob)) (? ? PARTITION p0 VALUES LESS THAN (1970),? ? PARTITION p1 VALUES LESS THAN (1980),? ? PARTITION p2 VALUES LESS THAN (1990));
? ? ? 進一步假設(shè)成員的最小年紀是16歲冬念。隨著日歷接近2005年年底趁窃,你會認識到不久將要接納1990年(以及以后年份)出生的成員〖鼻埃可以按照下面的方式醒陆,修改成員表來容納出生在1990-1999年之間的成員:
ALTER TABLE ADD PARTITION (PARTITION p3 VALUES LESS THAN (2000));
? ? ? 要點:對于通過RANGE分區(qū)的表,只可以使用ADD PARTITION添加新的分區(qū)到分區(qū)列表的高端裆针。設(shè)法通過這種方式在現(xiàn)有分區(qū)的前面或之間增加一個新的分區(qū)刨摩,將會導(dǎo)致下面的一個錯誤:
mysql> ALTER TABLE members ADD PARTITION (PARTITION p3 VALUES LESS THAN (1960));錯誤1463 (HY000): 對每個分區(qū),VALUES LESS THAN 值必須嚴格增長
? ? ? 采用一個類似的方式世吨,可以增加新的分區(qū)到已經(jīng)通過LIST分區(qū)的表澡刹。例如,假定有如下定義的一個表:
CREATE TABLE tt (? ? id INT,? ? data INT)PARTITION BY LIST(data) (? ? PARTITION p0 VALUES IN (5, 10, 15),? ? PARTITION p1 VALUES IN (6, 12, 18))耘婚;
? ? ? 可以通過下面的方法添加一個新的分區(qū)罢浇,用來保存擁有數(shù)據(jù)列值7,14和21的行:
ALTER TABLE tt ADD PARTITION (PARTITION p2 VALUES IN (7, 14, 21));
? ? ? 注意:不能添加這樣一個新的LIST分區(qū)嚷闭,該分區(qū)包含有已經(jīng)包含在現(xiàn)有分區(qū)值列表中的任意值攒岛。如果試圖這樣做,將會導(dǎo)致錯誤:
mysql> ALTER TABLE tt ADD PARTITION (PARTITION np VALUES IN (4, 8, 12));錯誤1465 (HY000): 在LIST分區(qū)中胞锰,同一個常數(shù)的多次定義
? ? ? 因為帶有數(shù)據(jù)列值12的任何行都已經(jīng)分配給了分區(qū)p1灾锯,所以不能在表tt上再創(chuàng)建一個其值列表包括12的新分區(qū)。為了實現(xiàn)這一點嗅榕,可以先刪除分區(qū)p1挠进,添加分區(qū)np,然后使用修正后的定義添加一個新的分區(qū)p1誊册。但是,正如我們前面討論過的暖璧,這將導(dǎo)致保存在分區(qū)p1中的所有數(shù)據(jù)丟失——而這往往并不是你所真正想要做的案怯。另外一種解決方法可能是,建立一個帶有新分區(qū)的表的副本澎办,然后使用“CREATE TABLE ... SELECT ...”把數(shù)據(jù)拷貝到該新表中嘲碱,然后刪除舊表,重新命名新表局蚀,但是麦锯,當(dāng)需要處理大量的數(shù)據(jù)時,這可能是非常耗時的琅绅。在需要高可用性的場合扶欣,這也可能是不可行的。
? ? ? 幸運地是千扶,MySQL 的分區(qū)實現(xiàn)提供了在不丟失數(shù)據(jù)的條件下重新定義分區(qū)的方式料祠。讓我們首先看兩個涉及到RANGE分區(qū)的簡單例子∨煨撸回想一下現(xiàn)在定義如下的成員表:
mysql> SHOW CREATE TABLE members\G*************************** 1. row ***************************? ? ? Table: membersCreate Table: CREATE TABLE `members` (? `id` int(11) default NULL,? `fname` varchar(25) default NULL,? `lname` varchar(25) default NULL,? `dob` date default NULL) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY RANGE (YEAR(dob)) (? PARTITION p0 VALUES LESS THAN (1970) ENGINE = MyISAM,? PARTITION p1 VALUES LESS THAN (1980) ENGINE = MyISAM,? PARTITION p2 VALUES LESS THAN (1990) ENGINE = MyISAM.? PARTITION p3 VALUES LESS THAN (2000) ENGINE = MyISAM)
? ? ? 假定想要把表示出生在1960年前成員的所有行移入到一個分開的分區(qū)中髓绽。正如我們前面看到的,不能通過使用“ALTER TABLE ... ADD PARTITION”來實現(xiàn)這一點妆绞。但是顺呕,要實現(xiàn)這一點,可以使用ALTER TABLE上的另外一個與分區(qū)有關(guān)的擴展括饶,具體實現(xiàn)如下:
ALTER TABLE members REORGANIZE PARTITION p0 INTO (? ? PARTITION s0 VALUES LESS THAN (1960),? ? PARTITION s1 VALUES LESS THAN (1970))株茶;
? ? ? 實際上,這個命令把分區(qū)p0分成了兩個新的分區(qū)s0和s1图焰。同時忌卤,它還根據(jù)包含在兩個“PARTITION ... VALUES ...”子句中的規(guī)則,把保存在分區(qū)p0中的數(shù)據(jù)移入到兩個新的分區(qū)中楞泼,所以分區(qū)s0中只包含YEAR(dob)小于1960的那些行驰徊,s1中包含那些YEAR(dob)大于或等于1960但是小于1970的行笤闯。
? ? ? 一個REORGANIZE PARTITION語句也可以用來合并相鄰的分區(qū)」鞒В可以使用如下的語句恢復(fù)成員表到它以前的分區(qū):
ALTER TABLE members REORGANIZE PARTITION s0,s1 INTO (? ? PARTITION p0 VALUES LESS THAN (1970))颗味;
? ? ? 使用“REORGANIZE PARTITION”拆分或合并分區(qū),沒有數(shù)據(jù)丟失牺弹。在執(zhí)行上面的語句中浦马,MySQL 把保存在分區(qū)s0和s1中的所有數(shù)據(jù)都移到分區(qū)p0中。
? ? ? “REORGANIZE PARTITION”的基本語法是:
ALTER TABLE tbl_name REORGANIZE PARTITION partition_list INTO (partition_definitions)张漂;
? ? ? 其中晶默,tbl_name 是分區(qū)表的名稱,partition_list 是通過逗號分開的航攒、一個或多個將要被改變的現(xiàn)有分區(qū)的列表磺陡。partition_definitions 是一個是通過逗號分開的、新分區(qū)定義的列表漠畜,它遵循與用在“CREATE TABLE”中的partition_definitions 相同的規(guī)則 (請參見13.1.5節(jié)币他,“CREATE TABLE語法”)。應(yīng)當(dāng)注意到憔狞,在把多少個分區(qū)合并到一個分區(qū)或把一個分區(qū)拆分成多少個分區(qū)方面蝴悉,沒有限制。例如瘾敢,可以重新組織成員表的四個分區(qū)成兩個分區(qū)拍冠,具體實現(xiàn)如下:
ALTER TABLE members REORGANIZE PARTITION p0,p1,p2,p3 INTO (? ? PARTITION m0 VALUES LESS THAN (1980),? ? PARTITION m1 VALUES LESS THAN (2000));
? ? ? 同樣簇抵,對于按LIST分區(qū)的表倦微,也可以使用REORGANIZE PARTITION。讓我們回到那個問題正压,即增加一個新的分區(qū)到已經(jīng)按照LIST分區(qū)的表tt中欣福,但是因為該新分區(qū)有一個值已經(jīng)存在于現(xiàn)有分區(qū)的值列表中,添加新的分區(qū)失敗焦履。我們可以通過先添加只包含非沖突值的分區(qū)拓劝,然后重新組織該新分區(qū)和現(xiàn)有的那個分區(qū),以便保存在現(xiàn)有的那個分區(qū)中的值現(xiàn)在移到了新的分區(qū)中嘉裤,來處理這個問題:
ALTER TABLE tt ADD PARTITION (PARTITION np VALUES IN (4, 8));ALTER TABLE tt REORGANIZE PARTITION p1,np INTO (? ? PARTITION p1 VALUES IN (6, 18),? ? PARTITION np VALUES in (4, 8, 12))郑临;
? ? ? 當(dāng)使用“ALTER TABLE ... REORGANIZE PARTITION”來對已經(jīng)按照RANGE和LIST分區(qū)表進行重新分區(qū)時,下面是一些要記住的關(guān)鍵點:
? ? ? ·? ? ? ? 用來確定新分區(qū)模式的PARTITION子句使用與用在CREATE TABLE中確定分區(qū)模式的PARTITION子句相同的規(guī)則屑宠。
? ? ? 最重要的是厢洞,應(yīng)該記住:新分區(qū)模式不能有任何重疊的區(qū)間(適用于按照RANGE分區(qū)的表)或值集合(適用于重新組織按照LIST分區(qū)的表)。
? ? ? ·? ? ? ? partition_definitions 列表中分區(qū)的合集應(yīng)該與在partition_list 中命名分區(qū)的合集占有相同的區(qū)間或值集合躺翻。
? ? ? 例如丧叽,在本節(jié)中用作例子的成員表中,分區(qū)p1和p2總共覆蓋了1980到1999的這些年公你。因此踊淳,對這兩個分區(qū)的重新組織都應(yīng)該覆蓋相同范圍的年份。
? ? ? ·? ? ? ? 對于按照RANGE分區(qū)的表陕靠,只能重新組織相鄰的分區(qū)迂尝;不能跳過RANGE分區(qū)。
? ? ? 例如剪芥,不能使用以“ALTER TABLE members REORGANIZE PARTITION p0,p2 INTO ...”開頭的語句垄开,來重新組織本節(jié)中用作例子的成員表。因為税肪,p0覆蓋了1970年以前的年份溉躲,而p2覆蓋了從1990到1999(包括1990和1999)之間的年份,因而這兩個分區(qū)不是相鄰的分區(qū)寸认。
? ? ? ·? ? ? ? 不能使用REORGANIZE PARTITION來改變表的分區(qū)類型;也就是說串慰,例如偏塞,不能把RANGE分區(qū)變?yōu)镠ASH分區(qū),反之亦然邦鲫。也不能使用該命令來改變分區(qū)表達式或列灸叼。如果想在不刪除和重建表的條件下實現(xiàn)這兩個任務(wù),可以使用“ALTER TABLE ... PARTITION BY ....”庆捺,例如:
·? ? ? ? ? ? ? ? ALTER TABLE members ·? ? ? ? ? ? ? ? ? ? PARTITION BY HASH(YEAR(dob))·? ? ? ? ? ? ? ? ? ? PARTITIONS 8古今;
? ? ? 注釋:在MySQL 5.1發(fā)布前的版本中,“ALTER TABLE ... PARTITION BY ...”還沒有實現(xiàn)滔以。作為替代捉腥,要么使用先刪除表,然后使用想要的分區(qū)重建表你画,或者——如果需要保留已經(jīng)存儲在表中的數(shù)據(jù)——可以使用“CREATE TABLE ... SELECT ...”來創(chuàng)建新的表抵碟,然后從舊表中把數(shù)據(jù)拷貝到新表中,再刪除舊表坏匪,如有必要拟逮,最后重新命名新表。
? ? ? 18.3.2. HASH和KEY分區(qū)的管理
? ? ? 在改變分區(qū)設(shè)置方面适滓,按照HASH分區(qū)或KEY分區(qū)的表彼此非常相似敦迄,但是它們又與按照RANGE或LIST分區(qū)的表在很多方面有差別。所以,本節(jié)只討論按照HASH或KEY分區(qū)表的修改罚屋。關(guān)于添加和刪除按照RANGE或LIST進行分區(qū)的表的分區(qū)的討論苦囱,參見18.3.1節(jié),“RANGE和LIST分區(qū)的管理”沿后。
? ? ? 不能使用與從按照RANGE或LIST分區(qū)的表中刪除分區(qū)相同的方式沿彭,來從HASH或KEY分區(qū)的表中刪除分區(qū)。但是尖滚,可以使用“ALTER TABLE ... COALESCE PARTITION”命令來合并HASH或KEY分區(qū)喉刘。例如,假定有一個包含顧客信息數(shù)據(jù)的表漆弄,它被分成了12個分區(qū)睦裳。該顧客表的定義如下:
CREATE TABLE clients (? ? id INT,? ? fname VARCHAR(30),? ? lname VARCHAR(30),? ? signed DATE)PARTITION BY HASH( MONTH(signed) )PARTITIONS 12;
? ? ? 要減少分區(qū)的數(shù)量從12到6撼唾,執(zhí)行下面的ALTER TABLE命令:
mysql> ALTER TABLE clients COALESCE PARTITION 6廉邑;Query OK, 0 rows affected (0.02 sec)
? ? ? 對于按照HASH催享,KEY滨达,LINEAR HASH,或LINEAR KEY分區(qū)的表寸士, COALESCE能起到同樣的作用渤愁。下面是一個類似于前面例子的另外一個例子牵祟,它們的區(qū)別只是在于表是按照LINEAR KEY 進行分區(qū):
mysql> CREATE TABLE clients_lk (? ? ->? ? id INT,? ? ->? ? fname VARCHAR(30),? ? ->? ? lname VARCHAR(30),? ? ->? ? signed DATE? ? -> )? ? -> PARTITION BY LINEAR KEY(signed)? ? -> PARTITIONS 12;Query OK, 0 rows affected (0.03 sec) mysql> ALTER TABLE clients_lk COALESCE PARTITION 6抖格;Query OK, 0 rows affected (0.06 sec)Records: 0? Duplicates: 0? Warnings: 0
? ? ? COALESCE不能用來增加分區(qū)的數(shù)量诺苹,如果你嘗試這么做,結(jié)果會出現(xiàn)類似于下面的錯誤:
mysql> ALTER TABLE clients COALESCE PARTITION 18;錯誤1478 (HY000): 不能移動所有分區(qū)雹拄,使用DROP TABLE代替
? ? ? 要增加顧客表的分區(qū)數(shù)量從12到18收奔,使用“ALTER TABLE ... ADD PARTITION”,具體如下:
ALTER TABLE clients ADD PARTITION PARTITIONS 18;
? ? ? 注釋:“ALTER TABLE ... REORGANIZE PARTITION”不能用于按照HASH或HASH分區(qū)的表滓玖。
? ? ? 18.3.3. 分區(qū)維護
? ? ? 注釋:實際上坪哄,本節(jié)討論的命令還沒有在MySQL 5.1中實現(xiàn), 在這里提出的目的势篡,是為了在5.1版投產(chǎn)前的開發(fā)周期期間损姜,引出來自用戶測試該軟件的反饋意見。(換句話說殊霞,就是“請不要反饋這樣的缺陷摧阅,說這些命令不起作用”)。隨著MySQL5.1版開發(fā)的繼續(xù)绷蹲,這些信息很有可能發(fā)生變化棒卷。隨著分區(qū)功能的實現(xiàn)和提高顾孽,我們將更新本節(jié)的內(nèi)容。
? ? ? MySQL 5.1中可以執(zhí)行許多分區(qū)維護的任務(wù)比规。對于分區(qū)表若厚,MySQL不支持命令CHECK TABLE,OPTIMIZE TABLE蜒什,ANALYZE TABLE测秸,或REPAIR TABLE。作為替代灾常,可以使用ALTER TABLE 的許多擴展來在一個或多個分區(qū)上直接地執(zhí)行這些操作霎冯,如下面列出的那樣:
? ? ? ·? ? ? ? 重建分區(qū): 這和先刪除保存在分區(qū)中的所有記錄,然后重新插入它們钞瀑,具有同樣的效果沈撞。它可用于整理分區(qū)碎片。
? ? ? 示例:
ALTER TABLE t1 REBUILD PARTITION (p0, p1)雕什;
? ? ? ·? ? ? ? 優(yōu)化分區(qū):如果從分區(qū)中刪除了大量的行缠俺,或者對一個帶有可變長度的行(也就是說,有VARCHAR贷岸,BLOB壹士,或TEXT類型的列)作了許多修改,可以使用“ALTER TABLE ... OPTIMIZE PARTITION”來收回沒有使用的空間偿警,并整理分區(qū)數(shù)據(jù)文件的碎片躏救。
? ? ? 示例:
ALTER TABLE t1 OPTIMIZE PARTITION (p0, p1);
? ? ? 在一個給定的分區(qū)表上使用“OPTIMIZE PARTITION”等同于在那個分區(qū)上運行CHECK PARTITION户敬,ANALYZE PARTITION落剪,和REPAIR PARTITION睁本。
? ? ? ·? ? ? ? 分析分區(qū):讀取并保存分區(qū)的鍵分布尿庐。
? ? ? 示例:
ALTER TABLE t1 ANALYZE PARTITION (p3);
? ? ? ·? ? ? ? 修補分區(qū): 修補被破壞的分區(qū)呢堰。
? ? ? 示例:
ALTER TABLE t1 REPAIR PARTITION (p0,p1);
? ? ? ·? ? ? ? 檢查分區(qū): 可以使用幾乎與對非分區(qū)表使用CHECK TABLE 相同的方式檢查分區(qū)抄瑟。
? ? ? 示例:
ALTER TABLE trb3 CHECK PARTITION (p1);
? ? ? 這個命令可以告訴你表t1的分區(qū)p1中的數(shù)據(jù)或索引是否已經(jīng)被破壞枉疼。如果發(fā)生了這種情況皮假,使用“ALTER TABLE ... REPAIR PARTITION”來修補該分區(qū)。
? ? ? 還可以使用mysqlcheck或myisamchk 應(yīng)用程序骂维,在對表進行分區(qū)時所產(chǎn)生的惹资、單獨的MYI文件上進行操作,來完成這些任務(wù)航闺。請參見8.7節(jié)褪测,“mysqlcheck:表維護和維修程序”猴誊。(在pre-alpha編碼中,這個功能已經(jīng)可以使用)侮措。
? ? ? 18.3.4. 獲取關(guān)于分區(qū)的信息
? ? ? 本節(jié)討論獲取關(guān)于現(xiàn)有分區(qū)的信息懈叹。這個功能仍然處于計劃階段,所以現(xiàn)階段在這里描述的分扎,實際上是我們想要在MySQL 5.1中實現(xiàn)的一個概觀澄成。
? ? ? 如在本章中別處討論的一樣,在SHOW CREATE TABLE的輸出中包含了用于創(chuàng)建分區(qū)表的PARTITION BY子句畏吓。例如:
mysql> SHOW CREATE TABLE trb3\G*************************** 1. row ***************************? ? ? Table: trb3Create Table: CREATE TABLE `trb3` (? `id` int(11) default NULL,? `name` varchar(50) default NULL,? `purchased` date default NULL) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY RANGE (YEAR(purchased)) (? PARTITION p0 VALUES LESS THAN (1990) ENGINE = MyISAM,? PARTITION p1 VALUES LESS THAN (1995) ENGINE = MyISAM,? PARTITION p2 VALUES LESS THAN (2000) ENGINE = MyISAM,? PARTITION p3 VALUES LESS THAN (2005) ENGINE = MyISAM)1 row in set (0.00 sec)
? ? ? 注釋:當(dāng)前墨状,對于按HASH或KEY分區(qū)的表,PARTITIONS子句并不顯示庵佣。 (Bug #14327)
? ? ? SHOW TABLE STATUS用于分區(qū)表歉胶,它的輸出與用于非分區(qū)表的輸出相同,除了引擎(Engine)列總是包含'PARTITION'值巴粪。(關(guān)于這個命令的更多信息通今,參見13.5.4.18節(jié),“SHOW TABLE STATUS語法”)肛根。要獲取單個分區(qū)的狀態(tài)信息辫塌,我們計劃實現(xiàn)一個SHOW PARTITION STATUS命令(請參見下面)。
? ? ? 計劃用于分區(qū)表的派哲、兩個附加的SHOW命令是:
? ? ? ·? ? ? ? SHOW PARTITIONS
? ? ? 這個命令預(yù)期其功能類似于SHOW TABLES和SHOW DATABASES臼氨,除了該命令將列出的是分區(qū)而不是表或數(shù)據(jù)庫。這個命令的輸出可能包含單個稱為Partitions_in_tbl_name 的列芭届,其中tbl_name 是分區(qū)表的名字储矩。對于SHOW TABLES命令而言,如果一旦選擇了一個數(shù)據(jù)庫褂乍,隨后該數(shù)據(jù)庫將作為SHOW TABLES命令的默認數(shù)據(jù)庫持隧。但是由于SHOW PARTITIONS命令不可能用這樣的方式來“選擇”一個表,它很可能需要使用FROM子句逃片,以便MySQL知道要顯示的是哪個表的分區(qū)信息屡拨。
? ? ? ·? ? ? ? SHOW PARTITION STATUS
? ? ? 這個命令將提供關(guān)于一個或多個分區(qū)的詳細狀態(tài)信息。它的輸出很可能包含有與SHOW TABLE STATUS 的輸出相同或類似的列褥实,此外呀狼,還包括顯示用于分區(qū)的數(shù)據(jù)和索引路徑的附加列。這個命令可能支持LIKE和FROM子句损离,這樣使得通過名字獲得關(guān)于一個給定分區(qū)的信息哥艇,或者獲得關(guān)于屬于指定表或數(shù)據(jù)庫的分區(qū)的信息,成為可能僻澎。
? ? ? 擴展INFORMATION_SCHEMA 數(shù)據(jù)庫的計劃也在進行中貌踏,以便提供關(guān)于分區(qū)表和分區(qū)的信息瓮增。這個計劃當(dāng)前還處一個在非常早的階段;隨著補充的信息變得可用哩俭,以及任何新的绷跑、與分區(qū)有關(guān)的INFORMATION_SCHEMA擴展得以實現(xiàn),我們將更新手冊相關(guān)部分的內(nèi)容凡资。