MySQL(十四)------- MySQL分區

         分區是根據一定的規則把數據庫中的一張表分解成多個更小的、更容易管理的部分,這些部分作爲一個獨立的對象可以存放在不同的地方。對於用戶來說,訪問表裏的數據跟不分區沒什麼差別,但是對於數據庫本身及其管理維護來說有很多好處:

  • 和單個磁盤或者文件系統相比,分區可以存儲更多的數據;
  • 優化查詢。在where字句中包含分區條件時,可以只掃描必要的一個或多個分區來提高查詢效率;同時在涉及SUM() 和 COUNT()這類聚合函數的查詢時,可以容易的在每個分區上並行處理,最終只需要彙總結果即可;
  • 對於已經過期或者不需要保存的數據,可以刪除與這些數據有關的分區來快速刪除數據;
  • 跨多個磁盤來分散數據查詢,以獲得更大的查詢吞吐量。

一、分區概述

      分區有利於管理非常大的表,將大表分區存儲分成小塊;查看當前MySQL是否支持分區:

MySQL5.1版本開始支持分區功能,MySQL5.6版本之前使用以下命令查看是否支持分區
mysql> show variables like '%partition%';
+-----------------------+-------------+
| Variable_name         |   Value     |                                                                                
+-----------------------+-------------+
| have_partition_engine |   YES       |         YES表明支持分區
+-----------------------+-------------+
1 row in set (0.00 sec)

MySQL5.6及其之後的版本使用以下命令查看
mysql> show plugins;
+----------------------------+----------+--------------------+---------+---------+
| Name                       | Status   | Type               | Library | License |
+----------------------------+----------+--------------------+---------+---------+
| binlog                     | ACTIVE   | STORAGE ENGINE     | NULL    | GPL     |
| mysql_native_password      | ACTIVE   | AUTHENTICATION     | NULL    | GPL     |
| sha256_password            | ACTIVE   | AUTHENTICATION     | NULL    | GPL     |
| CSV                        | ACTIVE   | STORAGE ENGINE     | NULL    | GPL     |
| MEMORY                     | ACTIVE   | STORAGE ENGINE     | NULL    | GPL     |
| InnoDB                     | ACTIVE   | STORAGE ENGINE     | NULL    | GPL     |
| INNODB_TRX                 | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_LOCKS               | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_LOCK_WAITS          | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_CMP                 | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_CMP_RESET           | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_CMPMEM              | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_CMPMEM_RESET        | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_CMP_PER_INDEX       | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_CMP_PER_INDEX_RESET | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_BUFFER_PAGE         | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_BUFFER_PAGE_LRU     | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_BUFFER_POOL_STATS   | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_TEMP_TABLE_INFO     | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_METRICS             | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_FT_DEFAULT_STOPWORD | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_FT_DELETED          | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_FT_BEING_DELETED    | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_FT_CONFIG           | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_FT_INDEX_CACHE      | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_FT_INDEX_TABLE      | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_SYS_TABLES          | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_SYS_TABLESTATS      | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_SYS_INDEXES         | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_SYS_COLUMNS         | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_SYS_FIELDS          | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_SYS_FOREIGN         | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_SYS_FOREIGN_COLS    | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_SYS_TABLESPACES     | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_SYS_DATAFILES       | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_SYS_VIRTUAL         | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| MyISAM                     | ACTIVE   | STORAGE ENGINE     | NULL    | GPL     |
| MRG_MYISAM                 | ACTIVE   | STORAGE ENGINE     | NULL    | GPL     |
| PERFORMANCE_SCHEMA         | ACTIVE   | STORAGE ENGINE     | NULL    | GPL     |
| ARCHIVE                    | ACTIVE   | STORAGE ENGINE     | NULL    | GPL     |
| BLACKHOLE                  | ACTIVE   | STORAGE ENGINE     | NULL    | GPL     |
| FEDERATED                  | DISABLED | STORAGE ENGINE     | NULL    | GPL     |
| partition                  | ACTIVE   | STORAGE ENGINE     | NULL    | GPL     |
| ngram                      | ACTIVE   | FTPARSER           | NULL    | GPL     |
+----------------------------+----------+--------------------+---------+---------+
44 rows in set (0.00 sec)

關鍵字partition的值爲ACTIVE表明支持分區

           MySQL支持MyISAM、InnoDB、Memory等存儲引擎創建分區表,不支持MERGE、CSV存儲引擎來創建分區表;另外,同一張表的不同分區存儲引擎必須一致。指定引擎的語句(engine=innodb)必須在分區語句(partition by ...)之前。

