MySQL 5.7 子分区

子分区
子分区(也称为复合分区)是分区表中每个分区的进一步划分。请看下面的CREATE TABLE语句:

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.02 sec)

表ts有3个范围分区。每个分区(p0、p1和p2)又进一步划分为2个子分区。实际上,整个表被分为3 * 2 = 6个分区。但是,由于PARTITION BY RANGE子句的作用,前2个只存储所购列中值小于1990的记录。

在MySQL 5.7中,可以对按范围或列表分区的表进行子分区。子分区可以使用哈希分区或键分区。这也称为复合分区。

也可以使用子分区子句显式定义子分区,为各个子分区指定选项。例如,在前面的例子中,创建相同的表ts的一种更冗长的方式是:

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

下面列出了一些语法注意事项:
.每个分区必须有相同数量的子分区。

.如果您在分区表的任何分区上使用SUBPARTITION显式地定义了任何子分区,则必须定义所有子分区。换句话说,下面的语句会失败:

mysql> CREATE TABLE ts1 (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
    -> )
    -> );
ERROR 1064 (42000): Wrong number of subpartitions defined, mismatch with previous setting near '
PARTITION p2 VALUES LESS THAN MAXVALUE (
SUBPARTITION s2,
SUBPARTITION s3
)
)' at line 8

即使包含SUBPARTITIONS 2子句,这条语句仍然会失败。

.每个子分区子句必须(至少)包含子分区的名称。否则,您可以为子分区设置任何想要的选项,或者允许它假定该选项的默认设置。

.子分区名在整个表中必须是唯一的。例如,下面的CREATE TABLE语句在MySQL 5.7中是有效的:

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
)
);

子分区可以用于特别大的MyISAM表,以便在许多磁盘上分发数据和索引。假设您有6个磁盘挂载为/disk0、/disk1、/disk2等。现在考虑下面的例子:

CREATE TABLE ts (id INT, purchased DATE)
ENGINE = MYISAM
PARTITION BY RANGE( YEAR(purchased) )
SUBPARTITION BY HASH( TO_DAYS(purchased) ) (
PARTITION p0 VALUES LESS THAN (1990) (
SUBPARTITION s0
DATA DIRECTORY = '/disk0/data'
INDEX DIRECTORY = '/disk0/idx',
SUBPARTITION s1
DATA DIRECTORY = '/disk1/data'
INDEX DIRECTORY = '/disk1/idx'
),
PARTITION p1 VALUES LESS THAN (2000) (
SUBPARTITION s2
DATA DIRECTORY = '/disk2/data'
INDEX DIRECTORY = '/disk2/idx',
SUBPARTITION s3
DATA DIRECTORY = '/disk3/data'
INDEX DIRECTORY = '/disk3/idx'
),
PARTITION p2 VALUES LESS THAN MAXVALUE (
SUBPARTITION s4
DATA DIRECTORY = '/disk4/data'
INDEX DIRECTORY = '/disk4/idx',
SUBPARTITION s5
DATA DIRECTORY = '/disk5/data'
INDEX DIRECTORY = '/disk5/idx'
)
);

在这种情况下,为每个RANGE的数据和索引使用一个单独的磁盘。许多其他的变化是可能的;另一个例子可能是:

CREATE TABLE ts (id INT, purchased DATE)
ENGINE = MYISAM
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年之前的行占用了大量的空间,因此以4种方式拆分,一个单独的磁盘专门用于存储数据和两个子分区(s0a和s0b)的索引,这 两个分区组成了p0分区。换句话说:
子分区s0a的数据存储在/disk0上。

.子分区s0a的数据存储在/disk0上。
.子分区s0a的索引存储在/disk1上。
.子分区s0b的数据存储在/disk2上。
.子分区s0b的索引存储在/disk3上。

.包含从1990年到1999年的日期的行(分区p1)不需要像1990年以前的行那样多的空间。它们被划分为2个磁盘(/disk4和/disk5),而不是像以 前那样存储在p0中的4个磁盘。

.属于p1的第一个子分区(s1a)的数据和索引存储在/disk4中,其中数据存储在/disk4/ Data中,索引存储在/disk4/idx中。
.属于p1的第二个子分区(s1b)的数据和索引存储在/disk5中——数据存储在/disk5/ Data中,索引存储在/disk5/idx中。

.反映2000年至今日期的行(分区p2)所占空间与前两个范围相比都没有那么大。目前,将所有这些都存储在默认位置就足够了。

将来,当从2000年开始的十年的购买数量增长到默认位置不再提供足够的空间时,可以使用ALTER TABLE … REORGANIZE PARTITION语句移动相 应的行。

MySQL 5.7 键分区

键分区
按键分区类似于按哈希分区,不同的是,哈希分区采用了用户定义的表达式,用于键分区的哈希函数由MySQL服务器提供。NDB集群使用MD5() 来实现这个目的。对于使用其他存储引擎的表,服务器使用自己的内部哈希函数,该函数基于与PASSWORD()相同的算法。

CREATE TABLE … PARTITION BY KEY的语法规则类似于按哈希分区的表。主要区别如下:
.使用KEY而不是HASH

.KEY只接受一个包含零个或多个列名的列表。任何用作分区键的列都必须包含表的主键(如果表有主键的话)的一部分或全部。如果没有指定列 名作为分区键,则使用表的主键(如果有主键的话)。例如,下面的CREATE TABLE语句在MySQL 5.7中是有效的:

mysql> CREATE TABLE k1 (
    -> id INT NOT NULL PRIMARY KEY,
    -> name VARCHAR(20)
    -> )
    -> PARTITION BY KEY()
    -> PARTITIONS 2;
Query OK, 0 rows affected (0.01 sec)

如果没有主键,但有一个唯一键,那么这个唯一键用于分区键:

mysql> CREATE TABLE k1 (
    -> id INT NOT NULL,
    -> name VARCHAR(20),
    -> UNIQUE KEY (id)
    -> )
    -> PARTITION BY KEY()
    -> PARTITIONS 2;
Query OK, 0 rows affected (0.01 sec)

但是,如果唯一键列没有定义为not NULL,那么前面的语句将失败。

在这两种情况下,分区键都是id列,尽管它没有显示在SHOW CREATE TABLE的输出中或INFORMATION_SCHEMA.PARTITIONS表的 PARTITION_EXPRESSION列中。

与其他分区类型不同的是,用于按键分区的列并不局限于整数值或NULL值。例如,下面的CREATE TABLE语句是有效的:

mysql> CREATE TABLE tm1 (
    -> s1 CHAR(32) PRIMARY KEY
    -> )
    -> PARTITION BY KEY(s1)
    -> PARTITIONS 10;
Query OK, 0 rows affected (0.04 sec)

如果指定了不同的分区类型,上述语句将无效。(在这种情况下,简单地使用按键分区()也是有效的,并且与按键分区(s1)的效果相同,因 为s1是表的主键。)

也可以通过线性键对表进行分区。这里有一个简单的例子:

mysql> CREATE TABLE tk (
    -> col1 INT NOT NULL,
    -> col2 CHAR(5),
    -> col3 DATE
    -> )
    -> PARTITION BY LINEAR KEY (col1)
    -> PARTITIONS 3;
Query OK, 0 rows affected (0.02 sec)

使用LINEAR对键分区的效果与对哈希分区的效果相同,分区数是使用2的幂算法而不是模运算得到的。

MySQL 5.7 哈希分区

哈希分区
哈希分区主要用于确保数据在预定数量的分区中均匀分布。使用范围分区或列表分区时,用户必须明确指定给定的列值或一组列值存储在哪个分 区中;使用哈希分区时,MySQL会帮你处理这个问题,你只需要根据要哈希的列值和要划分的分区数指定一个列值或表达式。

要使用哈希分区对表进行分区,需要在CREATE table语句中添加一个partition BY HASH (expr)子句,其中expr是一个返回整数的表达式。这 可以是MySQL整数类型的列的名称。此外,你很可能想在接下来使用分区num,其中num是一个正整数,表示表被划分的分区数。

为简单起见,下面示例中的表不使用任何键。你应该注意,如果一张表有任何唯一键,那么在这个表的分区表达式中使用的每一列都必须是每个 唯一键的一部分,包括主键。

下面的语句创建了一个在store_id列上使用哈希的表,它被分为4个分区:

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

如果不包含PARTITIONS子句,则分区的数量默认为1。

使用PARTITIONS关键字而后面没有数字会导致语法错误。

你也可以使用在expr中返回整数的SQL表达式。例如,你可能想根据雇员受雇的年份进行分区。可以这样做:

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

expr必须返回一个非常量、非随机的整数值(换句话说,它应该是可变的,但是确定的),而且不能包含任何禁止使用的结构。你还应该记住, 每当插入或更新(或删除)一行时,这个表达式都会被计算;这意味着非常复杂的表达式可能会导致性能问题,特别是在执行一次影响很多行的 操作(如批量插入)时。

最高效的散列函数是对表中的单个列进行操作,其值随列值的增加或减少而增加或减少,因为这允许在分区范围上进行“修剪”。也就是说,该 表达式与所基于的列的值变化越密切,MySQL使用该表达式进行哈希分区的效率就越高。

例如,如果 date_col 是 DATE 类型的列,那么表达式 TO_DAYS(date_col) 就被认为与 date_col 的值成正比变化,因为 date_col 的值每发 生一次变化,该表达式的值都会以一致的方式变化。表达式 YEAR(date_col) 相对于 date_col 的变化不像 TO_DAYS(date_col) 那样直接,因 为 date_col 的每个可能变化都不会在 YEAR(date_col) 中产生同等的变化。即便如此,YEAR(date_col) 仍是一个不错的哈希函数候选,因为 它与 date_col 的一部分成正比变化,并且 date_col 的任何变化都不会在 YEAR(date_col) 中产生不成比例的变化。

相比之下,假设你有一个名为int_col的列,其类型为INT。现在考虑表达式POW(5-int_col,3) + 6。对于哈希函数来说,这是一个糟糕的选择, 因为改变int_col的值并不一定会导致表达式的值成比例地改变。将int_col的值改变一个给定的值,可能会导致表达式的值发生很大不同的变化 。例如,将int_col从5改为6,表达式的值会改变-1,而将int_col的值从6改为7,表达式的值会改变-7。

换句话说,列值的图形与表达式的值越接近,就像方程y=cx所示的直线,其中c是某个非零常数,表达式越适合散列。这是因为表达式越非线性 ,其产生的分区之间的数据分布就越不均匀。

理论上,对于涉及多个列值的表达式,也可以进行“修剪”,但确定哪一个表达式适合是非常困难和耗时的。因此,不推荐使用涉及多列的散列 表达式。

当使用PARTITION BY HASH时,MySQL根据表达式结果的模值来确定使用num个分区中的哪个分区。换句话说,对于给定的表达式expr,存储记录 的分区是分区号N,其中N = MOD(expr, num)。假设表t1定义如下,它有4个分区:

mysql> CREATE TABLE t1 (col1 INT, col2 CHAR(5), col3 DATE)
    -> PARTITION BY HASH( YEAR(col3) )
    -> PARTITIONS 4;
Query OK, 0 rows affected (0.03 sec)

如果你在t1中插入一条col3值为‘2005-09-15’的记录,那么存储它的分区如下所示:

MOD(YEAR('2005-09-01'),4)
= MOD(2005,4)
= 1

MySQL 5.7还支持哈希分区的一种变体,称为线性哈希,它采用一种更复杂的算法来确定插入到分区表中的新行的位置。

每次插入或更新记录时,都会对用户提供的表达式进行计算。根据具体情况,还可以在删除记录时对其进行评估。

线性哈希分区
MySQL还支持线性哈希,它与常规哈希的不同之处在于,线性哈希利用线性2次幂算法,而常规哈希使用哈希函数值的模数。

从语法上讲,线性哈希分区和常规哈希之间的唯一区别是添加了在PARTITION BY子句中添加LINEAR关键字,如下所示:

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

给定表达式expr,使用线性哈希时存储记录的分区为num个分区中的第N个分区,其中N根据以下算法推导:
1.求下一个大于num的2次幂,我们称这个值为V;可计算为:

V = POWER(2, CEILING(LOG(2, num)))

