?
本文檔使用 PHP中文網(wǎng)手冊 發(fā)布
目錄
本章討論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)設法在本章中標出這些差異。在提出缺陷報告前,我們鼓勵參考下面的一些資源:
MySQL 分區(qū)論壇
這是一個為對MySQL分區(qū)技術(shù)感興趣或用MySQL分區(qū)技術(shù)做試驗提供的官方討論論壇。來自MySQL 的開發(fā)者和其他的人,會在上面發(fā)表和更新有關(guān)的材料。它由分區(qū)開發(fā)和文獻團隊的成員負責監(jiān)控。
分區(qū)缺陷報告
已經(jīng)歸檔在缺陷系統(tǒng)中的、所有分區(qū)缺陷的一個列表,而無論這些缺陷的年限、嚴重性或當前的狀態(tài)如何。根據(jù)許多規(guī)則可以對這些缺陷進行篩選,或者可以從MySQL缺陷系統(tǒng)主頁 開始,然后查找你特別感興趣的缺陷。
Mikael Ronstr?m's Blog
MySQL分區(qū)體系結(jié)構(gòu)和領(lǐng)先的開發(fā)者Mikael Ronstr?m 經(jīng)常在這里貼關(guān)于他研究MySQL 分區(qū)和MySQL簇的文章。
PlanetMySQL
一個MySQL 新聞網(wǎng)站,它以匯集MySQL相關(guān)的網(wǎng)誌為特點,那些使用我的MySQL的人應該對此有興趣。我們鼓勵查看那些研究MySQL分區(qū)的人的網(wǎng)誌鏈接,或者把你自己的網(wǎng)誌加到這些新聞報道中。
MySQL 5.1的二進制版本目前還不可用;但是,可以從BitKeeper知識庫中獲得源碼。要激活分區(qū),需要使用--with-分區(qū)選項編譯服務器。關(guān)于建立MySQL 的更多信息,請參見2.8節(jié),“使用源碼分發(fā)版安裝MySQL”。如果在編譯一個激活分區(qū)的MySQL 5.1創(chuàng)建中碰到問題,可以在MySQL分區(qū)論壇中查找解決辦法,如果在論壇中已經(jīng)貼出的文章中沒有找到問題的解決辦法,可以在上面尋找?guī)椭?/p>
本節(jié)提供了關(guān)于MySQL 5.1.分區(qū)在概念上的概述。
SQL標準在數(shù)據(jù)存儲的物理方面沒有提供太多的指南。SQL語言的使用獨立于它所使用的任何數(shù)據(jù)結(jié)構(gòu)或圖表、表、行或列下的介質(zhì)。但是,大部分高級數(shù)據(jù)庫管理系統(tǒng)已經(jīng)開發(fā)了一些根據(jù)文件系統(tǒng)、硬件或者這兩者來確定將要用于存儲特定數(shù)據(jù)塊物理位置的方法。在MySQL中,InnoDB存儲引擎長期支持表空間的概念,并且MySQL服務器甚至在分區(qū)引入之前,就能配置為存儲不同的數(shù)據(jù)庫使用不同的物理路徑(關(guān)于如何配置的解釋,請參見7.6.1節(jié),“使用符號鏈接”)。
分區(qū)又把這個概念推進了一步,它允許根據(jù)可以設置為任意大小的規(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ù)返回一個表示那個特定記錄應該保存在哪個分區(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ū)語法描述中找到。
當二進制碼變成可用時(也就是說,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 服務器所支持的任何存儲引擎;MySQL 分區(qū)引擎在一個單獨的層中運行,并且可以和任何這樣的層進行相互作用。在MySQL 5.1版中,同一個分區(qū)表的所有分區(qū)必須使用同一個存儲引擎;例如,不能對一個分區(qū)使用MyISAM,而對另一個使用InnoDB。但是,這并不妨礙在同一個 MySQL 服務器中,甚至在同一個數(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ū)的臨時表也是可能的;但是,這種表的生命周期只有當前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ù),它們可以各自保存在每個分區(qū)里。關(guān)于這些選項的更多信息,請參見18.3節(jié),“分區(qū)管理”。注釋:這個特殊的功能由于Bug #13250的原因,目前還不能實用。在第一個5.1二進制版本投入使用時,我們應該已經(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ù)查詢,來獲得更大的查詢吞吐量。
本節(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 服務器提供其自身的哈希函數(shù)。必須有一列或多列包含整數(shù)值。參見18.2.4節(jié),“KEY分區(qū)”。
無論使用何種類型的分區(qū),分區(qū)總是在創(chuàng)建時就自動的順序編號,且從0開始記錄,記住這一點非常重要。當有一新行插入到一個分區(qū)表中時,就是使用這些分區(qū)編號來識別正確的分區(qū)。例如,如果你的表使用4個分區(qū),那么這些分區(qū)就編號為0, 1, 2, 和3。對于RANGE和LIST分區(qū)類型,確認每個分區(qū)編號都定義了一個分區(qū),很有必要。對HASH分區(qū),使用的用戶函數(shù)必須返回一個大于0的整數(shù)值。對于KEY分區(qū),這個問題通過MySQL服務器內(nèi)部使用的 哈希函數(shù)自動進行處理。
分區(qū)的名字基本上遵循其他MySQL 標識符應當遵循的原則,例如用于表和數(shù)據(jù)庫名字的標識符。但是應當注意,分區(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語法” 中找到。
按照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工作的雇員相對應的所有行被保存在分區(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),服務器將不知道把該行保存在何處,將會導致錯誤。 要避免這種錯誤,可以通過在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中。在將來的某個時候,當商店數(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ū)在如下場合特別有用:
·???????? 當需要刪除“舊的”數(shù)據(jù)時。如果你使用上面最近的那個例子給出的分區(qū)方案,你只需簡單地使用 “ALTER TABLE employees DROP PARTITION p0;”來刪除所有在1991年前就已經(jīng)停止工作的雇員相對應的所有行。(更多信息請參見13.1.2節(jié),“ALTER TABLE語法” 和 18.3節(jié),“分區(qū)管理”)。對于有大量行的表,這比運行一個如“DELETE FROM employees WHERE YEAR(separated) <= 1990;”這樣的一個DELETE查詢要有效得多。
·???????? 想要使用一個包含有日期或時間值,或包含有從一些其他級數(shù)開始增長的值的列。
·???????? 經(jī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)工作正在進行中。
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中,當使用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é)合起來生成一個復合的子分區(qū),與HASH和KEY分區(qū)結(jié)合起來生成復合的子分區(qū)也是可能的。 關(guān)于這方面的討論,請參考18.2.5節(jié),“子分區(qū)”。
HASH分區(qū)主要用來確保數(shù)據(jù)在預先確定數(shù)目的分區(qū)中平均分布。在RANGE和LIST分區(qū)中,必須明確指定一個給定的列值或列值集合應該保存在哪個分區(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 設置,以便確保所有的行都能合適地插入到分區(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ù)。(換句話說,它既是變化的但又是確定的)。但是應當記住,每當插入或更新(或者可能刪除)一行,這個表達式都要計算一次;這意味著非常復雜的表達式可能會引起性能問題,尤其是在執(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)”也是可能的,但是要確定哪些適于 哈希是非常困難和耗時的?;谶@個原因,實際上不推薦使用涉及到多列的哈希表達式。
當使用了“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 還支持一個被稱為“linear hashing(線性哈希功能)”的變量,它使用一個更加復雜的算法來確定新行插入到已經(jīng)分區(qū)了的表中的位置。關(guān)于這種算法的描述,請參見18.2.3.1節(jié),“LINEAR HASH分區(qū)” 。
每當插入或更新一條記錄,用戶函數(shù)都要計算一次。當刪除記錄時,用戶函數(shù)也可能要進行計算,這取決于所處的環(huán)境。
注釋:如果將要分區(qū)的表有一個唯一的鍵,那么用來作為HASH用戶函數(shù)的自變數(shù)或者主鍵的column_list的自變數(shù)的任意列都必須是那個鍵的一部分。
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;
假設一個表達式expr, 當使用線性哈希功能時,記錄將要保存到的分區(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.??? 設置 N = F(column_list) & (V - 1).
4.??? 當 N >= num:
·???????? 設置 V = CEIL(V / 2)
·???????? 設置 N = N & (V - 1)
例如,假設表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)在假設要插入兩行記錄到表t1中,其中一條記錄col3列值為'2003-04-14',另一條記錄col3列值為'1998-10-19'。第一條記錄將要保存到的分區(qū)確定如下:
V = POWER(2, CEILING(LOG(2,7))) = 8
N = YEAR('2003-04-14') & (8 - 1)
?? = 2003 & 7
?? = 3
?
(3 >= 6 為假(FALSE): 記錄將被保存到#3號分區(qū)中)
第二條記錄將要保存到的分區(qū)序號計算如下:
V = 8
N = 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ù)的分布不大可能均衡。
按照KEY進行分區(qū)類似于按照HASH分區(qū),除了HASH分區(qū)使用的用戶定義的表達式,而KEY分區(qū)的 哈希函數(shù)是由MySQL 服務器提供。MySQL 簇(Cluster)使用函數(shù)MD5()來實現(xiàn)KEY分區(qū);對于使用其他存儲引擎的表,服務器使用其自己內(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ū)” 。
子分區(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ū)。這也被稱為復合分區(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ū)設置任何你所需要的選項,或者允許該子分區(qū)對那些選項采用其默認的設置。
·???????? 在每個分區(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ù)和索引。假設有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ū)那么大的空間。當前,在默認的位置能夠足夠保存所有這些記錄。
將來,如果從2000年開始后十年購買的數(shù)量已經(jīng)達到了默認的位置不能夠提供足夠的保存空間時,相應的記錄(行)可以通過使用“ALTER TABLE ... REORGANIZE PARTITION”語句移動到其他的位置。關(guān)于如何實現(xiàn)的說明,請參見18.3節(jié),“分區(qū)管理” 。
MySQL 中的分區(qū)在禁止空值(NULL)上沒有進行處理,無論它是一個列值還是一個用戶定義表達式的值。一般而言,在這種情況下MySQL 把NULL視為0。如果你希望回避這種做法,你應該在設計表時不允許空值;最可能的方法是,通過聲明列“NOT NULL”來實現(xiàn)這一點。
在本節(jié)中,我們提供了一些例子,來說明當決定一個行應該保存到哪個分區(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ū)的表,然后插入一個包含有適當值的記錄,再檢查對文件系統(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é)?,F(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?;氐较到y(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中。
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ū)是不可能的。
要點:當前,從5.1系列起建立的MySQL 服務器就把“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 開頭。例如,假設有一個使用下面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)建這個表具有同樣的效果。
關(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)。這里有一個非?;镜睦?,假設已經(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)
記住下面一點非常重要:當刪除了一個分區(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: tr
Create 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ù)并沒有被服務器報告出來,就好像通過同等的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ù)據(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)
);
進一步假設成員的最小年紀是16歲。隨著日歷接近2005年年底,你會認識到不久將要接納1990年(以及以后年份)出生的成員??梢园凑障旅娴姆绞剑薷某蓡T表來容納出生在1990-1999年之間的成員:
ALTER TABLE ADD PARTITION (PARTITION p3 VALUES LESS THAN (2000));
要點:對于通過RANGE分區(qū)的表,只可以使用ADD PARTITION添加新的分區(qū)到分區(qū)列表的高端。設法通過這種方式在現(xiàn)有分區(qū)的前面或之間增加一個新的分區(qū),將會導致下面的一個錯誤:
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ū)值列表中的任意值。如果試圖這樣做,將會導致錯誤:
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。但是,正如我們前面討論過的,這將導致保存在分區(qū)p1中的所有數(shù)據(jù)丟失——而這往往并不是你所真正想要做的。另外一種解決方法可能是,建立一個帶有新分區(qū)的表的副本,然后使用“CREATE TABLE ... SELECT ...”把數(shù)據(jù)拷貝到該新表中,然后刪除舊表,重新命名新表,但是,當需要處理大量的數(shù)據(jù)時,這可能是非常耗時的。在需要高可用性的場合,這也可能是不可行的。
幸運地是,MySQL 的分區(qū)實現(xiàn)提供了在不丟失數(shù)據(jù)的條件下重新定義分區(qū)的方式。讓我們首先看兩個涉及到RANGE分區(qū)的簡單例子?;叵胍幌卢F(xiàn)在定義如下的成員表:
mysql> SHOW CREATE TABLE members\G
*************************** 1. row ***************************
?????? Table: members
Create 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ū)??梢允褂萌缦碌恼Z句恢復成員表到它以前的分區(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語法”)。應當注意到,在把多少個分區(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)
);
當使用“ALTER TABLE ... REORGANIZE PARTITION”來對已經(jīng)按照RANGE和LIST分區(qū)表進行重新分區(qū)時,下面是一些要記住的關(guān)鍵點:
·???????? 用來確定新分區(qū)模式的PARTITION子句使用與用在CREATE TABLE中確定分區(qū)模式的PARTITION子句相同的規(guī)則。
最重要的是,應該記?。盒路謪^(qū)模式不能有任何重疊的區(qū)間(適用于按照RANGE分區(qū)的表)或值集合(適用于重新組織按照LIST分區(qū)的表)。
·???????? partition_definitions 列表中分區(qū)的合集應該與在partition_list 中命名分區(qū)的合集占有相同的區(qū)間或值集合。
例如,在本節(jié)中用作例子的成員表中,分區(qū)p1和p2總共覆蓋了1980到1999的這些年。因此,對這兩個分區(qū)的重新組織都應該覆蓋相同范圍的年份。
·???????? 對于按照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)?span>HASH分區(qū),反之亦然。也不能使用該命令來改變分區(qū)表達式或列。如果想在不刪除和重建表的條件下實現(xiàn)這兩個任務,可以使用“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ù)拷貝到新表中,再刪除舊表,如有必要,最后重新命名新表。
在改變分區(qū)設置方面,按照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ū)的表。
注釋:實際上,本節(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ū)維護的任務。對于分區(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 應用程序,在對表進行分區(qū)時所產(chǎn)生的、單獨的MYI文件上進行操作,來完成這些任務。請參見8.7節(jié),“mysqlcheck:表維護和維修程序”。(在pre-alpha編碼中,這個功能已經(jīng)可以使用)。
本節(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: trb3
Create 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)
注釋:當前,對于按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
這個命令預期其功能類似于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ū)的信息。這個計劃當前還處一個在非常早的階段;隨著補充的信息變得可用,以及任何新的、與分區(qū)有關(guān)的INFORMATION_SCHEMA擴展得以實現(xiàn),我們將更新手冊相關(guān)部分的內(nèi)容。
這是MySQL參考手冊的翻譯版本,關(guān)于MySQL參考手冊,請訪問dev.mysql.com。原始參考手冊為英文版,與英文版參考手冊相比,本翻譯版可能不是最新的。