二、分區類型

       主要有以下幾種:

  • RANGE分區:分區的值是連續的,不間斷;比如1~10,11~20,...
  • LIST分區:分區的值是離散的、無序的;比如(1,3),(2,6),(4,5),......
  • HASH分區:給定分區的個數,利用一定的規則將數據分到各個區中;
  • KEY分區:類似於HASH分區;
  • COLUMNS分區:MySQL5.5版本後新引入的,可細分爲RANGE COLUMNS,LIST COLUMNS;主要解決的是RANGE分區和LIST分區只支持整型作爲分區鍵的侷限性;
  RANGE分區 LIST分區 HASH分區 KEY分區 RANGE COLUMNS分區 LIST COLUMNS分區
分區鍵的類型 INT類型 INT類型 INT類型

除BLOB、TEXT類型

外的其它類型

整型、日期型、字符串型 整型、日期型、字符串型
分區鍵的選用 表中有主鍵或唯一鍵時必須選該列作爲分區字段,如果沒有則選擇滿足條件的即可
分區名 分區名字對大小寫不敏感,不能用大小寫來區分同一個分區名
             
             
             
             
             
             

有主鍵但是不用主鍵分區鍵時會報錯:

mysql> create table emp(id INT not null primary key,
                        ename varchar(30),
                        hired date not null default '1970-01-01',
                        separated date not null default '9999-12-31',
                        job varchar(30) not null,
                        store_id int not null) 
       partition by range (store_id) (
                   partition p0 values less than (10),
                   partition p1 values less than (20),
                   partition p2 values less than (30));
ERROR 1503 (HY000): A PRIMARY KEY must include all columns in the table's partitioning function

當去掉主鍵後就可成功創建
mysql> create table emp(id INT not null,
                        ename varchar(30),
                        hired date not null default '1970-01-01',
                        separated date not null default '9999-12-31',
                        job varchar(30) not null,
                        store_id int not null) 
       partition by range (store_id) (
                   partition p0 values less than (10),
                   partition p1 values less than (20),
                   partition p2 values less than (30));
Query OK, 0 rows affected (0.03 sec)

2.1 Range分區

       比如上面的的例子,成功創建了表emp,按照字段store_id進行分區,一共分爲三個區,小於10的在p0區,10~19的在p1區,20~29的在p2區;區間要連續而且不能相互重疊。

        但如果此時有一條大於30的數據插入,應該放在哪個區呢?,顯然,這時候會出錯,因爲系統也不知道該把這個數據放到哪個區裏:

mysql> insert into emp values(1,'tom','1982-01-23','2001-02-03','clerk',50);
ERROR 1526 (HY000): Table has no partition for value 50

        那這時候應該怎麼辦呢?增加一個分區,當值超過指定範圍時都放入到該分區裏面來:

增加p3分區,操過p2分區最大值的數據將全放到該分區中
mysql> alter table emp add partition (partition p3 values less than maxvalue);
Query OK, 0 rows affected (0.03 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> insert into emp values(1,'tom','1982-01-23','2001-02-03','clerk',50);
Query OK, 1 row affected (0.00 sec)

          前面還提到Range分區的分區鍵必須是整型,比如上面的store_id是int,但並不絕對,你也可以使用表達式或函數來將一個不是整型的字段處理後返回一個整型,這樣也可,比如:

創建一個表emp_date,使用年份來分區
mysql> create table emp_date(id INT not null,
                             ename varchar(30),
                             hired date not null default '1970-01-01',
                             separated date not null default '9999-12-31',
                             job varchar(30) not null,
                             store_id int not null) 
       partition by range (YEAR(separated)) ( 
                             partition p0 values less than (1995),
                             partition p1 values less than (2000),
                             partition p2 values less than (2005));
Query OK, 0 rows affected (0.03 sec)

           使用這樣轉換的方式可以達到效果,但也有缺陷,比如日期類型的數據只有YEAR()、MONTH()、TO_DAYS()、TO_SECONDS()等轉換函數可用,如果在此之外無法轉換的該怎麼辦呢?MySQL5.5版本之後提供了新的解決辦法,使用RANGE COLUMNS分區,它支持多種類型作爲分區鍵:

mysql> drop table emp_date;
Query OK, 0 rows affected (0.01 sec)

mysql> create table emp_date(id INT not null,
                             ename varchar(30), 
                             hired date not null default '1970-01-01',
                             separated date not null default '9999-12-31',
                             job varchar(30) not null,
                             store_id int not null) 
       partition by range columns(separated) (
                             partition p0 values less than ('1996-01-01'),
                             partition p1 values less than ('2001-01-01'),
                             partition p2 values less than ('2006-01-01'));
Query OK, 0 rows affected (0.03 sec)

RANGE分區功能特別適合以下兩種情況:

  • 當需要刪除過期的數據時,只需要用一個簡單的刪除分區語句就可以將某個日期前的分區刪掉,這對於大表來說比用DELETE刪除查詢的數據要快很多;
  • 經常運行包含分區鍵的查詢,這樣MySQL就可以只掃描包含該數據的分區,提高了效率;例如查emp表中store_id大於25的記錄,MySQL只需掃描p2、p3分區即可:
mysql> explain partitions select count(1) from emp where store_id >= 25 \G;
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: emp
   partitions: p2,p3
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 1
     filtered: 100.00
        Extra: Using where
1 row in set, 2 warnings (0.00 sec)

2.2 LIST分區

       LIST分區跟RANGE分區類似,只不過它用的是離散值,相當於枚舉,如果數據不在分區的集合中,那麼它就不在該分區,LIST分區不必聲明特定的順序:

mysql> create table expenses(expense_date date not null,
                             category int,
                             amount decimal(10,3)) 
       partition by list(category) (
                             partition p0 values in (3,5),
                             partition p1 values in (1,10),
                             partition p2 values in (4,9),
                             partition p3 values in (2),
                             partition p4 values in (6));
Query OK, 0 rows affected (0.05 sec)

        用LIST分區創建的表在插入數據時比如能在所有分區的集合中找得到,否則它就無法插入;它不存在像RANGE分區中那樣定義一個maxvalue來囊括範圍外的值。

        同樣,LIST分區的分區鍵只能是INT類型,如果想要用別的類型需使用LIST COLUMNS來創建:

mysql> create table expenses(expense_date date not null,
                             category varchar(30),
                             amount decimal(10,3)) 
       partition by list columns (category) (
                             partition p0 values in ('lind','food'),
                             partition p1 values in ('fight','begin'),
                             partition p2 values in ('lisa','mon'),
                             partition p3 values in ('talk'),
                             partition p4 values in ('fees'));
Query OK, 0 rows affected (0.08 sec)

2.3 Columns 分區

       前面也介紹了一些該分區的相關內容,它主要解決的就是RANGE分區和LIST分區只支持整型作爲分區鍵導致如果需要用別的類型時需要額外的函數計算得到整數或通過額外的轉換表來轉換爲整數再分區的問題。

COLUMNS分區支持的數據類型:

  • 所有整型:tinyint, smallint, mediumint, int, bigint;其它數值類型不支持,如Decimal和Float;
  • 日期時間類型:date和datetime;
  • 字符類型:char, varchar, binary, varbinary;不支持blob和text類型;

       對比RANGE和LIST分區,COLUMNS分區除了支持的數據類型增加之外,它還支持多列劃分,例如下面的例子:

1. 創建表cr3,使用a,b兩列作爲分區鍵:
mysql> create table rc3(a int,b int) 
       partition by range columns (a,b) (
                 partition p01 values less than (0,10),
                 partition p02 values less than (10,10),
                 partition p03 values less than (10,20),
                 partition p04 values less than (10,35),
                 partition p05 values less than (10,maxvalue),
                 partition p06 values less than (maxvalue,maxvalue));
Query OK, 0 rows affected (0.11 sec)

2. 插入數據(1,10),可以看到插入到p02中去了
mysql> insert into rc3 values(1,10);
Query OK, 1 row affected (0.00 sec)

mysql> select partition_name part,partition_expression expr,partition_description descr,table_rows from information_schema.partitions where table_schema = schema() and table_name='rc3';
+------+---------+-------------------+------------+
| part | expr    | descr             | table_rows |
+------+---------+-------------------+------------+
| p01  | `a`,`b` | 0,10              |          0 |
| p02  | `a`,`b` | 10,10             |          1 |
| p03  | `a`,`b` | 10,20             |          0 |
| p04  | `a`,`b` | 10,35             |          0 |
| p05  | `a`,`b` | 10,MAXVALUE       |          0 |
| p06  | `a`,`b` | MAXVALUE,MAXVALUE |          0 |
+------+---------+-------------------+------------+
6 rows in set (0.00 sec)

3. 插入數據(10,9),可以看到也插入p02分區中了
mysql> insert into rc3 values(10,9);
Query OK, 1 row affected (0.00 sec)

mysql> select partition_name part,partition_expression expr,partition_description descr,table_rows from information_schema.partitions where table_schema = schema() and table_name='rc3';
+------+---------+-------------------+------------+
| part | expr    | descr             | table_rows |
+------+---------+-------------------+------------+
| p01  | `a`,`b` | 0,10              |          0 |
| p02  | `a`,`b` | 10,10             |          2 |
| p03  | `a`,`b` | 10,20             |          0 |
| p04  | `a`,`b` | 10,35             |          0 |
| p05  | `a`,`b` | 10,MAXVALUE       |          0 |
| p06  | `a`,`b` | MAXVALUE,MAXVALUE |          0 |
+------+---------+-------------------+------------+
6 rows in set (0.00 sec)

4. 插入數據(10,10),可以看到被插入p03中了
mysql> insert into rc3 values(10,10);
Query OK, 1 row affected (0.00 sec)

mysql> select partition_name part,partition_expression expr,partition_description descr,table_rows from information_schema.partitions where table_schema = schema() and table_name='rc3';
+------+---------+-------------------+------------+
| part | expr    | descr             | table_rows |
+------+---------+-------------------+------------+
| p01  | `a`,`b` | 0,10              |          0 |
| p02  | `a`,`b` | 10,10             |          2 |
| p03  | `a`,`b` | 10,20             |          1 |
| p04  | `a`,`b` | 10,35             |          0 |
| p05  | `a`,`b` | 10,MAXVALUE       |          0 |
| p06  | `a`,`b` | MAXVALUE,MAXVALUE |          0 |
+------+---------+-------------------+------------+
6 rows in set (0.00 sec)

           總結:這種分區鍵的比較其實就是多列排序,先根據a字段排序,再根據b字段排序;(a, b)插入到哪個分區時它是這麼比較的:先判斷p01分區 (a < 0) OR ((a =0) AND (b < 10)),如果成立則放在p01分區,如果不成立再檢查p02分區 (a < 10) OR ((a = 10) AND (b < 10)),依次類推。例如插入數據(10,10)時是這麼比較:p01、p02都不滿足,到p03時(10 < 10) OR ((10 = 10) AND (10 < 20))成立,因此存放到p03分區。

2.4  HASH分區

        HASH分區主要用來分散熱點讀,確保數據在預先定義的分區中儘量平均分佈。MySQL支持兩種HASH分區,常規HASH分區線性HASH分區;下面分別說明一下在這兩種情況下插入數據時如何確定插入的是哪個分區。

  常規HASH分區:

1. 用hash分區創建表emp,分爲4個區
mysql> create table emp(id INT not null,
                        ename varchar(30),
                        hired date not null default '1970-01-01',
                        separated date not null default '9999-12-31',
                        job varchar(30) not null,
                        store_id int not null) 
       partition by hash (store_id) partitions 4;
Query OK, 0 rows affected (0.07 sec)

2. 插入了一條數據234,可以看到它被插入了p2分區中
mysql> insert into emp values (1,'tom','2010-10-10','9999-12-31','clerk',234);
Query OK, 1 row affected (0.01 sec)

mysql> explain partitions select * from emp where store_id = 234 \G;
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: emp
   partitions: p2
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 1
     filtered: 100.00
        Extra: Using where
1 row in set, 2 warnings (0.00 sec)

         確定數據插入到哪個分區中的規則是取餘法,比如上面的234插入有4個分區的表中,餘數N = MOD(234, 4) = 2,因此插入到第二個分區裏面;再例如229,餘數N = MOD(229, 4) = 1,因此它會被插入到第1個分區中。

         這樣的規則可以確保數據比較均勻的插入到定義的多個分區中,但是每當插入\更新\刪除一行數據時,PARTITION BY HASH(expr)裏面的expr都需要重新計算一次,這會引起性能問題,因此MySQL也不推薦使用涉及多列的哈希表達式。

         常規HASH分區有一個嚴重弊端,就是增加分區或者合併分區的時候,原來的數據需要重新計算重新分區,這樣做的代價非常大。比如原來4個分區,都存了一些數據,現在要增加一個分區到5個分區,那麼原來的餘數是0~3,現在的餘數是0~4,原來存儲的數據都需要重新計算並存儲到新區裏面。線性HASH分區就是來解決這個問題的。

線性HASH分區:在創建時只需加一個關鍵字linear

mysql> create table emp(id INT not null,
                        ename varchar(30),
                        hired date not null default '1970-01-01',
                        separated date not null default '9999-12-31',
                        job varchar(30) not null,
                        store_id int not null) 
        partition by linear hash (store_id) partitions 4;
Query OK, 0 rows affected (0.06 sec)

mysql> insert into emp values (1,'tom','2010-10-10','9999-12-31','clerk',234);
Query OK, 1 row affected (0.01 sec)

mysql> explain partitions select * from emp where store_id = 234 \G;
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: emp
   partitions: p2
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 1
     filtered: 100.00
        Extra: Using where
1 row in set, 2 warnings (0.00 sec)

           線性HASH分區的規則是線性的2的冪的運算法則。具體規則如下:

N = F(column_list) & (V - 1) ;  N表示分區的位置,F(column_list)就是插入的數據值,V表示2的冪值
V = Power(2, Ceiling(Log(2, num))) ; num表示總的分區個數
當N < num時,N是多少就放到第幾個分區;
當N > num時,V = Ceiling(V/2), N = N & (V - 1) .

         例如上面的例子,4個分區,插入數值234,這樣計算得到V = Power(2, Ceiling(Log(2, 4))) = Power(2, Ceiling(2)) = Power(2, 2) = 4,N = 234 & 3 = (1101010 & 0000011 = 10) = 2 ; 2小於4,所以直接放入第二個分區。

        特例,當線性HASH分區的個數是2的N次冪時,線性HASH分區的結果和常規HASH分區的結果是一致的。

         線性HASH分區的優點是在分區維護(增加、刪除、合併、拆分分區)時,MySQL能處理得更加迅速;缺點是線性HASH各個分區之間的數據分佈不太均衡。

2.5 KEY分區

       KEY分區域HASH分區很類似,主要的區別有三點:

  • KEY分區不允許使用用戶自定義的表達式,而HASH分區可以;
  • KEY分區支持除BLOB、TEXT外的數據類型作爲分區鍵,而HASH分區只支持整型;
  • KEY分區可以不指定分區鍵,前提是需要有主鍵或唯一鍵,而HASH分區必須指明分區鍵。
創建語句如下:使用job字段將表emp分爲4個區
mysql> create table emp(id INT not null,
                        ename varchar(30),
                        hired date not null default '1970-01-01',
                        separated date not null default '9999-12-31',
                        job varchar(30) not null,
                        store_id int not null) 
       partition by key (job) partitions 4;
Query OK, 0 rows affected (0.07 sec)
不指定分區鍵時默認用主鍵,沒有主鍵時默認用唯一鍵(必須非空,否則報錯),當唯一鍵也沒有時就會報錯,此時不能省略分區鍵
mysql> create table emp(id INT not null,
                        ename varchar(30),
                        hired date not null default '1970-01-01',
                        separated date not null default '9999-12-31',
                        job varchar(30) not null,
                        store_id int not null,
       primary key (id)) partition by key () partitions 4;
Query OK, 0 rows affected (0.06 sec)

        注意:在KEY分區的分區的分區表上不能夠執行“ ALTER TABLE DROP PRIMARY KEY"來刪除主鍵,會報錯。

        同樣,使用關鍵字LINEAR創建線性分區,作用與規則和HASH分區一樣。

2.6  子分區

       子分區(subpartition)是分區表中對每個分區再次進行分割,又稱爲複合分區;它可以在分區的基礎上再次進行分區,兩次分區的類型可以不相同。

mysql> 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);
Query OK, 0 rows affected (0.10 sec)

          表ts有3個range分區,這三個range分區(p0、p1、p2)又被進一步分割成2個hash分區,故整個表一共有6個分區。

          複合分區適用於保存非常大量的數據記錄。