(假设num是13。那么LOG(2,13)为3.7004397181411。CEILING(3.7004397181411)等于4,V = POWER(2,4),等于16。

2.设N = F(column_list) & (V – 1)。

3.当N >= num:
.设V = V / 2
.设N = N & (V – 1)

假设表t1使用线性哈希分区,有6个分区,使用下面的语句创建:

mysql> CREATE TABLE t1 (col1 INT, col2 CHAR(5), col3 DATE)
    -> PARTITION BY LINEAR HASH( YEAR(col3) )
    -> PARTITIONS 6;
Query OK, 0 rows affected (0.03 sec)

现在假设你想在t1中插入两条col3列值为‘2003-04-14’和‘1998-10-19’的记录。第一个分区的分区号确定如下:

V = POWER(2, CEILING( LOG(2,6) )) = 8
N = YEAR('2003-04-14') & (8 - 1)
= 2003 & 7
= 3
(3 >= 6 is FALSE: record stored in partition #3)

存储第二条记录的分区编号计算如下所示:

V = 8
N = YEAR('1998-10-19') & (8 - 1)
= 1998 & 7
= 6
(6 >= 6 is TRUE: additional step required)
N = 6 & ((8 / 2) - 1)
= 6 & 3
= 2


(2 >= 6 is FALSE: record stored in partition #2)

通过线性哈希进行分区的优点是,分区的添加、删除、合并和拆分都快得多,这在处理包含大量(tb级)数据的表时非常有用。缺点是,与使用 常规哈希分区获得的分布相比,数据不太可能在分区之间均匀分布。

MySQL 5.7 列表列分区

列表列分区
MySQL 5.7支持列表列分区。这是列表分区的一个变体,允许使用多个列作为分区键,并允许非整数类型的数据类型用作分区列;你可以使用字 符串类型、DATE和DATETIME列。

使用列表列分区,用户可以为客户数据创建一个表,根据客户所在城市的名称,将一行分配给对应于这些区域的4个分区中的任何一个,如下所示:

mysql> CREATE TABLE customers_1 (
    -> first_name VARCHAR(25),
    -> last_name VARCHAR(25),
    -> street_1 VARCHAR(30),
    -> street_2 VARCHAR(30),
    -> city VARCHAR(15),
    -> renewal DATE
    -> )
    -> PARTITION BY LIST COLUMNS(city) (
    -> PARTITION pRegion_1 VALUES IN('Oskarshamn', 'H?gsby', 'M?nster?s'),
    -> PARTITION pRegion_2 VALUES IN('Vimmerby', 'Hultsfred', 'V?stervik'),
    -> PARTITION pRegion_3 VALUES IN('N?ssj?', 'Eksj?', 'Vetlanda'),
    -> PARTITION pRegion_4 VALUES IN('Uppvidinge', 'Alvesta', 'V?xjo')
    -> );
Query OK, 0 rows affected (0.04 sec)

与按范围分区列一样,你不需要在COLUMNS()子句中使用表达式将列值转换为整数。(实际上,COLUMNS()不允许使用列名以外的表达式。)

也可以使用DATE和DATETIME列,如下面的例子所示,它们的名称和列与之前的customers_1表相同,但使用基于renew列的列表列分区将行存储在 4个分区中的一个中,具体取决于2010年2月客户账户计划更新的那一周:

mysql> CREATE TABLE customers_2 (
    -> first_name VARCHAR(25),
    -> last_name VARCHAR(25),
    -> street_1 VARCHAR(30),
    -> street_2 VARCHAR(30),
    -> city VARCHAR(15),
    -> renewal DATE
    -> )
    -> PARTITION BY LIST COLUMNS(renewal) (
    -> PARTITION pWeek_1 VALUES IN('2010-02-01', '2010-02-02', '2010-02-03',
    -> '2010-02-04', '2010-02-05', '2010-02-06', '2010-02-07'),
    -> PARTITION pWeek_2 VALUES IN('2010-02-08', '2010-02-09', '2010-02-10',
    -> '2010-02-11', '2010-02-12', '2010-02-13', '2010-02-14'),
    -> PARTITION pWeek_3 VALUES IN('2010-02-15', '2010-02-16', '2010-02-17',
    -> '2010-02-18', '2010-02-19', '2010-02-20', '2010-02-21'),
    -> PARTITION pWeek_4 VALUES IN('2010-02-22', '2010-02-23', '2010-02-24',
    -> '2010-02-25', '2010-02-26', '2010-02-27', '2010-02-28')
    -> );
Query OK, 0 rows affected (0.02 sec)

这是可行的,但如果涉及的日期数量非常大,则定义和维护起来就很麻烦。在这种情况下,采用范围或范围列分区通常更实用。在本例中,由于 我们希望用作分区键的列是日期列,所以我们使用范围列分区,如下所示:

mysql> CREATE TABLE customers_3 (
    -> first_name VARCHAR(25),
    -> last_name VARCHAR(25),
    -> street_1 VARCHAR(30),
    -> street_2 VARCHAR(30),
    -> city VARCHAR(15),
    -> renewal DATE
    -> )
    -> PARTITION BY RANGE COLUMNS(renewal) (
    -> PARTITION pWeek_1 VALUES LESS THAN('2010-02-09'),
    -> PARTITION pWeek_2 VALUES LESS THAN('2010-02-15'),
    -> PARTITION pWeek_3 VALUES LESS THAN('2010-02-22'),
    -> PARTITION pWeek_4 VALUES LESS THAN('2010-03-01')
    -> );
Query OK, 0 rows affected (0.03 sec)

此外(与范围列分区一样),你可以在COLUMNS()子句中使用多个列。

MySQL 5.7 范围列分区

范围列分区
范围列分区类似于范围分区,但允许您使用基于多个列值的范围来定义分区。此外,还可以使用非整数类型的列来定义范围。

范围列分区与范围分区在以下几个方面有很大的不同:
.范围列分区不接受表达式,只接受列名。

.范围列分区接受一个或多个列的列表。
范围列分区基于元组(列值列表)之间的比较,而不是标量值之间的比较。在范围列分区中行的放置也基于元组之间的比较;本节后面将进一步 讨论这一点。

.范围列分区列不限制为整数列;string、DATE和DATETIME列也可以用作分区列。

创建由范围列分区的表的基本语法如下所示:

CREATE TABLE table_name
PARTITIONED BY RANGE COLUMNS(column_list) (
PARTITION partition_name VALUES LESS THAN (value_list)[,
PARTITION partition_name VALUES LESS THAN (value_list)][,
...]
)
column_list:
column_name[, column_name][, ...]
value_list:
value[, value][, ...]

在上述语法中,column_list是一个包含一个或多个列的列表(有时称为分区列列表),value_list是一个值列表(也就是说,它是一个分区定 义值列表)。每个分区定义都必须提供一个value_list,每个value_list的值数量必须与column_list的列数量相同。一般来说,如果在columns 子句中使用N列,那么每个VALUES小于子句也必须提供N个值的列表。

分区列列表中的元素以及定义每个分区的值列表中的元素必须按相同的顺序出现。此外,值列表中的每个元素必须与列列表中对应的元素具有相 同的数据类型。不过,分区列列表中的列名顺序以及值列表中的顺序不必与 CREATE TABLE 语句主部分中的表列定义顺序相同。与按范围分区的 表一样,您可以使用 MAXVALUE 来表示一个值,使得插入给定列中的任何合法值始终小于此值。以下是一个 CREATE TABLE 语句的示例,有助于 说明所有这些要点:

mysql> CREATE TABLE rcx (
    -> a INT,
    -> b INT,
    -> c CHAR(3),
    -> d INT
    -> )
    -> PARTITION BY RANGE COLUMNS(a,d,c) (
    -> PARTITION p0 VALUES LESS THAN (5,10,'ggg'),
    -> PARTITION p1 VALUES LESS THAN (10,20,'mmm'),
    -> PARTITION p2 VALUES LESS THAN (15,30,'sss'),
    -> PARTITION p3 VALUES LESS THAN (MAXVALUE,MAXVALUE,MAXVALUE)
    -> );
Query OK, 0 rows affected (0.03 sec)

rcx表包含a、b、c、d列。提供给columns子句的分区列列表使用了其中3个列,按a、d、c的顺序。每个用于定义分区的值列表包含3个相同顺序 的值;也就是说,每个值列表元组的形式都是(INT, INT, CHAR(3)),对应于a、d、c列使用的数据类型(按此顺序)。

将行放置到分区中是通过比较将要插入的行的元组来确定的,该元组与COLUMNS子句中的列列表匹配,并用于定义表的分区的VALUES LESS THAN 子句。因为我们比较的是元组(即列表或值的集合)而不是标量值,所以范围列分区使用的values LESS than的语义与简单范围分区的情况有所 不同。在范围分区中,生成的表达式值等于VALUES小于中的一个极限值的行永远不会被放置到相应的分区中;然而,当使用范围列分区时,有时 可能会出现这样的情况:分区列列表的第一个元素的值与小于值列表的第一个元素的值相等,那么就被放置到相应的分区中。

考虑下面这个语句创建的范围分区表

mysql> CREATE TABLE r1 (
    -> a INT,
    -> b INT
    -> )
    -> PARTITION BY RANGE (a) (
    -> PARTITION p0 VALUES LESS THAN (5),
    -> PARTITION p1 VALUES LESS THAN (MAXVALUE)
    -> );
Query OK, 0 rows affected (0.02 sec)

如果我们在这个表中插入3行,使得a的每一行的列值为5,那么所有3行都存储在分区p1中,因为在每种情况下a列的值都不小于5,这可以通过对 INFORMATION_SCHEMA执行适当的查询来看到。分区表:

mysql> INSERT INTO r1 VALUES (5,10), (5,11), (5,12);
Query OK, 3 rows affected (0.00 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql> SELECT PARTITION_NAME,TABLE_ROWS
    -> FROM INFORMATION_SCHEMA.PARTITIONS
    -> WHERE TABLE_NAME = 'r1';
+----------------+------------+
| PARTITION_NAME | TABLE_ROWS |
+----------------+------------+
| p0             |          0 |
| p1             |          3 |
+----------------+------------+
2 rows in set (0.00 sec)

现在考虑一个类似的表rc1,它使用范围列分区,在COLUMNS子句中同时引用a和b列,如下所示:

mysql> CREATE TABLE rc1 (
    -> a INT,
    -> b INT
    -> )
    -> PARTITION BY RANGE COLUMNS(a, b) (
    -> PARTITION p0 VALUES LESS THAN (5, 12),
    -> PARTITION p3 VALUES LESS THAN (MAXVALUE, MAXVALUE)
    -> );
Query OK, 0 rows affected (0.01 sec)

如果我们在rc1中插入与r1中完全相同的行,那么行的分布就会大不相同:

mysql> INSERT INTO rc1 VALUES (5,10), (5,11), (5,12);
Query OK, 3 rows affected (0.00 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql> SELECT PARTITION_NAME,TABLE_ROWS
    -> FROM INFORMATION_SCHEMA.PARTITIONS
    -> WHERE TABLE_NAME = 'rc1';
+----------------+------------+
| PARTITION_NAME | TABLE_ROWS |
+----------------+------------+
| p0             |          2 |
| p3             |          1 |
+----------------+------------+
2 rows in set (0.00 sec)

这是因为我们比较的是行而不是标量值。我们可以将插入的行值与表rc1中用于定义分区p0的values THAN LESS THAN子句中的限制行值进行比较 ,如下所示:

mysql> SELECT (5,10) < (5,12), (5,11) < (5,12), (5,12) < (5,12);
+-----------------+-----------------+-----------------+
| (5,10) < (5,12) | (5,11) < (5,12) | (5,12) < (5,12) |
+-----------------+-----------------+-----------------+
|               1 |               1 |               0 |
+-----------------+-----------------+-----------------+
1 row in set (0.00 sec)

两个元组(5,10)和(5,11)的值小于(5,12),因此它们被存储在p0分区中。由于5不小于5,12不小于12,因此(5,12)被认为不小于(5,12 ),并存储在分区p1中。

上例中的SELECT语句也可以使用显式的行构造函数编写,如下所示:

mysql> SELECT ROW(5,10) < ROW(5,12), ROW(5,11) < ROW(5,12), ROW(5,12) < ROW(5,12);
+-----------------------+-----------------------+-----------------------+
| ROW(5,10) < ROW(5,12) | ROW(5,11) < ROW(5,12) | ROW(5,12) < ROW(5,12) |
+-----------------------+-----------------------+-----------------------+
|                     1 |                     1 |                     0 |
+-----------------------+-----------------------+-----------------------+
1 row in set (0.00 sec)

对于只使用单个分区列的按范围列分区的表,在分区中存储行与按范围分区的表是一样的。下面的CREATE TABLE语句使用1个分区列创建一个按 范围列分区的表:

mysql> CREATE TABLE rx (
    -> a INT,
    -> b INT
    -> )
    -> PARTITION BY RANGE COLUMNS (a) (
    -> PARTITION p0 VALUES LESS THAN (5),
    -> PARTITION p1 VALUES LESS THAN (MAXVALUE)
    -> );
Query OK, 0 rows affected (0.01 sec)

如果我们将(5,10),(5,11)和(5,12)行插入到这个表中,我们可以看到它们的位置与我们之前创建和填充的表r的位置相同:

mysql> INSERT INTO rx VALUES (5,10), (5,11), (5,12);
Query OK, 3 rows affected (0.01 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql> SELECT PARTITION_NAME,TABLE_ROWS
    -> FROM INFORMATION_SCHEMA.PARTITIONS
    -> WHERE TABLE_NAME = 'rx';
+----------------+------------+
| PARTITION_NAME | TABLE_ROWS |
+----------------+------------+
| p0             |          0 |
| p1             |          3 |
+----------------+------------+
2 rows in set (0.00 sec)

还可以创建按范围列进行分区的表,其中一个或多个列的限制值在连续的分区定义中重复出现。只要用于定义分区的列值元组严格递增,就可以 这样做。例如,下面的CREATE TABLE语句都是有效的。

mysql> CREATE TABLE rc2 (
    -> a INT,
    -> b INT
    -> )
    -> PARTITION BY RANGE COLUMNS(a,b) (
    -> PARTITION p0 VALUES LESS THAN (0,10),
    -> PARTITION p1 VALUES LESS THAN (10,20),
    -> PARTITION p2 VALUES LESS THAN (10,30),
    -> PARTITION p3 VALUES LESS THAN (MAXVALUE,MAXVALUE)
    -> );
Query OK, 0 rows affected (0.01 sec)

mysql> CREATE TABLE rc3 (
    -> a INT,
    -> b INT
    -> )
    -> PARTITION BY RANGE COLUMNS(a,b) (
    -> PARTITION p0 VALUES LESS THAN (0,10),
    -> PARTITION p1 VALUES LESS THAN (10,20),
    -> PARTITION p2 VALUES LESS THAN (10,30),
    -> PARTITION p3 VALUES LESS THAN (10,35),
    -> PARTITION p4 VALUES LESS THAN (20,40),
    -> PARTITION p5 VALUES LESS THAN (MAXVALUE,MAXVALUE)
    -> );
Query OK, 0 rows affected (0.02 sec)

下面的语句也成功了,尽管乍一看可能不会成功,因为分区p0的b列的极限值是25,分区p1的极限值是20,分区p1的c列的极限值是100,分区p2 的极限值是50:

mysql> CREATE TABLE rc4 (
    -> a INT,
    -> b INT,
    -> c INT
    -> )
    -> PARTITION BY RANGE COLUMNS(a,b,c) (
    -> PARTITION p0 VALUES LESS THAN (0,25,50),
    -> PARTITION p1 VALUES LESS THAN (10,20,100),
    -> PARTITION p2 VALUES LESS THAN (10,30,50),
    -> PARTITION p3 VALUES LESS THAN (MAXVALUE,MAXVALUE,MAXVALUE)
    -> );
Query OK, 0 rows affected (0.02 sec)

在设计按范围列进行分区的表时,你总是可以使用mysql客户端比较所需的元组来测试连续的分区定义,如下所示:

mysql> SELECT (0,25,50) < (10,20,100), (10,20,100) < (10,30,50);
+-------------------------+--------------------------+
| (0,25,50) < (10,20,100) | (10,20,100) < (10,30,50) |
+-------------------------+--------------------------+
|                       1 |                        1 |
+-------------------------+--------------------------+
1 row in set (0.00 sec)

如果CREATE TABLE语句包含的分区定义不是严格按递增顺序排列的,则会失败并报错,如下例所示:

mysql> CREATE TABLE rcf (
    -> a INT,
    -> b INT,
    -> c INT
    -> )
    -> PARTITION BY RANGE COLUMNS(a,b,c) (
    -> PARTITION p0 VALUES LESS THAN (0,25,50),
    -> PARTITION p1 VALUES LESS THAN (20,20,100),
    -> PARTITION p2 VALUES LESS THAN (10,30,50),
    -> PARTITION p3 VALUES LESS THAN (MAXVALUE,MAXVALUE,MAXVALUE)
    -> );
ERROR 1493 (HY000): VALUES LESS THAN value must be strictly increasing for each partition

当出现这样的错误时,您可以通过在列列表之间进行“小于”比较来推断哪些分区定义是无效的。在这种情况下,问题出在分区p2的定义上,因 为用于定义它的元组不小于用于定义分区p3的元组,如下所示:

mysql> SELECT (0,25,50) < (20,20,100), (20,20,100) < (10,30,50);
+-------------------------+--------------------------+
| (0,25,50) < (20,20,100) | (20,20,100) < (10,30,50) |
+-------------------------+--------------------------+
|                       1 |                        0 |
+-------------------------+--------------------------+
1 row in set (0.00 sec)

在使用范围列时,同一列的MAXVALUE也可以出现在多个VALUES LESS THAN子句中。然而,在连续的分区定义中,单个列的限制值应该增加,不应 该有超过一个分区定义中有MAXVALUE,它用作所有列值的上限,该分区定义应该出现在分区列表的PARTITION ... VALUES LESS THAN子句的最后 。此外,您不能在多个分区定义中使用MAXVALUE作为第一列的限制值。

如前所述,范围列分区也可以使用非整数列作为分区列。考虑一个名为employees的表(未分区),使用以下语句创建:

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
);

使用范围列分区,你可以创建这个表的一个版本,根据员工的姓氏将每一行存储在4个分区中的一个中,如下所示:

mysql> CREATE TABLE employees_by_lname (
    -> 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 COLUMNS (lname) (
    -> PARTITION p0 VALUES LESS THAN ('g'),
    -> PARTITION p1 VALUES LESS THAN ('m'),
    -> PARTITION p2 VALUES LESS THAN ('t'),
    -> PARTITION p3 VALUES LESS THAN (MAXVALUE)
    -> );
Query OK, 0 rows affected (0.02 sec)

或者,用户也可以通过执行下面的ALTER TABLE语句,对前面创建的employees表进行分区:

mysql> ALTER TABLE employees PARTITION BY RANGE COLUMNS (lname) (
    -> PARTITION p0 VALUES LESS THAN ('g'),
    -> PARTITION p1 VALUES LESS THAN ('m'),
    -> PARTITION p2 VALUES LESS THAN ('t'),
    -> PARTITION p3 VALUES LESS THAN (MAXVALUE)
    -> );
Query OK, 0 rows affected (0.03 sec)
Records: 0  Duplicates: 0  Warnings: 0

由于不同的字符集和排序规则具有不同的排序顺序,当使用字符串列作为分区列时,使用的字符集和排序规则可能会影响给定行的按范围列分区 的表的哪个分区。此外,在创建给定的数据库、表或列之后,更改其字符集或排序规则可能会导致行分布方式的更改。例如,当使用区分大小写 的排序规则时,` and `排在` Andersen `之前,但当使用不区分大小写的排序规则时,则相反。

类似地,用户可以使用下面的ALTER table语句对employees表进行分区,根据雇员的年龄,将表中的每一行存储在几个分区中的一个中:

mysql> ALTER TABLE employees PARTITION BY RANGE COLUMNS (hired) (
    -> PARTITION p0 VALUES LESS THAN ('1970-01-01'),
    -> PARTITION p1 VALUES LESS THAN ('1980-01-01'),
    -> PARTITION p2 VALUES LESS THAN ('1990-01-01'),
    -> PARTITION p3 VALUES LESS THAN ('2000-01-01'),
    -> PARTITION p4 VALUES LESS THAN ('2010-01-01'),
    -> PARTITION p5 VALUES LESS THAN (MAXVALUE)
    -> );
Query OK, 0 rows affected (0.04 sec)
Records: 0  Duplicates: 0  Warnings: 0

MySQL 5.7 列表分区

列表分区
MySQL中的列表分区在很多方面与范围分区类似。与按范围分区一样,每个分区都必须显式定义。这两种分区的主要区别在于,在列表分区中, 每个分区是根据列值在一组值列表中的成员关系定义和选择的,而不是根据一组连续范围的值的成员关系。这是通过使用分区列表(expr)来完 成的,其中expr是一个列值或一个基于列值并返回整数值的表达式,然后通过(value_list)中的值来定义每个分区,其中value_list是一个逗 号分隔的整数值列表。与由range定义分区的情况不同,列表分区不需要以任何特定顺序声明。

对于下面的示例,我们假设要分区的表的基本定义由如下所示的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
);

要对表进行分区,使属于同一个区域的数据行存储在同一个分区中,用户可以使用下面的CREATE table语句:

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

这使得向表中添加或删除特定区域的员工记录变得容易。例如,假设西部地区的所有门店都卖给了另一家公司。在 MySQL 5.7 中,与该地区门 店员工相关的所有行都可以通过执行 ALTER TABLE employees TRUNCATE PARTITION pWest 这个查询来删除,这比执行等效的 DELETE 语句 DELETE FROM employees WHERE store_id IN (4,12,13,14,18); 要高效得多。(使用 ALTER TABLE employees DROP PARTITION pWest 也 会删除所有这些行,但还会从表的定义中移除分区 pWest;您需要使用 ALTER TABLE…ADD PARTITION 语句来恢复表的原始分区方案。)

与范围分区一样,也可以将列表分区与按哈希或键分区结合起来,以生成复合分区(子分区)。

与范围分区不同,这里没有诸如 MAXVALUE 这样的“万能”选项;分区表达式的所有预期值都应在 PARTITION … VALUES IN (…) 子句中涵 盖。包含未匹配分区列值的 INSERT 语句会因错误而失败,如下例所示:

mysql> CREATE TABLE h2 (
    ->   c1 INT,
    ->   c2 INT
    ->   )
    ->   PARTITION BY LIST(c1) (
    ->   PARTITION p0 VALUES IN (1, 4, 7),
    ->   PARTITION p1 VALUES IN (2, 5, 8)
    -> );
Query OK, 0 rows affected (0.01 sec)

mysql> INSERT INTO h2 VALUES (3, 5);
ERROR 1526 (HY000): Table has no partition for value 3

使用单个 INSERT 语句插入多行时,其行为取决于表是否使用事务存储引擎。对于 InnoDB 表,该语句被视为单个事务,因此任何不匹配的值的 存在都会导致整个语句完全失败,且不会插入任何行。对于使用非事务存储引擎(如 MyISAM)的表,包含不匹配值的行之前的任何行都会被插 入,但之后的行则不会。

使用IGNORE关键字可以忽略这种类型的错误。如果这样做,包含不匹配的分区列值的行不会被插入,但是包含匹配值的行会被插入,并且不会报告错误:

mysql> TRUNCATE h2;
Query OK, 0 rows affected (0.01 sec)

mysql> SELECT * FROM h2;
Empty set (0.00 sec)

mysql> INSERT IGNORE INTO h2 VALUES (2, 5), (6, 10), (7, 5), (3, 1), (1, 9);
Query OK, 3 rows affected, 2 warnings (0.00 sec)
Records: 5  Duplicates: 2  Warnings: 2

mysql> SELECT * FROM h2;
+------+------+
| c1   | c2   |
+------+------+
|    7 |    5 |
|    1 |    9 |
|    2 |    5 |
+------+------+
3 rows in set (0.00 sec)

MySQL 5.7支持列表列分区。这是列表分区的一个变体,允许您使用整数类型以外的其他类型的列进行分区,也可以使用多个列作为分区键。

MySQL 5.7 范围分区

范围分区
按范围分区的表的分区方式是这样的:每个分区包含分区表达式值位于给定范围内的行。范围应该是连续的但不重叠的,并且使用VALUES LESS THAN操作符定义。在下面的几个例子中,假设您要创建一个表,如下面所示,用于保存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
);

这里使用的employees表没有主键或唯一键。虽然这些例子的效果与当前讨论的目的一致,但你应该记住,表在实践中很可能有主键、唯一键, 或者两者都有,如果存在分区列,那么是否允许分区列取决于用于这些键的列。

根据用户的需要,可以通过多种方式对该表进行分区。一种方法是使用store_id列。例如,你可能决定用4种方式对表进行分区,比如添加一个 partition by RANGE子句,如下所示:

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

在这个分区方案中,在商店1 ~ 5工作的员工所对应的所有行存储在p0分区中,在商店6 ~ 10工作的员工所对应的行存储在p1分区中,以此类推 。注意,每个分区都是按从低到高的顺序定义的。这是分区范围语法的要求。你可以把它想象成C或Java中的一系列的if…elseif语句。

我们很容易就能确定要将包含数据(72,’Mitchell’, ‘Wilson’, ‘1998-06-25’, NULL, 13)的新行插入到分区p2中,但是当链增加了第21个存 储时会发生什么呢?在这种模式下,没有规则覆盖store_id大于20的行,因此会报错,因为服务器不知道把它放在哪里。为了避免这种情况,可 以在CREATE TABLE语句中使用包罗万象的VALUES LESS THAN子句,该子句提供所有大于显式指定的最大值的值:

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

另一种避免在找不到匹配值时出错的方法是在INSERT语句中使用IGNORE关键字。

MAXVALUE表示一个整数值,总是大于可能的最大整数值(在数学语言中,它用作最小上界)。现在,任何store_id列值大于或等于16(定义的最 大值)的行都存储在分区p3中。在未来的某个时刻,当存储的数量增加到25、30或更多时,用户可以使用ALTER TABLE语句为存储21 ~ 25、26 ~ 30等添加新的分区(有关如何操作的细节,请参见22.3节)。

类似地,用户也可以根据员工的工作代码对表进行分区,也就是说,根据job_code列值的范围进行分区。例如,假设两位数的工作代码用于普通 (店内)员工,三位的工作代码用于办公室和技术支持人员,四位的工作代码用于管理职位,可以使用以下语句创建分区表:

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

在本例中,所有与店内员工相关的行都存储在p0分区中,与办公室和支持人员相关的行存储在p1分区中,与经理相关的行存储在p2分区中。

也可以在VALUES LESS THAN子句中使用表达式。然而,MySQL必须能够将表达式的返回值作为小于(< )比较的一部分进行计算。 您可以使用基于两个日期列之一的表达式,而不是根据存储编号拆分表数据。例如,假设你希望根据每个员工离开公司的年份进行划分;也就是 YEAR(separated)。下面是一个实现这种分区方案的CREATE TABLE语句的例子:

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


在本方案中,对于1991年以前离职的所有员工,所有行都存储在p0分区中;对于1991年到1995年离开的人,p1;对于1996年到2000年离开的人, 在p2;对于2000年后离开的工人,也就是p3。

也可以根据时间戳列的值对表进行范围分区,使用UNIX_TIMESTAMP()函数,如下面的例子所示。
mysql> CREATE TABLE quarterly_report_status (
    -> report_id INT NOT NULL,
    -> report_status VARCHAR(20) NOT NULL,
    -> report_updated TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
    -> )
    -> PARTITION BY RANGE ( UNIX_TIMESTAMP(report_updated) ) (
    -> PARTITION p0 VALUES LESS THAN ( UNIX_TIMESTAMP('2008-01-01 00:00:00') ),
    -> PARTITION p1 VALUES LESS THAN ( UNIX_TIMESTAMP('2008-04-01 00:00:00') ),
    -> PARTITION p2 VALUES LESS THAN ( UNIX_TIMESTAMP('2008-07-01 00:00:00') ),
    -> PARTITION p3 VALUES LESS THAN ( UNIX_TIMESTAMP('2008-10-01 00:00:00') ),
    -> PARTITION p4 VALUES LESS THAN ( UNIX_TIMESTAMP('2009-01-01 00:00:00') ),
    -> PARTITION p5 VALUES LESS THAN ( UNIX_TIMESTAMP('2009-04-01 00:00:00') ),
    -> PARTITION p6 VALUES LESS THAN ( UNIX_TIMESTAMP('2009-07-01 00:00:00') ),
    -> PARTITION p7 VALUES LESS THAN ( UNIX_TIMESTAMP('2009-10-01 00:00:00') ),
    -> PARTITION p8 VALUES LESS THAN ( UNIX_TIMESTAMP('2010-01-01 00:00:00') ),
    -> PARTITION p9 VALUES LESS THAN (MAXVALUE)
    -> );
Query OK, 0 rows affected (0.05 sec)

不允许使用任何其他涉及TIMESTAMP值的表达式。(参见Bug #42849。)

当满足以下一个或多个条件时,范围分区特别有用:
.你想要或者需要删除“旧”数据。如果用户使用前面给出的employees表分区方案,可以使用ALTER table employees drop partition p0语句 ;删除与1991年之前停止为公司工作的员工的所有行。对于包含很多行的表,这比执行DELETE查询(例如DELETE FROM employees WHERE YEAR (separated) < = 1990;)要高效。 .您希望使用包含日期或时间值的列,或者包含从其他系列产生的值。 .用户经常会执行直接依赖于用于分区表的列的查询。例如,当执行类似于EXPLAIN SELECT COUNT(*) FROM employees WHERE BETWEEN '2000- 01-01' AND '2000-12-31' GROUP BY store_id;的查询时,MySQL可以很快确定只有分区p2需要被扫描,因为剩余的分区不能包含任何满足 WHERE子句的记录。 这种分区的一种变体是范围列分区。根据范围列进行分区使得可以使用多个列来定义分区范围,这些范围既适用于分区中的行位置,也适用于在 执行分区修剪时确定包含或排除特定的分区。 基于时间间隔的分区方案。如果你想在MySQL 5.7中实现一个基于范围或时间间隔的分区方案,你有两个选择: 1.按范围对表进行分区,对于分区表达式,使用一个操作DATE、TIME或DATETIME列并返回整数值的函数,如下所示:

mysql> CREATE TABLE members (
    -> firstname VARCHAR(25) NOT NULL,
    -> lastname VARCHAR(25) NOT NULL,
    -> username VARCHAR(16) NOT NULL,
    -> email VARCHAR(35),
    -> joined DATE NOT NULL
    -> )
    -> PARTITION BY RANGE( YEAR(joined) ) (
    -> PARTITION p0 VALUES LESS THAN (1960),
    -> PARTITION p1 VALUES LESS THAN (1970),
    -> PARTITION p2 VALUES LESS THAN (1980),
    -> PARTITION p3 VALUES LESS THAN (1990),
    -> PARTITION p4 VALUES LESS THAN MAXVALUE
    -> );
Query OK, 0 rows affected (0.02 sec)

在MySQL 5.7中,也可以使用UNIX_TIMESTAMP()函数,根据时间戳列的值对表进行范围分区,如下面的例子所示:
mysql> CREATE TABLE quarterly_report_status (
    -> report_id INT NOT NULL,
    -> report_status VARCHAR(20) NOT NULL,
    -> report_updated TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
    -> )
    -> PARTITION BY RANGE ( UNIX_TIMESTAMP(report_updated) ) (
    -> PARTITION p0 VALUES LESS THAN ( UNIX_TIMESTAMP('2008-01-01 00:00:00') ),
    -> PARTITION p1 VALUES LESS THAN ( UNIX_TIMESTAMP('2008-04-01 00:00:00') ),
    -> PARTITION p2 VALUES LESS THAN ( UNIX_TIMESTAMP('2008-07-01 00:00:00') ),
    -> PARTITION p3 VALUES LESS THAN ( UNIX_TIMESTAMP('2008-10-01 00:00:00') ),
    -> PARTITION p4 VALUES LESS THAN ( UNIX_TIMESTAMP('2009-01-01 00:00:00') ),
    -> PARTITION p5 VALUES LESS THAN ( UNIX_TIMESTAMP('2009-04-01 00:00:00') ),
    -> PARTITION p6 VALUES LESS THAN ( UNIX_TIMESTAMP('2009-07-01 00:00:00') ),
    -> PARTITION p7 VALUES LESS THAN ( UNIX_TIMESTAMP('2009-10-01 00:00:00') ),
    -> PARTITION p8 VALUES LESS THAN ( UNIX_TIMESTAMP('2010-01-01 00:00:00') ),
    -> PARTITION p9 VALUES LESS THAN (MAXVALUE)
    -> );
Query OK, 0 rows affected (0.05 sec)

在MySQL 5.7中,任何涉及时间戳值的表达式都是不允许的。(参见Bug #42849。)

在MySQL 5.7中也可以使用UNIX_TIMESTAMP(timestamp_column)作为按列表分区的表的分区表达式。然而,这样做通常是不实际的。

2.按范围列对表进行分区,使用DATE或DATETIME列作为分区列。例如,成员表可以直接使用合并的列定义,如下所示:

mysql> CREATE TABLE members (
    -> firstname VARCHAR(25) NOT NULL,
    -> lastname VARCHAR(25) NOT NULL,
    -> username VARCHAR(16) NOT NULL,
    -> email VARCHAR(35),
    -> joined DATE NOT NULL
    -> )
    -> PARTITION BY RANGE COLUMNS(joined) (
    -> PARTITION p0 VALUES LESS THAN ('1960-01-01'),
    -> PARTITION p1 VALUES LESS THAN ('1970-01-01'),
    -> PARTITION p2 VALUES LESS THAN ('1980-01-01'),
    -> PARTITION p3 VALUES LESS THAN ('1990-01-01'),
    -> PARTITION p4 VALUES LESS THAN MAXVALUE
    -> );
Query OK, 0 rows affected (0.02 sec)

使用除 DATE 或 DATETIME 之外的日期或时间类型的分区列,RANGE COLUMNS 不支持这种用法。

MySQL NDB API统计计数器和变量

NDB API统计计数器和变量
与 Ndb 对象执行的操作或影响 Ndb 对象的多种类型的统计计数器可用。此类操作包括启动和关闭(或中止)事务;主键和唯一键操作;表、范 围和修剪扫描;在等待各种操作完成时被阻塞的线程;以及 NDBCLUSTER 发送和接收的数据和事件。每当调用 NDB API 或向数据节点发送或接 收数据时,NDB 内核内部都会递增这些计数器。mysqld 将这些计数器作为系统状态变量公开;其值可以在 SHOW STATUS 的输出中读取,或者通 过查询 INFORMATION_SCHEMA.SESSION_STATUS 或 INFORMATION_SCHEMA.GLOBAL_STATUS 表来获取。通过比较操作 NDB 表的语句执行前后计数器 的值,您可以观察到在 API 级别执行的相应操作,从而了解执行该语句的成本。

你可以使用下面的SHOW STATUS语句列出所有这些状态变量:

mysql> SHOW STATUS LIKE 'ndb_api%';
+----------------------------------------------+----------------+
| Variable_name                                | Value          |
+----------------------------------------------+----------------+
| Ndb_api_wait_exec_complete_count             | 576            |
| Ndb_api_wait_scan_result_count               | 432            |
| Ndb_api_wait_meta_request_count              | 1292           |
| Ndb_api_wait_nanos_count                     | 80154431380927 |
| Ndb_api_bytes_sent_count                     | 250968         |
| Ndb_api_bytes_received_count                 | 2080340        |
| Ndb_api_trans_start_count                    | 409            |
| Ndb_api_trans_commit_count                   | 332            |
| Ndb_api_trans_abort_count                    | 2              |
| Ndb_api_trans_close_count                    | 409            |
| Ndb_api_pk_op_count                          | 475            |
| Ndb_api_uk_op_count                          | 0              |
| Ndb_api_table_scan_count                     | 95             |
| Ndb_api_range_scan_count                     | 0              |
| Ndb_api_pruned_scan_count                    | 0              |
| Ndb_api_scan_batch_count                     | 147            |
| Ndb_api_read_row_count                       | 348            |
| Ndb_api_trans_local_read_row_count           | 108            |
| Ndb_api_adaptive_send_forced_count           | 225            |
| Ndb_api_adaptive_send_unforced_count         | 457            |
| Ndb_api_adaptive_send_deferred_count         | 0              |
| Ndb_api_event_data_count                     | 78             |
| Ndb_api_event_nondata_count                  | 7              |
| Ndb_api_event_bytes_count                    | 26756          |
| Ndb_api_wait_exec_complete_count_slave       | 0              |
| Ndb_api_wait_scan_result_count_slave         | 0              |
| Ndb_api_wait_meta_request_count_slave        | 0              |
| Ndb_api_wait_nanos_count_slave               | 0              |
| Ndb_api_bytes_sent_count_slave               | 0              |
| Ndb_api_bytes_received_count_slave           | 0              |
| Ndb_api_trans_start_count_slave              | 0              |
| Ndb_api_trans_commit_count_slave             | 0              |
| Ndb_api_trans_abort_count_slave              | 0              |
| Ndb_api_trans_close_count_slave              | 0              |
| Ndb_api_pk_op_count_slave                    | 0              |
| Ndb_api_uk_op_count_slave                    | 0              |
| Ndb_api_table_scan_count_slave               | 0              |
| Ndb_api_range_scan_count_slave               | 0              |
| Ndb_api_pruned_scan_count_slave              | 0              |
| Ndb_api_scan_batch_count_slave               | 0              |
| Ndb_api_read_row_count_slave                 | 0              |
| Ndb_api_trans_local_read_row_count_slave     | 0              |
| Ndb_api_adaptive_send_forced_count_slave     | 0              |
| Ndb_api_adaptive_send_unforced_count_slave   | 0              |
| Ndb_api_adaptive_send_deferred_count_slave   | 0              |
| Ndb_api_event_data_count_injector            | 78             |
| Ndb_api_event_nondata_count_injector         | 7              |
| Ndb_api_event_bytes_count_injector           | 26756          |
| Ndb_api_wait_exec_complete_count_session     | 131            |
| Ndb_api_wait_scan_result_count_session       | 142            |
| Ndb_api_wait_meta_request_count_session      | 480            |
| Ndb_api_wait_nanos_count_session             | 1672752063     |
| Ndb_api_bytes_sent_count_session             | 162512         |
| Ndb_api_bytes_received_count_session         | 1019116        |
| Ndb_api_trans_start_count_session            | 87             |
| Ndb_api_trans_commit_count_session           | 64             |
| Ndb_api_trans_abort_count_session            | 0              |
| Ndb_api_trans_close_count_session            | 87             |
| Ndb_api_pk_op_count_session                  | 101            |
| Ndb_api_uk_op_count_session                  | 0              |
| Ndb_api_table_scan_count_session             | 29             |
| Ndb_api_range_scan_count_session             | 0              |
| Ndb_api_pruned_scan_count_session            | 0              |
| Ndb_api_scan_batch_count_session             | 19             |
| Ndb_api_read_row_count_session               | 55             |
| Ndb_api_trans_local_read_row_count_session   | 14             |
| Ndb_api_adaptive_send_forced_count_session   | 76             |
| Ndb_api_adaptive_send_unforced_count_session | 88             |
| Ndb_api_adaptive_send_deferred_count_session | 0              |
+----------------------------------------------+----------------+
69 rows in set (0.01 sec)

这些状态变量也可以从INFORMATION_SCHEMA数据库的SESSION_STATUS和GLOBAL_STATUS表中获得,如下所示:

mysql> SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS  WHERE VARIABLE_NAME LIKE 'ndb_api%';
ERROR 3167 (HY000): The 'INFORMATION_SCHEMA.SESSION_STATUS' feature is disabled; see the documentation for  'show_compatibility_56'

这个错误是由于MySQL 5.7.6及以上版本中,INFORMATION_SCHEMA.SESSION_STATUS视图已被弃用,默认情况下该功能被禁用导致的?,下面临时
启用一下:

mysql> SET GLOBAL show_compatibility_56=ON;
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS  WHERE VARIABLE_NAME LIKE 'ndb_api%';
+----------------------------------------------+----------------+
| VARIABLE_NAME                                | VARIABLE_VALUE |
+----------------------------------------------+----------------+
| NDB_API_WAIT_EXEC_COMPLETE_COUNT             | 576            |
| NDB_API_WAIT_SCAN_RESULT_COUNT               | 432            |
| NDB_API_WAIT_META_REQUEST_COUNT              | 1293           |
| NDB_API_WAIT_NANOS_COUNT                     | 80408736595437 |
| NDB_API_BYTES_SENT_COUNT                     | 251028         |
| NDB_API_BYTES_RECEIVED_COUNT                 | 2080368        |
| NDB_API_TRANS_START_COUNT                    | 409            |
| NDB_API_TRANS_COMMIT_COUNT                   | 332            |
| NDB_API_TRANS_ABORT_COUNT                    | 2              |
| NDB_API_TRANS_CLOSE_COUNT                    | 409            |
| NDB_API_PK_OP_COUNT                          | 475            |
| NDB_API_UK_OP_COUNT                          | 0              |
| NDB_API_TABLE_SCAN_COUNT                     | 95             |
| NDB_API_RANGE_SCAN_COUNT                     | 0              |
| NDB_API_PRUNED_SCAN_COUNT                    | 0              |
| NDB_API_SCAN_BATCH_COUNT                     | 147            |
| NDB_API_READ_ROW_COUNT                       | 348            |
| NDB_API_TRANS_LOCAL_READ_ROW_COUNT           | 108            |
| NDB_API_ADAPTIVE_SEND_FORCED_COUNT           | 225            |
| NDB_API_ADAPTIVE_SEND_UNFORCED_COUNT         | 457            |
| NDB_API_ADAPTIVE_SEND_DEFERRED_COUNT         | 0              |
| NDB_API_EVENT_DATA_COUNT                     | 78             |
| NDB_API_EVENT_NONDATA_COUNT                  | 7              |
| NDB_API_EVENT_BYTES_COUNT                    | 26756          |
| NDB_API_WAIT_EXEC_COMPLETE_COUNT_SLAVE       | 0              |
| NDB_API_WAIT_SCAN_RESULT_COUNT_SLAVE         | 0              |
| NDB_API_WAIT_META_REQUEST_COUNT_SLAVE        | 0              |
| NDB_API_WAIT_NANOS_COUNT_SLAVE               | 0              |
| NDB_API_BYTES_SENT_COUNT_SLAVE               | 0              |
| NDB_API_BYTES_RECEIVED_COUNT_SLAVE           | 0              |
| NDB_API_TRANS_START_COUNT_SLAVE              | 0              |
| NDB_API_TRANS_COMMIT_COUNT_SLAVE             | 0              |
| NDB_API_TRANS_ABORT_COUNT_SLAVE              | 0              |
| NDB_API_TRANS_CLOSE_COUNT_SLAVE              | 0              |
| NDB_API_PK_OP_COUNT_SLAVE                    | 0              |
| NDB_API_UK_OP_COUNT_SLAVE                    | 0              |
| NDB_API_TABLE_SCAN_COUNT_SLAVE               | 0              |
| NDB_API_RANGE_SCAN_COUNT_SLAVE               | 0              |
| NDB_API_PRUNED_SCAN_COUNT_SLAVE              | 0              |
| NDB_API_SCAN_BATCH_COUNT_SLAVE               | 0              |
| NDB_API_READ_ROW_COUNT_SLAVE                 | 0              |
| NDB_API_TRANS_LOCAL_READ_ROW_COUNT_SLAVE     | 0              |
| NDB_API_ADAPTIVE_SEND_FORCED_COUNT_SLAVE     | 0              |
| NDB_API_ADAPTIVE_SEND_UNFORCED_COUNT_SLAVE   | 0              |
| NDB_API_ADAPTIVE_SEND_DEFERRED_COUNT_SLAVE   | 0              |
| NDB_API_EVENT_DATA_COUNT_INJECTOR            | 78             |
| NDB_API_EVENT_NONDATA_COUNT_INJECTOR         | 7              |
| NDB_API_EVENT_BYTES_COUNT_INJECTOR           | 26756          |
| NDB_API_WAIT_EXEC_COMPLETE_COUNT_SESSION     | 131            |
| NDB_API_WAIT_SCAN_RESULT_COUNT_SESSION       | 142            |
| NDB_API_WAIT_META_REQUEST_COUNT_SESSION      | 481            |
| NDB_API_WAIT_NANOS_COUNT_SESSION             | 1672988157     |
| NDB_API_BYTES_SENT_COUNT_SESSION             | 162572         |
| NDB_API_BYTES_RECEIVED_COUNT_SESSION         | 1019144        |
| NDB_API_TRANS_START_COUNT_SESSION            | 87             |
| NDB_API_TRANS_COMMIT_COUNT_SESSION           | 64             |
| NDB_API_TRANS_ABORT_COUNT_SESSION            | 0              |
| NDB_API_TRANS_CLOSE_COUNT_SESSION            | 87             |
| NDB_API_PK_OP_COUNT_SESSION                  | 101            |
| NDB_API_UK_OP_COUNT_SESSION                  | 0              |
| NDB_API_TABLE_SCAN_COUNT_SESSION             | 29             |
| NDB_API_RANGE_SCAN_COUNT_SESSION             | 0              |
| NDB_API_PRUNED_SCAN_COUNT_SESSION            | 0              |
| NDB_API_SCAN_BATCH_COUNT_SESSION             | 19             |
| NDB_API_READ_ROW_COUNT_SESSION               | 55             |
| NDB_API_TRANS_LOCAL_READ_ROW_COUNT_SESSION   | 14             |
| NDB_API_ADAPTIVE_SEND_FORCED_COUNT_SESSION   | 76             |
| NDB_API_ADAPTIVE_SEND_UNFORCED_COUNT_SESSION | 88             |
| NDB_API_ADAPTIVE_SEND_DEFERRED_COUNT_SESSION | 0              |
+----------------------------------------------+----------------+
69 rows in set, 1 warning (0.00 sec)

mysql> SELECT * FROM INFORMATION_SCHEMA.GLOBAL_STATUS  WHERE VARIABLE_NAME LIKE 'ndb_api%';
+----------------------------------------------+----------------+
| VARIABLE_NAME                                | VARIABLE_VALUE |
+----------------------------------------------+----------------+
| NDB_API_WAIT_EXEC_COMPLETE_COUNT             | 576            |
| NDB_API_WAIT_SCAN_RESULT_COUNT               | 432            |
| NDB_API_WAIT_META_REQUEST_COUNT              | 1293           |
| NDB_API_WAIT_NANOS_COUNT                     | 80505790466465 |
| NDB_API_BYTES_SENT_COUNT                     | 251028         |
| NDB_API_BYTES_RECEIVED_COUNT                 | 2080368        |
| NDB_API_TRANS_START_COUNT                    | 409            |
| NDB_API_TRANS_COMMIT_COUNT                   | 332            |
| NDB_API_TRANS_ABORT_COUNT                    | 2              |
| NDB_API_TRANS_CLOSE_COUNT                    | 409            |
| NDB_API_PK_OP_COUNT                          | 475            |
| NDB_API_UK_OP_COUNT                          | 0              |
| NDB_API_TABLE_SCAN_COUNT                     | 95             |
| NDB_API_RANGE_SCAN_COUNT                     | 0              |
| NDB_API_PRUNED_SCAN_COUNT                    | 0              |
| NDB_API_SCAN_BATCH_COUNT                     | 147            |
| NDB_API_READ_ROW_COUNT                       | 348            |
| NDB_API_TRANS_LOCAL_READ_ROW_COUNT           | 108            |
| NDB_API_ADAPTIVE_SEND_FORCED_COUNT           | 225            |
| NDB_API_ADAPTIVE_SEND_UNFORCED_COUNT         | 457            |
| NDB_API_ADAPTIVE_SEND_DEFERRED_COUNT         | 0              |
| NDB_API_EVENT_DATA_COUNT                     | 78             |
| NDB_API_EVENT_NONDATA_COUNT                  | 7              |
| NDB_API_EVENT_BYTES_COUNT                    | 26756          |
| NDB_API_WAIT_EXEC_COMPLETE_COUNT_SLAVE       | 0              |
| NDB_API_WAIT_SCAN_RESULT_COUNT_SLAVE         | 0              |
| NDB_API_WAIT_META_REQUEST_COUNT_SLAVE        | 0              |
| NDB_API_WAIT_NANOS_COUNT_SLAVE               | 0              |
| NDB_API_BYTES_SENT_COUNT_SLAVE               | 0              |
| NDB_API_BYTES_RECEIVED_COUNT_SLAVE           | 0              |
| NDB_API_TRANS_START_COUNT_SLAVE              | 0              |
| NDB_API_TRANS_COMMIT_COUNT_SLAVE             | 0              |
| NDB_API_TRANS_ABORT_COUNT_SLAVE              | 0              |
| NDB_API_TRANS_CLOSE_COUNT_SLAVE              | 0              |
| NDB_API_PK_OP_COUNT_SLAVE                    | 0              |
| NDB_API_UK_OP_COUNT_SLAVE                    | 0              |
| NDB_API_TABLE_SCAN_COUNT_SLAVE               | 0              |
| NDB_API_RANGE_SCAN_COUNT_SLAVE               | 0              |
| NDB_API_PRUNED_SCAN_COUNT_SLAVE              | 0              |
| NDB_API_SCAN_BATCH_COUNT_SLAVE               | 0              |
| NDB_API_READ_ROW_COUNT_SLAVE                 | 0              |
| NDB_API_TRANS_LOCAL_READ_ROW_COUNT_SLAVE     | 0              |
| NDB_API_ADAPTIVE_SEND_FORCED_COUNT_SLAVE     | 0              |
| NDB_API_ADAPTIVE_SEND_UNFORCED_COUNT_SLAVE   | 0              |
| NDB_API_ADAPTIVE_SEND_DEFERRED_COUNT_SLAVE   | 0              |
| NDB_API_EVENT_DATA_COUNT_INJECTOR            | 78             |
| NDB_API_EVENT_NONDATA_COUNT_INJECTOR         | 7              |
| NDB_API_EVENT_BYTES_COUNT_INJECTOR           | 26756          |
| NDB_API_WAIT_EXEC_COMPLETE_COUNT_SESSION     | 131            |
| NDB_API_WAIT_SCAN_RESULT_COUNT_SESSION       | 142            |
| NDB_API_WAIT_META_REQUEST_COUNT_SESSION      | 481            |
| NDB_API_WAIT_NANOS_COUNT_SESSION             | 1672988157     |
| NDB_API_BYTES_SENT_COUNT_SESSION             | 162572         |
| NDB_API_BYTES_RECEIVED_COUNT_SESSION         | 1019144        |
| NDB_API_TRANS_START_COUNT_SESSION            | 87             |
| NDB_API_TRANS_COMMIT_COUNT_SESSION           | 64             |
| NDB_API_TRANS_ABORT_COUNT_SESSION            | 0              |
| NDB_API_TRANS_CLOSE_COUNT_SESSION            | 87             |
| NDB_API_PK_OP_COUNT_SESSION                  | 101            |
| NDB_API_UK_OP_COUNT_SESSION                  | 0              |
| NDB_API_TABLE_SCAN_COUNT_SESSION             | 29             |
| NDB_API_RANGE_SCAN_COUNT_SESSION             | 0              |
| NDB_API_PRUNED_SCAN_COUNT_SESSION            | 0              |
| NDB_API_SCAN_BATCH_COUNT_SESSION             | 19             |
| NDB_API_READ_ROW_COUNT_SESSION               | 55             |
| NDB_API_TRANS_LOCAL_READ_ROW_COUNT_SESSION   | 14             |
| NDB_API_ADAPTIVE_SEND_FORCED_COUNT_SESSION   | 76             |
| NDB_API_ADAPTIVE_SEND_UNFORCED_COUNT_SESSION | 88             |
| NDB_API_ADAPTIVE_SEND_DEFERRED_COUNT_SESSION | 0              |
+----------------------------------------------+----------------+
69 rows in set, 1 warning (0.00 sec)

每个 ndb 对象都有其自身的计数器。NDB API 应用程序可以读取这些计数器的值,用于优化或监控。对于使用多个 Ndb 对象并发运行的多线程 客户端,还可以从属于给定 ndb_cluster_connection 的所有 Ndb 对象中获取计数器的汇总视图。

暴露了四组这样的计数器。一组只适用于当前会话;另外3个是全局性的。尽管它们的值可以在mysql客户端中作为会话或全局状态变量获得。这 意味着使用SHOW STATUS指定SESSION或GLOBAL关键字对NDB API统计状态变量报告的值没有影响,并且这些变量的值无论从SESSION_STATUS或 GLOBAL_STATUS表的等效列中获得的值都是相同的。
.会话计数器(特定于会话)
会话计数器与(仅)当前会话使用的Ndb对象相关。其他MySQL客户端使用这些对象不会影响这些计数。

为了尽量减少与标准MySQL会话变量的混淆,我们将与这些NDB API会话计数器对应的变量称为“_session变量”,前面有下划线。

.从属计数器(全局)
这组计数器与复制从SQL线程使用的Ndb对象相关(如果有的话)。如果这个mysqld不作为复制从,或者不使用NDB表,那么所有这些计数都是0。

我们将相关的状态变量称为“_slave变量”(前面有下划线)。

.注入器计数器(全局)
注入器计数器与用于侦听二进制日志注入器线程的集群事件的Ndb对象相关。即使不写二进制日志,附加到NDB集群的mysqld进程也会继续侦听一 些事件,比如模式更改。
我们把对应于NDB API注入器计数器的状态变量称为“_injector variables”。(以下划线开头)。

.服务器(全局)计数器(全局)
这组计数器与当前mysqld使用的所有Ndb对象相关。这包括所有MySQL客户端应用程序、从SQL线程(如果有的话)、binlog注入器和NDB实用程序 线程。
我们将与这些计数器对应的状态变量称为“全局变量”或“mysqld级变量”。

你可以通过额外过滤变量名中的子字符串session、slave或injector(以及常用的前缀Ndb_api)来获得一组特定变量的值。对于_session变量 ,可以这样做:

mysql> SHOW STATUS LIKE 'ndb_api%session';
+----------------------------------------------+------------+
| Variable_name                                | Value      |
+----------------------------------------------+------------+
| Ndb_api_wait_exec_complete_count_session     | 131        |
| Ndb_api_wait_scan_result_count_session       | 142        |
| Ndb_api_wait_meta_request_count_session      | 481        |
| Ndb_api_wait_nanos_count_session             | 1672988157 |
| Ndb_api_bytes_sent_count_session             | 162572     |
| Ndb_api_bytes_received_count_session         | 1019144    |
| Ndb_api_trans_start_count_session            | 87         |
| Ndb_api_trans_commit_count_session           | 64         |
| Ndb_api_trans_abort_count_session            | 0          |
| Ndb_api_trans_close_count_session            | 87         |
| Ndb_api_pk_op_count_session                  | 101        |
| Ndb_api_uk_op_count_session                  | 0          |
| Ndb_api_table_scan_count_session             | 29         |
| Ndb_api_range_scan_count_session             | 0          |
| Ndb_api_pruned_scan_count_session            | 0          |
| Ndb_api_scan_batch_count_session             | 19         |
| Ndb_api_read_row_count_session               | 55         |
| Ndb_api_trans_local_read_row_count_session   | 14         |
| Ndb_api_adaptive_send_forced_count_session   | 76         |
| Ndb_api_adaptive_send_unforced_count_session | 88         |
| Ndb_api_adaptive_send_deferred_count_session | 0          |
+----------------------------------------------+------------+
21 rows in set (0.00 sec)

要获取NDB API mysqld级别状态变量的列表,请过滤以ndb_api开头、以_count结尾的变量名,如下所示:

mysql> SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS  WHERE VARIABLE_NAME LIKE 'ndb_api%count';
+--------------------------------------+----------------+
| VARIABLE_NAME                        | VARIABLE_VALUE |
+--------------------------------------+----------------+
| NDB_API_WAIT_EXEC_COMPLETE_COUNT     | 578            |
| NDB_API_WAIT_SCAN_RESULT_COUNT       | 432            |
| NDB_API_WAIT_META_REQUEST_COUNT      | 1293           |
| NDB_API_WAIT_NANOS_COUNT             | 81348847300579 |
| NDB_API_BYTES_SENT_COUNT             | 251124         |
| NDB_API_BYTES_RECEIVED_COUNT         | 2080408        |
| NDB_API_TRANS_START_COUNT            | 411            |
| NDB_API_TRANS_COMMIT_COUNT           | 334            |
| NDB_API_TRANS_ABORT_COUNT            | 2              |
| NDB_API_TRANS_CLOSE_COUNT            | 411            |
| NDB_API_PK_OP_COUNT                  | 477            |
| NDB_API_UK_OP_COUNT                  | 0              |
| NDB_API_TABLE_SCAN_COUNT             | 95             |
| NDB_API_RANGE_SCAN_COUNT             | 0              |
| NDB_API_PRUNED_SCAN_COUNT            | 0              |
| NDB_API_SCAN_BATCH_COUNT             | 147            |
| NDB_API_READ_ROW_COUNT               | 348            |
| NDB_API_TRANS_LOCAL_READ_ROW_COUNT   | 108            |
| NDB_API_ADAPTIVE_SEND_FORCED_COUNT   | 225            |
| NDB_API_ADAPTIVE_SEND_UNFORCED_COUNT | 459            |
| NDB_API_ADAPTIVE_SEND_DEFERRED_COUNT | 0              |
| NDB_API_EVENT_DATA_COUNT             | 78             |
| NDB_API_EVENT_NONDATA_COUNT          | 7              |
| NDB_API_EVENT_BYTES_COUNT            | 26756          |
+--------------------------------------+----------------+
24 rows in set, 1 warning (0.00 sec)

并非所有计数器都反映在所有4组状态变量中。对于事件计数器DataEventsRecvdCount、NondataEventsRecvdCount和EventBytesRecvdCount,只 有_injector和mysqld级别的NDB API状态变量可用:


mysql> SHOW STATUS LIKE 'ndb_api%event%';
+--------------------------------------+-------+
| Variable_name                        | Value |
+--------------------------------------+-------+
| Ndb_api_event_data_count             | 78    |
| Ndb_api_event_nondata_count          | 7     |
| Ndb_api_event_bytes_count            | 26756 |
| Ndb_api_event_data_count_injector    | 78    |
| Ndb_api_event_nondata_count_injector | 7     |
| Ndb_api_event_bytes_count_injector   | 26756 |
+--------------------------------------+-------+
6 rows in set (0.00 sec)

_injector状态变量,如下所示:

mysql> SHOW STATUS LIKE 'ndb_api%injector%';
+--------------------------------------+-------+
| Variable_name                        | Value |
+--------------------------------------+-------+
| Ndb_api_event_data_count_injector    | 78    |
| Ndb_api_event_nondata_count_injector | 7     |
| Ndb_api_event_bytes_count_injector   | 26756 |
+--------------------------------------+-------+
3 rows in set (0.00 sec)

要查看所有已提交事务的计数,也就是所有TransCommitCount计数器状态变量,你可以为SHOW STATUS加上子字符串trans_commit_count进行过滤 ,如下所示:

mysql> SHOW STATUS LIKE '%trans_commit_count%';
+------------------------------------+-------+
| Variable_name                      | Value |
+------------------------------------+-------+
| Ndb_api_trans_commit_count         | 334   |
| Ndb_api_trans_commit_count_slave   | 0     |
| Ndb_api_trans_commit_count_session | 64    |
+------------------------------------+-------+
3 rows in set (0.00 sec)

由此,你可以确定在当前mysql客户端会话中已经提交了334个事务,并且自上次重新启动以来,这个mysqld上已经提交了64个事务。

通过比较执行语句前后对应的_session状态变量的值,可以看到给定SQL语句如何增加各种NDB API计数器。在这个例子中,在得到SHOW STATUS 的初始值之后,我们在jycs数据库中创建了一个名为t的NDB表,它只有一列:

mysql> SHOW STATUS LIKE 'ndb_api%session%';
+----------------------------------------------+------------+
| Variable_name                                | Value      |
+----------------------------------------------+------------+
| Ndb_api_wait_exec_complete_count_session     | 131        |
| Ndb_api_wait_scan_result_count_session       | 142        |
| Ndb_api_wait_meta_request_count_session      | 481        |
| Ndb_api_wait_nanos_count_session             | 1672988157 |
| Ndb_api_bytes_sent_count_session             | 162572     |
| Ndb_api_bytes_received_count_session         | 1019144    |
| Ndb_api_trans_start_count_session            | 87         |
| Ndb_api_trans_commit_count_session           | 64         |
| Ndb_api_trans_abort_count_session            | 0          |
| Ndb_api_trans_close_count_session            | 87         |
| Ndb_api_pk_op_count_session                  | 101        |
| Ndb_api_uk_op_count_session                  | 0          |
| Ndb_api_table_scan_count_session             | 29         |
| Ndb_api_range_scan_count_session             | 0          |
| Ndb_api_pruned_scan_count_session            | 0          |
| Ndb_api_scan_batch_count_session             | 19         |
| Ndb_api_read_row_count_session               | 55         |
| Ndb_api_trans_local_read_row_count_session   | 14         |
| Ndb_api_adaptive_send_forced_count_session   | 76         |
| Ndb_api_adaptive_send_unforced_count_session | 88         |
| Ndb_api_adaptive_send_deferred_count_session | 0          |
+----------------------------------------------+------------+
21 rows in set (0.00 sec)

mysql> use jycs;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> CREATE TABLE t (c INT) ENGINE NDBCLUSTER;
Query OK, 0 rows affected (0.08 sec)

现在,您可以执行新的SHOW STATUS语句并观察更改,如下所示(在输出中突出显示更改的行):

mysql> SHOW STATUS LIKE 'ndb_api%session%';
+----------------------------------------------+------------+
| Variable_name                                | Value      |
+----------------------------------------------+------------+
| Ndb_api_wait_exec_complete_count_session     | 139        |
| Ndb_api_wait_scan_result_count_session       | 142        |
| Ndb_api_wait_meta_request_count_session      | 497        |
| Ndb_api_wait_nanos_count_session             | 1714790150 |
| Ndb_api_bytes_sent_count_session             | 164632     |
| Ndb_api_bytes_received_count_session         | 1051308    |
| Ndb_api_trans_start_count_session            | 91         |
| Ndb_api_trans_commit_count_session           | 68         |
| Ndb_api_trans_abort_count_session            | 0          |
| Ndb_api_trans_close_count_session            | 91         |
| Ndb_api_pk_op_count_session                  | 107        |
| Ndb_api_uk_op_count_session                  | 0          |
| Ndb_api_table_scan_count_session             | 29         |
| Ndb_api_range_scan_count_session             | 0          |
| Ndb_api_pruned_scan_count_session            | 0          |
| Ndb_api_scan_batch_count_session             | 19         |
| Ndb_api_read_row_count_session               | 57         |
| Ndb_api_trans_local_read_row_count_session   | 14         |
| Ndb_api_adaptive_send_forced_count_session   | 78         |
| Ndb_api_adaptive_send_unforced_count_session | 94         |
| Ndb_api_adaptive_send_deferred_count_session | 0          |
+----------------------------------------------+------------+
21 rows in set (0.00 sec)

类似地,你可以看到在t中插入一行所引起的NDB API统计计数器的变化:插入行,然后运行与前面示例中相同的SHOW STATUS语句,如下所示:

mysql> INSERT INTO t VALUES (100);
Query OK, 1 row affected (0.01 sec)

mysql> SHOW STATUS LIKE 'ndb_api%session%';
+----------------------------------------------+------------+
| Variable_name                                | Value      |
+----------------------------------------------+------------+
| Ndb_api_wait_exec_complete_count_session     | 142        |
| Ndb_api_wait_scan_result_count_session       | 146        |
| Ndb_api_wait_meta_request_count_session      | 498        |
| Ndb_api_wait_nanos_count_session             | 1715762929 |
| Ndb_api_bytes_sent_count_session             | 164928     |
| Ndb_api_bytes_received_count_session         | 1051540    |
| Ndb_api_trans_start_count_session            | 94         |
| Ndb_api_trans_commit_count_session           | 70         |
| Ndb_api_trans_abort_count_session            | 0          |
| Ndb_api_trans_close_count_session            | 94         |
| Ndb_api_pk_op_count_session                  | 109        |
| Ndb_api_uk_op_count_session                  | 0          |
| Ndb_api_table_scan_count_session             | 30         |
| Ndb_api_range_scan_count_session             | 0          |
| Ndb_api_pruned_scan_count_session            | 0          |
| Ndb_api_scan_batch_count_session             | 19         |
| Ndb_api_read_row_count_session               | 58         |
| Ndb_api_trans_local_read_row_count_session   | 15         |
| Ndb_api_adaptive_send_forced_count_session   | 81         |
| Ndb_api_adaptive_send_unforced_count_session | 95         |
| Ndb_api_adaptive_send_deferred_count_session | 0          |
+----------------------------------------------+------------+
21 rows in set (0.00 sec)

我们可以从这些结果中得出一些观察结果:
.虽然我们没有使用显式的主键创建t,但在此过程中执行了6个主键操作(Ndb_api_pk_op_count_session的“before”和“after”值之差,即 107减106)。这反映了隐藏主键的创建,这是使用NDB存储引擎的所有表的一个特性。

.通过比较Ndb_api_wait_nanos_count_session的连续值,我们可以看到,实现CREATE TABLE语句的NDB API操作等待数据节点响应的时间 (1714790150-1672988157 = 41801993 纳秒,或大约0.04秒)要比INSERT (1715762929-1714790150 = 972779 纳秒或大约0.001秒)长得多。 mysql客户端中报告的这些语句的执行时间与这些图大致相关。

在没有足够(纳秒)时间分辨率的平台上,由于SQL语句执行非常快而导致的WaitNanosCount NDB API计数器值的微小变化可能并不总是在 Ndb_api_wait_nanos_count_session, Ndb_api_wait_nanos_count_slave或Ndb_api_wait_nanos_count的值中可见。

.INSERT语句增加了NDB API统计计数器的ReadRowCount和TransLocalReadRowCount,正如Ndb_api_read_row_count_session和 Ndb_api_trans_local_read_row_count_session增加的值所反映的那样。

MySQL 为NDB集群分发MySQL用户和权限

为NDB集群分发MySQL用户和权限
NDB集群支持在一个NDB集群中的所有SQL节点上分配MySQL用户和权限。默认情况下不启用此支持;为了这样做,您应该遵循以下过程。

通常,MySQL数据库中的每个MySQL服务器的用户权限表必须使用MyISAM存储引擎,这意味着在一个SQL节点上创建的用户帐户及其相关权限在集群的其他SQL节点上不可用。SQL文件ndb_dist_priv。在MySQL安装目录的share目录下可以找到NDB Cluster发行版提供的sql文件。

[root@mysqld share]# pwd
/usr/local/mysql/share
[root@mysqld share]# ll ndb*
-rw-r--r--. 1 root mysql 12442 Sep 26  2024 ndb_dist_priv.sql

启用分布式权限的第一步是将此脚本加载到作为SQL节点的MySQL服务器中(在此之后我们将其称为目标SQL节点或MySQL服务器)。您可以在目标SQL节点的系统shell中执行以下命令,然后将其更改为MySQL安装目录(其中options表示连接到该SQL节点所需的任何附加选项):

[root@mysqld share]# mysql -uroot -p123456 < /usr/local/mysql/share/ndb_dist_priv.sql
mysql: [Warning] Using a password on the command line interface can be insecure.

导入 ndb_dist_priv.sql 会在目标 SQL 节点的 mysql 数据库中创建多个存储例程(六个存储过程和一个存储函数)。在 mysql 客户端连接到

SQL 节点(以 MySQL 根用户身份)后,您可以按如下所示验证这些例程是否已创建:

mysql> SELECT ROUTINE_NAME, ROUTINE_SCHEMA, ROUTINE_TYPE FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_NAME LIKE

'mysql_cluster%' ORDER BY ROUTINE_TYPE;
+---------------------------------------------+----------------+--------------+
| ROUTINE_NAME                                | ROUTINE_SCHEMA | ROUTINE_TYPE |
+---------------------------------------------+----------------+--------------+
| mysql_cluster_privileges_are_distributed    | mysql          | FUNCTION     |
| mysql_cluster_backup_privileges             | mysql          | PROCEDURE    |
| mysql_cluster_move_grant_tables             | mysql          | PROCEDURE    |
| mysql_cluster_move_privileges               | mysql          | PROCEDURE    |
| mysql_cluster_restore_local_privileges      | mysql          | PROCEDURE    |
| mysql_cluster_restore_privileges            | mysql          | PROCEDURE    |
| mysql_cluster_restore_privileges_from_local | mysql          | PROCEDURE    |
+---------------------------------------------+----------------+--------------+
7 rows in set (0.03 sec)

名为mysql_cluster_move_privileges的存储过程创建现有权限表的备份副本,然后将它们转换为NDB。

mysql_cluster_move_privileges分两步执行备份和转换。第一步是调用mysql_cluster_backup_privileges,这将在mysql数据库中创建两组副本:
.一组使用 MyISAM 存储引擎的本地副本。它们的名称是在原始权限表名后添加 _backup 后缀生成的。

.一组使用NDBCLUSTER存储引擎的分布式副本。这些表通过在原始表的名称前加上ndb_前缀和_backup来生成的。

副本创建后,mysql_cluster_move_privileges调用mysql_cluster_move_grant_tables,其中包含ALTER TABLE…ENGINE =NDB将mysql系统表转换为NDB。

通常,您不应该手动调用mysql_cluster_backup_privileges或mysql_cluster_move_grant_tables;这些存储过程仅供mysql_cluster_move_privileges调用。

虽然原始权限表是自动备份的,但在继续之前,最好在所有受影响的SQL节点上手动创建现有权限表的备份。你可以使用mysqldump来实现这个目的,如下所示:

[root@mysqld /]# mysqldump  -uroot -p123456 mysql user db tables_priv columns_priv procs_priv proxies_priv > backup_file
mysqldump: [Warning] Using a password on the command line interface can be insecure.

要执行转换,必须使用mysql客户端连接到目标SQL节点(同样,作为mysql root用户)。像这样调用存储过程:

mysql> CALL mysql.mysql_cluster_move_privileges();
Query OK, 0 rows affected (2.22 sec)

根据权限表中的行数,此过程可能需要一些时间来执行。如果某些权限表是空的,那么当mysql_cluster_move_privileges返回时,您可能会看到一个或多个No data – zero rows fetched, selected, or processed 警告。在这种情况下,可以安全地忽略警告。为了验证转换是否成功,

你可以使用存储函数mysql_cluster_priviles_are_distributed,如下所示:

mysql> SELECT CONCAT( 'Conversion ',IF(mysql.mysql_cluster_privileges_are_distributed(), 'succeeded', 'failed'), '.') AS

Result;
+-----------------------+
| Result                |
+-----------------------+
| Conversion succeeded. |
+-----------------------+
1 row in set (0.01 sec)

mysql_cluster_privile_are_distributed检查是否存在分布式权限表,如果所有的权限表都是分布式的,则返回1;否则,返回0。

您可以使用如下查询来验证备份是否已经创建:

mysql> SELECT TABLE_NAME, ENGINE FROM INFORMATION_SCHEMA.TABLES  WHERE TABLE_SCHEMA = 'mysql' AND TABLE_NAME LIKE '%backup'

ORDER BY ENGINE;
+-------------------------+------------+
| TABLE_NAME              | ENGINE     |
+-------------------------+------------+
| db_backup               | MyISAM     |
| user_backup             | MyISAM     |
| columns_priv_backup     | MyISAM     |
| tables_priv_backup      | MyISAM     |
| proxies_priv_backup     | MyISAM     |
| procs_priv_backup       | MyISAM     |
| ndb_tables_priv_backup  | ndbcluster |
| ndb_proxies_priv_backup | ndbcluster |
| ndb_procs_priv_backup   | ndbcluster |
| ndb_db_backup           | ndbcluster |
| ndb_columns_priv_backup | ndbcluster |
| ndb_user_backup         | ndbcluster |
+-------------------------+------------+
12 rows in set (0.01 sec)

一旦转换为分布式权限,任何时候在任何SQL节点上创建、删除MySQL用户帐户或更新其权限,这些更改都会立即对连接到集群的所有其他MySQL服务器生效。一旦分配了权限,任何连接到集群的新MySQL服务器都会自动参与分配。

对于在执行mysql_cluster_move_privileges时连接到SQL节点的客户端,您可能需要在这些SQL节点上执行FLUSH PRIVILEGES,或者断开连接,然后重新连接客户端,以便这些客户端能够看到权限的变化。

所有MySQL用户权限分布在所有连接的MySQL服务器上。这包括与视图和存储过程相关的任何权限,尽管当前不支持视图和存储过程本身的分发。

在mysql_cluster_move_privileges运行期间,如果一个SQL节点与集群断开连接,必须在重新连接到集群后使用DROP TABLE IF EXISTS mysql.user mysql.db mysql.tables_priv mysql.columns_priv mysql.procs_priv语句删除它的权限表,这将导致SQL节点使用共享权限表,而 不是自己的本地版本。第一次将新的SQL节点连接到集群时不需要这样做。

如果初始重新启动整个集群(关闭所有数据节点,然后使用–initial重新启动),则共享权限表将丢失。如果发生这种情况,您可以使用原始 目标SQL节点从mysql_cluster_move_privileges所做的备份或mysqldump创建的转储文件中恢复它们。如果你需要使用一个新的MySQL服务器来执 行恢复,你应该在第一次连接到集群时用–skip-grant-tables启动它;在此之后,您可以在本地恢复权限表,然后使用 mysql_cluster_move_privileges再次分发它们。在恢复和分发表之后,您应该重新启动这个MySQL服务器,不使用–skip-grant-tables选项。

还可以使用ndb_restore –restore-privilege-tables从ndb_mgm客户端中使用START BACKUP进行的备份中恢复分布式表。 (mysql_cluster_move_privileges创建的MyISAM表不通过START BACKUP命令进行备份)ndb_restore默认不恢复权限表;使用–restore- privilege-tables选项才会执行恢复。

您可以使用两个过程中的任何一个来恢复SQL节点的本地权限。mysql_cluster_restore_privileges的工作原理如下:
1.如果mysql.ndb_*_backup表的副本可用,试图从这些副本中来还原系统表。
2.否则,试图从命名为*_backup(没有ndb_前缀)的本地备份中还原系统表

另一个过程名为mysql_cluster_restore_local_privileges,它只从本地备份恢复系统表,而不检查ndb_*备份。

mysql_cluster_restore_privileges或mysql_cluster_restore_local_privileges重新创建的系统表使用MySQL服务器默认的存储引擎;它们不 以任何方式共享或分发,也不使用NDB Cluster的NDB存储引擎。

额外的存储过程mysql_cluster_restore_privile_from_local用于使用mysql_cluster_restore_privileges和 mysql_cluster_restore_local_privileges。它不应该被直接调用。

MySQL 在线添加NDB集群数据节点

在线添加NDB集群数据节点

在向NDB集群添加新数据节点所需的基本步骤。无论您是为数据节点进程使用ndbd还是ndbmtd二进制文件,此过程都适用。
假设您已经有一个正在运行的NDB集群,在线添加数据节点需要以下步骤:
1.编辑集群配置config.ini文件,添加与要添加的节点相对应的新[ndbd]部分。在集群使用多个管理服务器的情况下,需要对管理服务器使用的 所有config.ini文件进行这些更改。

必须注意,在config.ini文件中添加的任何新数据节点的节点id不能与现有节点使用的节点id重叠。如果您的API节点使用动态分配的节点id, 并且这些id与您想要用于新数据节点的节点id相匹配,则可以强制任何此类API节点“迁移”,如本过程后面所述。

2.滚动重启所有NDB Cluster管理服务器。
必须使用–reload或–initial选项重新启动所有管理服务器,以强制读取新配置。

3.对所有现有的NDB Cluster数据节点执行滚动重启。在重新启动现有数据节点时,没有必要(通常甚至不希望)使用–initial。
如果您使用的API节点具有动态分配的id,与您希望分配给新数据节点的任何节点id相匹配,则在此步骤中重新启动任何数据节点进程之前,必 须重新启动所有API节点(包括SQL节点)。这将导致具有先前未显式分配的节点id的任何API节点放弃这些节点id并获取新的节点id。

4.对连接到NDB集群的所有SQL或API节点执行滚动重启。

5.启动新的数据节点。
新的数据节点可以按任何顺序启动。它们也可以并发地启动,只要它们是在所有现有数据节点的滚动重启完成之后,并且在继续下一步之前启动 的。

6.在NDB集群管理客户端中执行一个或多个CREATE NODEGROUP命令,创建新数据节点所属的新节点组。

7.在所有数据节点(包括新节点)之间重新分配集群的数据。通常,这是通过在mysql客户端为每个NDBCLUSTER表,发出ALTER TABLE… ALGORITHM=INPLACE,REORGANIZE PARTITION语句。

异常:对于使用MAX_ROWS选项创建的表,此语句不起作用;相反,使用AALTER TABLE … ALGORITHM=INPLACE MAX_ROWS=…重新整理这样的表 您还应该记住,不赞成以这种方式使用MAX_ROWS来设置分区数量,
NDB 7.5.4及以后的版本,应该使用PARTITION_BALANCE;

只需要对添加新节点组时已经存在的表执行此操作。添加新节点组后创建的表中的数据将自动分布;但是,添加到任何给定表TBL中的在添加新 节点之前就存在的数据,在重新组织该表之前不会使用新节点进行分发。

8.
ALTER ONLINE TABLE … REORGANIZE PARTITION重新组织分区,但不回收“旧”节点上释放的空间。您可以通过在mysql客户端中为每个 NDBCLUSTER表发出一个OPTIMIZE table语句来实现这一点。

这适用于内存中NDB表的可变宽度列所使用的空间。内存表的固定宽度列不支持OPTIMIZE TABLE;磁盘数据表也不支持。

您可以添加所需的所有节点,然后连续发出几个CREATE NODEGROUP命令,将新的节点组添加到集群中。

在线添加NDB集群数据节点示例
提供一个详细的示例,说明如何在线添加新的NDB集群数据节点,从在单个节点组中具有2个数据节点的NDB集群开始,到在两 个节点组中具有4个数据节点的集群结束。

开始配置
为了说明,我们假设一个最小的配置,并且集群使用config.ini文件,该文件只包含以下信息:

[root@mgmd mysql-cluster]# cat config.ini
[ndbd default]
# Options affecting ndbd processes on all data nodes:
NoOfReplicas=2      # Number of replicas
DataMemory=1024M    # How much memory to allocate for data storage
IndexMemory=256M    # How much memory to allocate for index storage
                    # For DataMemory and IndexMemory, we have used the
                    # default values. Since the "world" database takes up
                    # only about 500KB, this should be more than enough for
                    # this example NDB Cluster setup.
ServerPort=2202     # This the default value; however, you can use any
                    # port that is free for all the hosts in the cluster
                    # Note1: It is recommended that you do not specify the port
                    # number at all and simply allow the default value to be used
                    # instead
                    # Note2: The port was formerly specified using the PortNumber
                    # TCP parameter; this parameter is no longer available in NDB
                    # Cluster 7.5.
[ndb_mgmd]
# Management process options:
HostName=10.10.10.102            # Hostname or IP address of MGM node
DataDir=/var/lib/mysql-cluster     # Directory for MGM node log files
[ndbd]
# Options for data node "A":
HostName=10.10.10.104            # Hostname or IP address
NodeId=2                           # Node ID for this data node
DataDir=/usr/local/mysql/data      # Directory for this data node's data files
[ndbd]
# Options for data node "B":
HostName=10.10.10.105            # Hostname or IP address
NodeId=3                           # Node ID for this data node
DataDir=/usr/local/mysql/data      # Directory for this data node's data files
[mysqld]
# SQL node options:
HostName=10.10.10.103            # Hostname or IP address
                                   # (additional mysqld connections can be
                                   # specified for this node for various
                                   # purposes such as running ndb_restore)


我们还假设您已经使用适当的命令行或my.cnf选项启动了集群,并且在管理客户端中运行SHOW会产生类似于下面所示的输出:

[root@mgmd /]# ndb_mgm
-- NDB Cluster -- Management Client --
ndb_mgm> show
Connected to Management Server at: localhost:1186
Cluster Configuration
---------------------
[ndbd(NDB)]     2 node(s)
id=2    @10.10.10.104  (mysql-5.7.48 ndb-7.5.36, Nodegroup: 0, *)
id=3    @10.10.10.105  (mysql-5.7.48 ndb-7.5.36, Nodegroup: 0)

[ndb_mgmd(MGM)] 1 node(s)
id=1    @10.10.10.102  (mysql-5.7.48 ndb-7.5.36)

[mysqld(API)]   1 node(s)
id=4    @10.10.10.103  (mysql-5.7.48 ndb-7.5.36)

最后,我们假设集群中创建了以下数据表,如下所示:

mysql> use jycs
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> show tables;
+-----------------+
| Tables_in_jycs  |
+-----------------+
| btest           |
| city            |
| country         |
| countrylanguage |
| ctest           |
| example         |
| fish            |
+-----------------+
7 rows in set (0.01 sec)

mysql> select count(*) from btest;
+----------+
| count(*) |
+----------+
|        1 |
+----------+
1 row in set (0.00 sec)

mysql> select count(*) from city;
+----------+
| count(*) |
+----------+
|     4079 |
+----------+
1 row in set (0.00 sec)

mysql> select count(*) from country;
+----------+
| count(*) |
+----------+
|      239 |
+----------+
1 row in set (0.00 sec)

mysql> select count(*) from countrylanguage;
+----------+
| count(*) |
+----------+
|      984 |
+----------+
1 row in set (0.00 sec)

mysql> select count(*) from ctest;
+----------+
| count(*) |
+----------+
|        3 |
+----------+
1 row in set (0.01 sec)

mysql> select count(*) from example;
+----------+
| count(*) |
+----------+
|        0 |
+----------+
1 row in set (0.00 sec)

mysql> select count(*) from fish;
+----------+
| count(*) |
+----------+
|        6 |
+----------+
1 row in set (0.00 sec)

在本例中,我们展示了用于数据节点进程的单线程ndbd。如果您正在使用多线程的ndbmtd,您也可以应用这个示例,方法是将ndbmtd替换为后面 步骤中出现的ndbd。

步骤1:更新配置文件。
在文本编辑器中打开集群全局配置文件,并添加与2个新数据节点对应的[ndbd]部分。(我们给这些数据节点的id为5和6,并假设它们将分别运 行在地址为10.10.10.106和10.10.10.107的主机上。)在你添加了新的部分之后,config.ini文件的内容应该如下所示:

[root@mgmd mysql-cluster]# cat config.ini
[ndbd default]
# Options affecting ndbd processes on all data nodes:
NoOfReplicas=2      # Number of replicas
DataMemory=1024M    # How much memory to allocate for data storage
IndexMemory=256M    # How much memory to allocate for index storage
                    # For DataMemory and IndexMemory, we have used the
                    # default values. Since the "world" database takes up
                    # only about 500KB, this should be more than enough for
                    # this example NDB Cluster setup.
ServerPort=2202     # This the default value; however, you can use any
                    # port that is free for all the hosts in the cluster
                    # Note1: It is recommended that you do not specify the port
                    # number at all and simply allow the default value to be used
                    # instead
                    # Note2: The port was formerly specified using the PortNumber
                    # TCP parameter; this parameter is no longer available in NDB
                    # Cluster 7.5.
[ndb_mgmd]
# Management process options:
HostName=10.10.10.102            # Hostname or IP address of MGM node
DataDir=/var/lib/mysql-cluster     # Directory for MGM node log files
[ndbd]
# Options for data node "A":
HostName=10.10.10.104            # Hostname or IP address
NodeId=2                           # Node ID for this data node
DataDir=/usr/local/mysql/data      # Directory for this data node's data files
[ndbd]
# Options for data node "B":
HostName=10.10.10.105            # Hostname or IP address
NodeId=3                           # Node ID for this data node
DataDir=/usr/local/mysql/data      # Directory for this data node's data files
[ndbd]
# Options for data node "C":
HostName=10.10.10.106            # Hostname or IP address
NodeId=5                           # Node ID for this data node
DataDir=/usr/local/mysql/data      # Directory for this data node's data files
[ndbd]
# Options for data node "D":
HostName=10.10.10.107            # Hostname or IP address
NodeId=6                           # Node ID for this data node
DataDir=/usr/local/mysql/data      # Directory for this data node's data files
[mysqld]
# SQL node options:
HostName=10.10.10.103            # Hostname or IP address
                                   # (additional mysqld connections can be
                                   # specified for this node for various
                                   # purposes such as running ndb_restore)

完成必要的更改后,保存文件。

步骤2:重新启动管理服务器。
重新启动集群管理服务器需要发出单独的命令来停止管理服务器,然后重新启动它,如下所示:
1.使用管理客户端Stop命令停止管理服务器,如下所示

ndb_mgm> 1 stop
Node 1 has shutdown.
Disconnecting to allow Management Server to shutdown

ndb_mgm>

2.由于关闭管理服务器将导致管理客户端终止,因此必须从系统shell启动管理服务器。为简单起见,我们假设config.ini与管理服务器二进制 文件位于同一目录中,但实际上,您必须为配置文件提供正确的路径。您还必须提供–reload或–initial选项,以便管理服务器从文件而不是 其配置缓存中读取新配置。如果您的shell的当前目录与管理服务器二进制文件所在的目录相同,那么您可以调用管理服务器,如下所示:

ndb_mgmd -f  config.ini --reload --configdir=/var/lib/mysql-cluster

如果您的shell的当前目录与管理服务器二进制文件所在的目录不相同那么您可以调用管理服务器,如下所示:

[root@mgmd /]# ndb_mgmd -f /var/lib/mysql-cluster/config.ini --reload --configdir=/var/lib/mysql-cluster
MySQL Cluster Management Server mysql-5.7.48 ndb-7.5.36

如果你在重启ndb_mgm进程后在管理客户端检查SHOW的输出,你现在应该看到如下所示:

[root@mgmd /]# ndb_mgm
-- NDB Cluster -- Management Client --
ndb_mgm> show
Connected to Management Server at: localhost:1186
Cluster Configuration
---------------------
[ndbd(NDB)]     4 node(s)
id=2    @10.10.10.104  (mysql-5.7.48 ndb-7.5.36, Nodegroup: 0, *)
id=3    @10.10.10.105  (mysql-5.7.48 ndb-7.5.36, Nodegroup: 0)
id=5 (not connected, accepting connect from 10.10.10.106)
id=6 (not connected, accepting connect from 10.10.10.107)

[ndb_mgmd(MGM)] 1 node(s)
id=1    @10.10.10.102  (mysql-5.7.48 ndb-7.5.36)

[mysqld(API)]   1 node(s)
id=7 (not connected, accepting connect from 10.10.10.103)

步骤3:执行现有数据节点的滚动重启。
这一步完全可以在集群管理客户端中使用RESTART命令完成,如下所示:

在重启数据节点之前需要先关闭SQL节点,否则在重启数据节点时会出现如下错误

ndb_mgm> 2 restart
Node 2: Node shutdown initiated
Node 2: Node shutdown completed, restarting, no start.
Node 2 is being restarted

ndb_mgm> Node 2: Start initiated (version 7.5.36)
Node 2: Forced node shutdown completed. Occured during startphase 5. Caused by error 2303: 'System error, node killed during  node restart by other node(Internal error, programming error or missing error message, please report a bug). Temporary error,  restart node'.

查看数据节点日志:

2025-07-01 19:27:47 [ndbd] INFO     -- Node 2 killed this node because it could not copy a subscription during node restart.  Copy subscrip
2025-07-01 19:27:47 [ndbd] INFO     -- NDBCNTR (Line: 303) 0x00000002
2025-07-01 19:27:47 [ndbd] INFO     -- Error handler shutting down system
2025-07-01 19:27:47 [ndbd] INFO     -- Error handler shutdown completed - exiting
2025-07-01 19:27:47 [ndbd] ALERT    -- Node 2: Forced node shutdown completed. Occured during startphase 5. Caused by error  2303: 'System error, node killed during node restart by other node(Internal error, programming error or missing error message,  please report a bug). Temporary error, restart node'.

从Node 2 killed this node because it could not copy a subscription during node restart. 可知是在复制订阅时出现,就是因为SQL节 点还在运行。

关闭SQL节点:

[root@mysqld data]# service mysql.server stop
Shutting down MySQL..... SUCCESS!

ndb_mgm> show
Cluster Configuration
---------------------
[ndbd(NDB)]     4 node(s)
id=2    @10.10.10.104  (mysql-5.7.48 ndb-7.5.36, Nodegroup: 0)
id=3    @10.10.10.105  (mysql-5.7.48 ndb-7.5.36, Nodegroup: 0, *)
id=5 (not connected, accepting connect from 10.10.10.106)
id=6 (not connected, accepting connect from 10.10.10.107)

[ndb_mgmd(MGM)] 1 node(s)
id=1    @10.10.10.102  (mysql-5.7.48 ndb-7.5.36)

[mysqld(API)]   1 node(s)
id=7 (not connected, accepting connect from 10.10.10.103)

重启数据节点:

ndb_mgm> 2 restart
Node 2: Node shutdown initiated
Node 2: Node shutdown completed, restarting, no start.
Node 2 is being restarted

ndb_mgm> Node 2: Start initiated (version 7.5.36)
Node 2: Started (version 7.5.36)

ndb_mgm> 3 restart
Node 3: Node shutdown initiated
Node 3: Node shutdown completed, restarting, no start.
Node 3 is being restarted

ndb_mgm> Node 3: Start initiated (version 7.5.36)
Node 3: Started (version 7.5.36)

ndb_mgm>

步骤4:执行所有集群API节点的滚动重启。

[root@mysqld data]# service mysql.server restart
 ERROR! MySQL server PID file could not be found!
Starting MySQL.. SUCCESS!
[root@mysqld data]#

ndb_mgm> show
Cluster Configuration
---------------------
[ndbd(NDB)]     4 node(s)
id=2    @10.10.10.104  (mysql-5.7.48 ndb-7.5.36, Nodegroup: 0, *)
id=3    @10.10.10.105  (mysql-5.7.48 ndb-7.5.36, Nodegroup: 0)
id=5 (not connected, accepting connect from 10.10.10.106)
id=6 (not connected, accepting connect from 10.10.10.107)

[ndb_mgmd(MGM)] 1 node(s)
id=1    @10.10.10.102  (mysql-5.7.48 ndb-7.5.36)

[mysqld(API)]   1 node(s)
id=7    @10.10.10.103  (mysql-5.7.48 ndb-7.5.36)

步骤5:执行新数据节点的初始启动。
从新数据节点的每个主机上的系统shell中,使用–initial选项启动数据节点,如下所示:

[root@ndbdc /]# ndbd -c 10.10.10.102 --initial
2025-07-01 19:49:26 [ndbd] INFO     -- Angel connected to '10.10.10.102:1186'
2025-07-01 19:49:26 [ndbd] INFO     -- Angel allocated nodeid: 5

[root@ndbdd /]#  ndbd -c 10.10.10.102 --initial
2025-07-01 19:49:34 [ndbd] INFO     -- Angel connected to '10.10.10.102:1186'
2025-07-01 19:49:34 [ndbd] INFO     -- Angel allocated nodeid: 6

与重新启动现有数据节点的情况不同,您可以并发地启动新的数据节点;你不需要等待一个开始后再开始另一个。

等到两个新数据节点都启动后,再继续下一步。一旦新的数据节点已经启动,您可以在管理客户端的SHOW命令的输出中看到,它们还不属于任何 节点组(如此处粗体所示):

ndb_mgm> Node 5: Started (version 7.5.36)
Node 6: Started (version 7.5.36)

ndb_mgm> show
Cluster Configuration
---------------------
[ndbd(NDB)]     4 node(s)
id=2    @10.10.10.104  (mysql-5.7.48 ndb-7.5.36, Nodegroup: 0, *)
id=3    @10.10.10.105  (mysql-5.7.48 ndb-7.5.36, Nodegroup: 0)
id=5    @10.10.10.106  (mysql-5.7.48 ndb-7.5.36, no nodegroup)
id=6    @10.10.10.107  (mysql-5.7.48 ndb-7.5.36, no nodegroup)

[ndb_mgmd(MGM)] 1 node(s)
id=1    @10.10.10.102  (mysql-5.7.48 ndb-7.5.36)

[mysqld(API)]   1 node(s)
id=7    @10.10.10.103  (mysql-5.7.48 ndb-7.5.36)

步骤6:创建一个新的节点组。
可以通过在集群管理客户端中发出CREATE NODEGROUP命令来实现这一点。该命令将包含在新节点组中的数据节点的节点id的逗号分隔列表作为参 数,如下所示:

ndb_mgm> CREATE NODEGROUP 5,6
Nodegroup 1 created

通过再次发出SHOW,您可以验证数据节点5和6已经加入了新的节点组:

ndb_mgm> show
Cluster Configuration
---------------------
[ndbd(NDB)]     4 node(s)
id=2    @10.10.10.104  (mysql-5.7.48 ndb-7.5.36, Nodegroup: 0, *)
id=3    @10.10.10.105  (mysql-5.7.48 ndb-7.5.36, Nodegroup: 0)
id=5    @10.10.10.106  (mysql-5.7.48 ndb-7.5.36, Nodegroup: 1)
id=6    @10.10.10.107  (mysql-5.7.48 ndb-7.5.36, Nodegroup: 1)

[ndb_mgmd(MGM)] 1 node(s)
id=1    @10.10.10.102  (mysql-5.7.48 ndb-7.5.36)

[mysqld(API)]   1 node(s)
id=7    @10.10.10.103  (mysql-5.7.48 ndb-7.5.36)

步骤7:重新分配集群数据。
创建节点组时,现有数据和索引不会自动分发到新节点组的数据节点,这可以通过发出适当的在管理客户端中使用REPORT命令:

ndb_mgm> ALL REPORT MEMORY
Node 2: Data usage is 0%(173 32K pages of total 32768)
Node 2: Index usage is 0%(80 8K pages of total 32800)
Node 3: Data usage is 0%(173 32K pages of total 32768)
Node 3: Index usage is 0%(80 8K pages of total 32800)
Node 5: Data usage is 0%(17 32K pages of total 32768)
Node 5: Index usage is 0%(0 8K pages of total 32800)
Node 6: Data usage is 0%(17 32K pages of total 32768)
Node 6: Index usage is 0%(0 8K pages of total 32800)

通过对每个NDB表执行ALTER TABLE … ALGORITHM=INPLACE, REORGANIZE PARTITION语句,可以在所有数据节点之间重新分布数据。

mysql> SELECT TABLE_SCHEMA, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE ENGINE = 'NDBCLUSTER';
+--------------+-----------------------+
| TABLE_SCHEMA | TABLE_NAME            |
+--------------+-----------------------+
| jycs         | btest                 |
| jycs         | city                  |
| jycs         | country               |
| jycs         | countrylanguage       |
| jycs         | ctest                 |
| jycs         | example               |
| jycs         | fish                  |
| mysql        | ndb_apply_status      |
| mysql        | ndb_index_stat_head   |
| mysql        | ndb_index_stat_sample |
| world        | city                  |
| world        | country               |
| world        | countrylanguage       |
+--------------+-----------------------+
13 rows in set (0.02 sec)

mysql> ALTER TABLE jycs.btest ALGORITHM=INPLACE, REORGANIZE PARTITION;
Query OK, 0 rows affected (21.38 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> ALTER TABLE jycs.city ALGORITHM=INPLACE, REORGANIZE PARTITION;
Query OK, 0 rows affected (8.68 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> ALTER TABLE jycs.country ALGORITHM=INPLACE, REORGANIZE PARTITION;
Query OK, 0 rows affected (8.50 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> ALTER TABLE jycs.countrylanguage ALGORITHM=INPLACE, REORGANIZE PARTITION;
Query OK, 0 rows affected (8.56 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> ALTER TABLE jycs.ctest ALGORITHM=INPLACE, REORGANIZE PARTITION;
Query OK, 0 rows affected (6.97 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> ALTER TABLE jycs.example ALGORITHM=INPLACE, REORGANIZE PARTITION;
Query OK, 0 rows affected (12.81 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> ALTER TABLE jycs.fish ALGORITHM=INPLACE, REORGANIZE PARTITION;
Query OK, 0 rows affected (15.82 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> ALTER TABLE mysql.ndb_apply_status ALGORITHM=INPLACE, REORGANIZE PARTITION;
Query OK, 0 rows affected (4.68 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> ALTER TABLE mysql.ndb_index_stat_head ALGORITHM=INPLACE, REORGANIZE PARTITION;
Query OK, 0 rows affected (5.17 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> ALTER TABLE mysql.ndb_index_stat_sample ALGORITHM=INPLACE, REORGANIZE PARTITION;
Query OK, 0 rows affected (6.23 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> ALTER TABLE world.city ALGORITHM=INPLACE, REORGANIZE PARTITION;
Query OK, 0 rows affected (7.17 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> ALTER TABLE world.country ALGORITHM=INPLACE, REORGANIZE PARTITION;
Query OK, 0 rows affected (8.48 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> ALTER TABLE world.countrylanguage ALGORITHM=INPLACE, REORGANIZE PARTITION;
Query OK, 0 rows affected (7.98 sec)
Records: 0  Duplicates: 0  Warnings: 0


ndb_mgm> ALL REPORT MEMORY
Node 2: Data usage is 0%(161 32K pages of total 32768)
Node 2: Index usage is 0%(76 8K pages of total 32800)
Node 3: Data usage is 0%(161 32K pages of total 32768)
Node 3: Index usage is 0%(76 8K pages of total 32800)
Node 5: Data usage is 0%(113 32K pages of total 32768)
Node 5: Index usage is 0%(58 8K pages of total 32800)
Node 6: Data usage is 0%(113 32K pages of total 32768)
Node 6: Index usage is 0%(58 8K pages of total 32800)

此外,对于每个表,应该在ALTER table语句之后加上一个OPTIMIZE table语句来回收浪费的空间。

mysql> OPTIMIZE TABLE jycs.btest ;
+------------+----------+----------+----------+
| Table      | Op       | Msg_type | Msg_text |
+------------+----------+----------+----------+
| jycs.btest | optimize | status   | OK       |
+------------+----------+----------+----------+
1 row in set (0.06 sec)

mysql> OPTIMIZE TABLE jycs.city;
+-----------+----------+----------+----------+
| Table     | Op       | Msg_type | Msg_text |
+-----------+----------+----------+----------+
| jycs.city | optimize | status   | OK       |
+-----------+----------+----------+----------+
1 row in set (0.01 sec)

mysql> OPTIMIZE TABLE jycs.country;
+--------------+----------+----------+----------+
| Table        | Op       | Msg_type | Msg_text |
+--------------+----------+----------+----------+
| jycs.country | optimize | status   | OK       |
+--------------+----------+----------+----------+
1 row in set (0.00 sec)

mysql> OPTIMIZE TABLE jycs.countrylanguage;
+----------------------+----------+----------+----------+
| Table                | Op       | Msg_type | Msg_text |
+----------------------+----------+----------+----------+
| jycs.countrylanguage | optimize | status   | OK       |
+----------------------+----------+----------+----------+
1 row in set (0.00 sec)

mysql> OPTIMIZE TABLE jycs.ctest;
+------------+----------+----------+----------+
| Table      | Op       | Msg_type | Msg_text |
+------------+----------+----------+----------+
| jycs.ctest | optimize | status   | OK       |
+------------+----------+----------+----------+
1 row in set (0.00 sec)

mysql> OPTIMIZE TABLE jycs.example;
+--------------+----------+----------+----------+
| Table        | Op       | Msg_type | Msg_text |
+--------------+----------+----------+----------+
| jycs.example | optimize | status   | OK       |
+--------------+----------+----------+----------+
1 row in set (0.01 sec)

mysql> OPTIMIZE TABLE jycs.fish;
+-----------+----------+----------+----------+
| Table     | Op       | Msg_type | Msg_text |
+-----------+----------+----------+----------+
| jycs.fish | optimize | status   | OK       |
+-----------+----------+----------+----------+
1 row in set (0.07 sec)

mysql> OPTIMIZE TABLE mysql.ndb_apply_status;
+------------------------+----------+----------+----------+
| Table                  | Op       | Msg_type | Msg_text |
+------------------------+----------+----------+----------+
| mysql.ndb_apply_status | optimize | status   | OK       |
+------------------------+----------+----------+----------+
1 row in set (0.01 sec)

mysql> OPTIMIZE TABLE mysql.ndb_index_stat_head;
+---------------------------+----------+----------+----------+
| Table                     | Op       | Msg_type | Msg_text |
+---------------------------+----------+----------+----------+
| mysql.ndb_index_stat_head | optimize | status   | OK       |
+---------------------------+----------+----------+----------+
1 row in set (0.00 sec)

mysql> OPTIMIZE TABLE mysql.ndb_index_stat_sample;
+-----------------------------+----------+----------+----------+
| Table                       | Op       | Msg_type | Msg_text |
+-----------------------------+----------+----------+----------+
| mysql.ndb_index_stat_sample | optimize | status   | OK       |
+-----------------------------+----------+----------+----------+
1 row in set (0.01 sec)

mysql> OPTIMIZE TABLE world.city;
+------------+----------+----------+----------+
| Table      | Op       | Msg_type | Msg_text |
+------------+----------+----------+----------+
| world.city | optimize | status   | OK       |
+------------+----------+----------+----------+
1 row in set (0.01 sec)

mysql> OPTIMIZE TABLE world.country;
+---------------+----------+----------+----------+
| Table         | Op       | Msg_type | Msg_text |
+---------------+----------+----------+----------+
| world.country | optimize | status   | OK       |
+---------------+----------+----------+----------+
1 row in set (0.01 sec)

mysql> OPTIMIZE TABLE world.countrylanguage;
+-----------------------+----------+----------+----------+
| Table                 | Op       | Msg_type | Msg_text |
+-----------------------+----------+----------+----------+
| world.countrylanguage | optimize | status   | OK       |
+-----------------------+----------+----------+----------+
1 row in set (0.01 sec)

在ALL REPORT MEMORY的输出中执行这些语句后,您可以看到数据和索引现在在所有集群数据节点之间重新分布,如下所示:

ndb_mgm> ALL REPORT MEMORY
Node 2: Data usage is 0%(161 32K pages of total 32768)
Node 2: Index usage is 0%(76 8K pages of total 32800)
Node 3: Data usage is 0%(161 32K pages of total 32768)
Node 3: Index usage is 0%(76 8K pages of total 32800)
Node 5: Data usage is 0%(113 32K pages of total 32768)
Node 5: Index usage is 0%(58 8K pages of total 32800)
Node 6: Data usage is 0%(113 32K pages of total 32768)
Node 6: Index usage is 0%(58 8K pages of total 32800)

由于一次只能执行一个对NDBCLUSTER表的DDL操作,因此必须等待每个ALTER TABLE…REORGANIZE PARTITION语句在发出下一个语句之前完成。

对于在添加新数据节点之后创建的 NDBCLUSTER 表,无需发出 ALTER TABLE … REORGANIZE PARTITION 语句;添加到此类表中的数据会自动在 所有数据节点之间进行分配。然而,在添加新节点之前就已存在的 NDBCLUSTER 表中,无论是现有数据还是新数据都不会使用新节点进行分配, 除非使用 ALTER TABLE … REORGANIZE PARTITION 对这些表进行重组。