2.7  MYSQL分區處理NULL值的方式

        MySQL不禁止在分區鍵值上使用NULL,但是,對於null值的處理,不同的分區方式處理方式不同。

  • RANGE分區中NULL值會被當做最小值來處理;
  • LIST分區中NULL值必須出現在枚舉列表中,否則不被接受;
  • HASH/KEY分區中NULL值會被當做0值來處理。

range分區中null值 處理舉例:可以看到null值被放在最小分區中。

mysql> create table tb_range(id int,name varchar(5)) 
       partition by range(id) (
                 partition p0 values less than (-6),
                 partition p1 values less than (0),
                 partition p2 values less than (1),
                 partition p3 values less than maxvalue);
Query OK, 0 rows affected (0.06 sec)

mysql> insert into tb_range values (null,'null');
Query OK, 1 row affected (0.01 sec)

mysql> select partition_name part,partition_expression expr,partition_description descr,table_rows from information_schema.partitions where table_schema = schema() and table_name='tb_range';
+------+------+----------+------------+
| part | expr | descr    | table_rows |
+------+------+----------+------------+
| p0   | id   | -6       |          1 |
| p1   | id   | 0        |          0 |
| p2   | id   | 1        |          0 |
| p3   | id   | MAXVALUE |          0 |
+------+------+----------+------------+
4 rows in set (0.00 sec)

LIST分區中null值 處理舉例:可以看到null值必須在枚舉列表中存在,否則不被插入。

mysql> create table tb_list(id int,name varchar(5)) partition by list(id) (partition p0 values in (0),partition p1 values in (1));
Query OK, 0 rows affected (0.04 sec)

mysql> insert into tb_list values (null,'null');
ERROR 1526 (HY000): Table has no partition for value NULL

mysql> drop table tb_list;
Query OK, 0 rows affected (0.03 sec)

mysql> create table tb_list(id int,name varchar(5)) partition by list(id) (partition p0 values in (0,null),partition p1 values in (1));
Query OK, 0 rows affected (0.04 sec)

mysql> insert into tb_list values (null,'null');
Query OK, 1 row affected (0.01 sec)

mysql> select partition_name part,partition_expression expr,partition_description descr,table_rows from information_schema.partitions where table_schema = schema() and table_name='tb_list';
+------+------+--------+------------+
| part | expr | descr  | table_rows |
+------+------+--------+------------+
| p0   | id   | NULL,0 |          1 |
| p1   | id   | 1      |          0 |
+------+------+--------+------------+
2 rows in set (0.00 sec)

HASH分區中null值 處理舉例:可以看到null值被當做0值插入到第一個分區中。

mysql> create table tb_hash(id int,name varchar(5)) partition by hash(id) partitions 2;
Query OK, 0 rows affected (0.05 sec)

mysql> insert into tb_hash values (null,'null');
Query OK, 1 row affected (0.01 sec)

mysql> select partition_name part,partition_expression expr,partition_description descr,table_rows from information_schema.partitions where table_schema = schema() and table_name='tb_hash';
+------+------+-------+------------+
| part | expr | descr | table_rows |
+------+------+-------+------------+
| p0   | id   | NULL  |          1 |
| p1   | id   | NULL  |          0 |
+------+------+-------+------------+
2 rows in set (0.00 sec)

          由於不同的分區類型對null值的處理不一樣,因此,爲了避免在處理NULL值時出現誤判,推薦在設置字段時通過非空約束和默認值來繞開NULL值帶來的麻煩。

三、分區管理

       MySQL支持對分區進行添加、刪除、重定義、合併、拆分分區的命令,都是通過ALTER TABLE來實現。

3.1  RANGE & LIST分區管理

       由於在刪除、添加、重定義分區的處理上,這兩個類型很相似,所以合併來說。

首先準備好表和數據:

mysql> create table emp_date(id INT not null,
    ->                              ename varchar(30),
    ->                              hired date not null default '1970-01-01',
    ->                              separated date not null default '9999-12-31',
    ->                              job varchar(30) not null,
    ->                              store_id int not null)
    ->        partition by range (YEAR(separated)) (
    ->                              partition p0 values less than (1995),
    ->                              partition p1 values less than (2000),
    ->                              partition p2 values less than (2005),
    ->                              partition p3 values less than (2015));
Query OK, 0 rows affected (0.06 sec)

mysql> insert into emp_date values
    -> (7499,'allen','1981-02-20','2003-08-03','salesman',30),
    -> (7521,'al','1981-02-22','1993-09-01','salesman',30),
    -> (7566,'alfj','1981-04-02','2000-08-01','manager',20),
    -> (7654,'alli','1981-09-28','2012-12-31','salesman',30),
    -> (7698,'adby','1981-05-02','1998-09-08','manager',30),
    -> (7782,'asy','1981-09-02','2007-08-01','manager',10),
    -> (7788,'aofxd','1981-09-12','2012-05-01','analyst',20),
    -> (7839,'aloxd','1981-09-02','2011-09-03','president',10),
    -> (7844,'alsef','1981-09-28','2010-12-31','salesman',30),
    -> (7876,'ancsr','1981-09-12','2000-01-01','clerk',20),
    -> (7900,'anvuzy','1981-09-12','2004-09-02','clerk',30),
    -> (7902,'aksad','1981-09-12','2010-10-31','analyst',20),
    -> (7934,'ffgjg','1981-09-12','2011-12-31','clerk',10);
Query OK, 13 rows affected (0.01 sec)
Records: 13  Duplicates: 0  Warnings: 0

mysql> create table expenses(expense_date date not null,
    ->                              category int,
    ->                              amount decimal(10,3))
    ->        partition by list(category) (
    ->                              partition p0 values in (3,5),
    ->                              partition p1 values in (1,10),
    ->                              partition p2 values in (4,9),
    ->                              partition p3 values in (2),
    ->                              partition p4 values in (6));
Query OK, 0 rows affected (0.07 sec)
操作類型 RANGE分區 LIST分區

刪除分區

ALTER TABLE DROP PARTITION

mysql> alter table emp_date drop partition p2;
Query OK, 0 rows affected (0.02 sec)
Records: 0  Duplicates: 0  Warnings: 0
mysql> alter table expenses drop partition p3;
Query OK, 0 rows affected (0.02 sec)
Records: 0  Duplicates: 0  Warnings: 0
 

檢查發現無論是表結構還是數據均被刪除了

mysql> show create table emp_date \G
*************************** 1. row ***************************
       Table: emp_date
Create Table: CREATE TABLE `emp_date` (
  `id` int(11) NOT NULL,
  `ename` varchar(30) DEFAULT NULL,
  `hired` date NOT NULL DEFAULT '1970-01-01',
  `separated` date NOT NULL DEFAULT '9999-12-31',
  `job` varchar(30) NOT NULL,
  `store_id` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8
/*!50100 PARTITION BY RANGE (YEAR(separated))
(PARTITION p0 VALUES LESS THAN (1995) ENGINE = InnoDB,
 PARTITION p1 VALUES LESS THAN (2000) ENGINE = InnoDB,
 PARTITION p3 VALUES LESS THAN (2015) ENGINE = InnoDB) */
1 row in set (0.00 sec)

mysql> select * from emp_date where separated between '2000-01-01'

and '2004-12-31';
Empty set (0.00 sec)

mysql> show create table expenses \G
*************************** 1. row ***************************
       Table: expenses
Create Table: CREATE TABLE `expenses` (
  `expense_date` date NOT NULL,
  `category` int(11) DEFAULT NULL,
  `amount` decimal(10,3) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8
/*!50100 PARTITION BY LIST (category)
(PARTITION p0 VALUES IN (3,5) ENGINE = InnoDB,
 PARTITION p1 VALUES IN (1,10) ENGINE = InnoDB,
 PARTITION p2 VALUES IN (4,9) ENGINE = InnoDB,
 PARTITION p4 VALUES IN (6) ENGINE = InnoDB) */
1 row in set (0.01 sec)
 

當刪除p2分區後插入一條原本應該放在p2分區的數據,

發現此時存放在p3分區了,這說明了range分區哪怕刪除一個也還要保持分區的連續性

mysql> select * from emp_date where separated between

'2000-01-01' and '2004-12-31';
Empty set (0.00 sec)

mysql> select partition_name part,partition_expression expr,

partition_description descr,table_rows from

information_schema.partitions where table_schema = schema()

and table_name='emp_date';
+------+-----------------+-------+------------+
| part | expr            | descr | table_rows |
+------+-----------------+-------+------------+
| p0   | YEAR(separated) | 1995  |          0 |
| p1   | YEAR(separated) | 2000  |          0 |
| p3   | YEAR(separated) | 2015  |          7 |
+------+-----------------+-------+------------+
3 rows in set (0.00 sec)

mysql> insert into emp_date values

(7566,'jons','1981-04-02','2000-08-01','manager',20);
Query OK, 1 row affected (0.00 sec)

mysql> select partition_name part,

partition_expression expr,partition_description descr,

table_rows from information_schema.partitions

where table_schema = schema() and table_name='emp_date';
+------+-----------------+-------+------------+
| part | expr            | descr | table_rows |
+------+-----------------+-------+------------+
| p0   | YEAR(separated) | 1995  |          0 |
| p1   | YEAR(separated) | 2000  |          0 |
| p3   | YEAR(separated) | 2015  |          8 |
+------+-----------------+-------+------------+
3 rows in set (0.00 sec)

當list分區的p3區被刪除後,原來能插入數值2,現在不可以了

增加分區

ALTER TABLE ADD PARTITION

增加p4分區,注意只能從分區列表的最大端增加,比如 

這裏增加的2030是大於2015的,所以能夠成功

mysql> alter table emp_date add partition (partition p4 values less than (2030));
Query OK, 0 rows affected (0.02 sec)
Records: 0  Duplicates: 0  Warnings: 0

增加p5分區,注意增加的值不能在原有分區上出現過;

比如這裏的7、8都沒有在原有分區上出現過,所以能成功

mysql> alter table expenses add partition (partition p5 values in (7,8));
Query OK, 0 rows affected (0.02 sec)
Records: 0  Duplicates: 0  Warnings: 0

拆分分區

ALTER TABLE REORGANIZE

PARTITION INTO

將p3分區(2000~2015)拆分爲p2(2000~2005)分區和p3(2005~2015)分區

mysql> alter table emp_date reorganize partition p3 into (

partition p2 values less than (2005),

partition p3 values less than (2015));
Query OK, 0 rows affected (0.06 sec)
Records: 0  Duplicates: 0  Warnings: 0

將p2分區(4,9)拆分爲p2(4)分區和p3(9)分區

mysql> alter table expenses reorganize partition

p2 into (partition p2 values in (4),

partition p3 values in (9));
Query OK, 0 rows affected (0.05 sec)
Records: 0  Duplicates: 0  Warnings: 0

合併分區

ALTER TABLE REORGANIZE

PARTITION INTO

將p1、p2、p3三個分區合併爲一個分區p1(1995~2015)

mysql> alter table emp_date reorganize partition p1,p2,p3 into (partition p1 values less than (2015));
Query OK, 0 rows affected (0.04 sec)
Records: 0  Duplicates: 0  Warnings: 0

將p3分區(9)和p4分區(6)合併爲p3分區(9,6)

mysql> alter table expenses reorganize partition p3,p4 into (partition p3 values in (9,6));
Query OK, 0 rows affected (0.04 sec)
Records: 0  Duplicates: 0  Warnings: 0

 

注意:重新定義分區時(拆分、合併),只能夠定義

相鄰的分區,並且拆分或合併前後分區的覆蓋區間應

相同且連續;另外,不能通過重定義分區來更改分

區的類型,例如不可將range分區變爲hash分區。

注意:重新定義分區時(拆分、合併),只能夠定義

相鄰的分區,並且拆分或合併前後分區的覆蓋區間應相同;另外,不能通過重定義分區來更改分

區的類型,例如不可將list分區變爲hash分區。

  無法在原有分區上增加值的範圍,比如想在p2分區(2000~2005)上將範圍擴大是不可以的,但想在p4分區(2015~2030)上將範圍擴大是可行的;連續性要求 單純的使用add語句將p4 (6)調增爲p4 (6,11)是不可行的,但可以通過新增分區然後再合併的方式來達到這樣的目的。

3.2  HASH & KEY 分區管理

         在改變分區設置方面,HASH分區和KEY分區非常類似。

準備一個HASH分區表,分爲4個區:

create table emp(id INT not null,
                        ename varchar(30),
                        hired date not null default '1970-01-01',
                        separated date not null default '9999-12-31',
                        job varchar(30) not null,
                        store_id int not null) 
       partition by hash (store_id) partitions 4;

刪除兩個分區(其實也可以理解爲修改、合併),原有的4個分區變爲2個:ALTER TABLE COALESCE PARTITION

mysql> alter table emp coalesce partition 2;
Query OK, 0 rows affected (0.07 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> show create table emp \G
*************************** 1. row ***************************
       Table: emp
Create Table: CREATE TABLE `emp` (
  `id` int(11) NOT NULL,
  `ename` varchar(30) DEFAULT NULL,
  `hired` date NOT NULL DEFAULT '1970-01-01',
  `separated` date NOT NULL DEFAULT '9999-12-31',
  `job` varchar(30) NOT NULL,
  `store_id` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8
/*!50100 PARTITION BY HASH (store_id)
PARTITIONS 2 */
1 row in set (0.01 sec)

增加分區ALTER TABLE ADD PARTITION

mysql> alter table emp add partition partitions 8;
Query OK, 0 rows affected (0.12 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> show create table emp \G
*************************** 1. row ***************************
       Table: emp
Create Table: CREATE TABLE `emp` (
  `id` int(11) NOT NULL,
  `ename` varchar(30) DEFAULT NULL,
  `hired` date NOT NULL DEFAULT '1970-01-01',
  `separated` date NOT NULL DEFAULT '9999-12-31',
  `job` varchar(30) NOT NULL,
  `store_id` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8
/*!50100 PARTITION BY HASH (store_id)
PARTITIONS 10 */
1 row in set (0.00 sec)

          注意:刪除分區是在原有的基礎上刪除n個,比如上面的例子原有4個分區,刪除了兩個還剩2個分區;增加分區是在原有基礎上增加n個,比如上面的例子在原有2個分區的基礎上增加8個分區,因此一共有10個分區了。

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