All You Need to Know About MySQL Partitions (Part 2)

Part 1中,已经详细介绍了MySQL中的几种分区方式。在这里,详细介绍一下分区的各种操作。同样,这篇文章是对官方文档的梳理。

3. 分区管理

对于一个一分区的表来说,分区管理包括添加分区、删除分区、合并分区、拆分分区以及,改变分区方式等。对于这些操作,都需要使用ALTER TABLE语句来进行操作。需要注意的是,每一个ALTER TABLE语句只能有一个分区相关的操作,而不能在一个ALTER TABLE语句中有多个分区操作。

3.1 RANGE分区和LIST分区管理

对Range分区和List分区操作是一样的。

3.1.1 删除分区

比如下面的例子:

CREATE TABLE tr (id INT, name VARCHAR(50), purchased DATE)
PARTITION BY RANGE( YEAR(purchased) ) (
    PARTITION p0 VALUES LESS THAN (1990),
    PARTITION p1 VALUES LESS THAN (1995),
    PARTITION p2 VALUES LESS THAN (2000),
    PARTITION p3 VALUES LESS THAN (2005),
    PARTITION p4 VALUES LESS THAN (2010),
    PARTITION p5 VALUES LESS THAN (2015)
);

可以这样来删除分区:

ALTER TABLE tr DROP PARTITION p2;

牢记,删除一个分区,那么这个分区里的所有数据也会被删除。所以,这需要有DROP权限。

如果只想删除某个分区的数据而保留这个分区,可以使用truncate

ALTER TABLE tr TRUNCATE PARTITION p2;

这样,分区p2仍然保留,只是分区内的数据删除了。

为了查看一张表的分区情况,可以使用SHOW CREATE TABLE

SHOW CREATE TABLE tr;

删除List分区也和Range分区操作一样。

3.1.2 添加分区

加入已有下面的分区表:

CREATE TABLE members (
    id INT,
    fname VARCHAR(25),
    lname VARCHAR(25),
    dob DATE
)
PARTITION BY RANGE( YEAR(dob) ) (
    PARTITION p0 VALUES LESS THAN (1980),
    PARTITION p1 VALUES LESS THAN (1990),
    PARTITION p2 VALUES LESS THAN (2000)
);

可以使用下面的语句来添加分区:

ALTER TABLE members ADD PARTITION (PARTITION p3 VALUES LESS THAN (2010));

不过需要注意的是,添加分区只能在高范围添加,在低范围添加分区不会成功。

如果添加下面的分区:

ALTER TABLE members ADD PARTITION (PARTITION p3 VALUES LESS THAN (1970));

这会导致失败。

在一条ALTER TABLE语句中,可以添加多个分区:

ALTER TABLE employees ADD PARTITION (
    PARTITION p5 VALUES LESS THAN (2010),
    PARTITION p6 VALUES LESS THAN MAXVALUE
);

3.1.3 调整分区

不过,如果觉得之前的分区范围不合理的话,可以调整分区:

ALTER TABLE members
    REORGANIZE PARTITION p0 INTO (
        PARTITION n0 VALUES LESS THAN (1970),
        PARTITION n1 VALUES LESS THAN (1980)
);

对于List分区来说也是一样的。

同样也可以用来合并分区:

ALTER TABLE members REORGANIZE PARTITION s0,s1 INTO (
    PARTITION p0 VALUES LESS THAN (1970)
);

在重新划分与合并分区过程中,不会有数据丢失。

使用REORGANIZE PARTITION语句对分区进行调整时,需要注意以下几点:

  • 对于Range分区和List分区,调整前的范围不能丢弃;
  • 这个语句不能用来修改分区的方式,以及分区定义中的列以及表达式。

3.2 HASH分区和KEY分区管理

Hash分区与Key分区的管理类似,但是和Range分区与List分区有些不同。这里仅仅介绍Hash分区与Key分区的修改。

Hash分区和Key分区不能直接删除分区。假如一张表划分成12个分区:

CREATE TABLE clients (
    id INT,
    fname VARCHAR(30),
    lname VARCHAR(30),
    signed DATE
)
PARTITION BY HASH( MONTH(signed) )
PARTITIONS 12;

如果想改成8个分区,那么可以这样:

ALTER TABLE clients COALESCE PARTITION 4;

对于Hash分区、Key分区、Liner Hash分区和Liner Key分区来说,都可以使用COALESCE来合并分区。

COALESCE是用来减少分区的,后面的数字就是要减少的分区数量,这个数字不能比调整前的分区数量大,不然会失败。

如果想增加分区的数量,可以使用ALTER TABLE ... ADD PARTITION

ALTER TABLE clients ADD PARTITION PARTITIONS 6;

同样,后面的数字是需要增加的分区数量。

3.3 交换分区

在MySQL中,可以把一个分区表中的某个分区与另一个没有分区的表进行数据交换。不过,对于分区表pt、表pt中要交换的分区p和普通表nt需要满足下面的条件:

  • nt没有分区;
  • nt不是临时表;
  • 除了一个分区一个没分区外,表ptnt的结构一样;
  • nt没有外键,同时也没有别的表的外键引用表nt
  • nt中的数据需要满足分区p的定义,如果使用WITHOUT VALIDATION的话就不需要了;
  • 对于InnoDB表,ptnt的行格式需要一致;

为了成功交换分区,需要有ALTERINSERTCREATEDROP的权限。

同时需要注意:

  • ALTER TABLE ... EXCHANGE PARTITION语句的执行不会触发两个表的触发器;
  • 表中的AUTO_INCREMENT字段的值会被重置。

语法如下:

ALTER TABLE pt
    EXCHANGE PARTITION p
    WITH TABLE nt;

3.3.1 与一个未分区的表交换分区

定义一张分区表如下并插入几条数据:

CREATE TABLE e (
    id INT NOT NULL,
    fname VARCHAR(30),
    lname VARCHAR(30)
)
    PARTITION BY RANGE (id) (
        PARTITION p0 VALUES LESS THAN (50),
        PARTITION p1 VALUES LESS THAN (100),
        PARTITION p2 VALUES LESS THAN (150),
        PARTITION p3 VALUES LESS THAN (MAXVALUE)
);

INSERT INTO e VALUES
    (1669, "Jim", "Smith"),
    (337, "Mary", "Jones"),
    (16, "Frank", "White"),
    (2005, "Linda", "Black");

然后创建一张和e结构一样但是不分区的表e2

可以通过下面的语句将表e中的分区p0中的数据交换到表e2中:

ALTER TABLE e EXCHANGE PARTITION p0 WITH TABLE e2;

这样,分区p0中的数据就被交换到表e2中了,可以通过查看数据来验证。

既然是交换,那么表e2中不一定非要是空的,也可以有数据。经过上面的交换之后,p0分区中没有数据了,而e2中有一条数据:

SELECT * FROM e2;
+----+-------+-------+
| id | fname | lname |
+----+-------+-------+
| 16 | Frank | White |
+----+-------+-------+

现在在p0中插入一条数据:

INSERT INTO e VALUES (41, "Michael", "Green");

然后再交换分区:

ALTER TABLE e EXCHANGE PARTITION p0 WITH TABLE e2;

这样,分区p0中的数据就和e2中的数据交换了,可以通过查看数据来验证。

3.3.2 不符合分区定义的数据

在进行分区交换的时候,未分区表nt中的数据要满足待交换分区p的定义,如果不满足的话,执行交换分区会失败。

如果表nt中确实有不满足分区定义的数据但还是需要进行交换,那么可以使用WITHOUT VALIDATION

ALTER TABLE e EXCHANGE PARTITION p0 WITH TABLE e2 WITHOUT VALIDATION;

需要注意的是,在MySQL 5.6 中不支持WITHOUT VALIDATION,表nt中的数据一定要满足分区定义,否则执行不成功。

3.3.3 交换分区不校验数据

如果在交换过程中使用WITHOUT VALIDATION的话,可以加快交换的速度,但是并没有比校验快多少。

3.3.4 交换子分区

同样,也可以交换一个子分区。比如下面一个有子分区的表:

CREATE TABLE es (
    id INT NOT NULL,
    fname VARCHAR(30),
    lname VARCHAR(30)
)
    PARTITION BY RANGE (id)
    SUBPARTITION BY KEY (lname)
    SUBPARTITIONS 2 (
        PARTITION p0 VALUES LESS THAN (50),
        PARTITION p1 VALUES LESS THAN (100),
        PARTITION p2 VALUES LESS THAN (150),
        PARTITION p3 VALUES LESS THAN (MAXVALUE)
);

虽然我们没有显示定义子分区的名字,不过MySQL还是会自动给子分区一个名字的。可以通过下面的方式查看子分区的名字:

SELECT PARTITION_NAME, SUBPARTITION_NAME, TABLE_ROWS FROM INFORMATION_SCHEMA.PARTITIONS WHERE TABLE_NAME = 'es';

然后就可以交换子分区了:

ALTER TABLE es EXCHANGE PARTITION p3sp0 WITH TABLE es2;

和交换分区一样。

只不过,如果一个表有子分区,那么就只能对子分区进行交换,而不能对分区进行交换:

ALTER TABLE es EXCHANGE PARTITION p3 WITH TABLE es2;

3.4 分区的维护

对于分区的维护,可以从以下几个方面进行。

3.4.1 重建分区

这里的重建不是说重新定义分区,而是为了避免磁盘碎片化而重新整理分区的内容:

ALTER TABLE t1 REBUILD PARTITION p0, p1;

这个过程的效果就和删除分区的数据然后重新插入是一样的。

3.4.2 优化分区

如果对一个分区进行了大量的删除操作,或者对使用变长字段(VARCHARBLOBTEXT)进行了更新操作,那么可以使用如下的语句来进行分区优化:

ALTER TABLE t1 OPTIMIZE PARTITION p0, p1;

语句OPTIMIZE PARTITION的效果就和一起执行CHECK PARTITIONANALYZE PARTITIONREPAIR PARTITION一样。

不过对于InnoDB来说,不支持对于指定的分区进行上述操作,不过可以使用ALTER TABLE ... OPTIMIZE PARTITION来替代。

3.5 获取分区的详细信息

对于一个分区表,可以使用如下几种方式来获取关于分区的详细信息。

3.5.1 SHOW CREATE TABLE

SHOW CREATE TABLE显示创建表时候的语句,如果一个表使用了分区,那么这里也会有分区的详细定义信息:

mysql> SHOW CREATE TABLE trb3\G
*************************** 1. row ***************************
       Table: trb3
Create Table: CREATE TABLE `trb3` (
  `id` int(11) DEFAULT NULL,
  `name` varchar(50) DEFAULT NULL,
  `purchased` date DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
/*!50100 PARTITION BY RANGE (YEAR(purchased))
(PARTITION p0 VALUES LESS THAN (1990) ENGINE = InnoDB,
 PARTITION p1 VALUES LESS THAN (1995) ENGINE = InnoDB,
 PARTITION p2 VALUES LESS THAN (2000) ENGINE = InnoDB,
 PARTITION p3 VALUES LESS THAN (2005) ENGINE = InnoDB) */
0 row in set (0.00 sec)

这就是一个简单的例子。可以看出这个表使用的是Range分区,每个分区的详细范围定义也给出来了。

3.5.2 SHOW TABLE STATUS

使用SHOW TABLE STATUS给出的关于分区的信息很少,只只有一个字段来标识这个表是否是分区的:

SHOW TABLE STATUS test;

这里的test是库名,给出库里所有表的信息。这是一个分区表的例子:

           Name: e
         Engine: InnoDB
        Version: 10
     Row_format: Compact
           Rows: 6
 Avg_row_length: 10922
    Data_length: 65536
Max_data_length: 0
   Index_length: 0
      Data_free: 0
 Auto_increment: NULL
    Create_time: 2020-03-18 21:43:16
    Update_time: NULL
     Check_time: NULL
      Collation: utf8_general_ci
       Checksum: NULL
 Create_options: partitioned
        Comment:

字段Create_options的值是partitoned,标识这个表是分区的。

3.5.3 INFORMATION_SCHEMA.PARTITIONS表

为了获得详细的信息,可以查询INFORMATION_SCHEMA.PARTITIONS表。

这个表里的每一行数据都是一个分区或子分区的信息。

这个表的有下面这些字段:

Field Type Null 含义
TABLE_CATALOG varchar(512) NO 总是def
TABLE_SCHEMA varchar(64) NO 分区所属库
TABLE_NAME varchar(64) NO 分区所属表
PARTITION_NAME varchar(64) YES 分区名
SUBPARTITION_NAME varchar(64) YES 有子分区的话就是子分区名,否则是NULL
PARTITION_ORDINAL_POSITION bigint(21) unsigned YES 分区的下标,按照定义从1开始,如果分区发生更改这个下标也会有变化
SUBPARTITION_ORDINAL_POSITION bigint(21) unsigned YES 子分区的下标,和分区的下标类似
PARTITION_METHOD varchar(18) YES 分区类型,可取值:RANGE, LIST, HASH, LINEAR HASH, KEY, or LINEAR KEY
SUBPARTITION_METHOD varchar(12) YES 子分区类型,可取值:HASH, LINEAR HASH, KEY, or LINEAR KEY
PARTITION_EXPRESSION longtext YES 分区表达式,如果分区定义是PARTITION BY HASH(c1 + c2),那么这个值就是c1 + c2
SUBPARTITION_EXPRESSION longtext YES 子分区表达式,和分区表达式类似
PARTITION_DESCRIPTION longtext YES 对于Range分区和List分区的范围取值,其它类型的分区这个值为NULL
TABLE_ROWS bigint(21) unsigned NO 这个分区数据的行数
AVG_ROW_LENGTH bigint(21) unsigned NO 平均每行的大小(单位是Byte)
DATA_LENGTH bigint(21) unsigned NO 这个分区所有数据的总大小(单位是Byte)
MAX_DATA_LENGTH bigint(21) unsigned YES 这个分区中最大行的大小(单位是Byte)
INDEX_LENGTH bigint(21) unsigned NO 这个分区的索引大小(单位是Byte)
DATA_FREE bigint(21) unsigned NO 分配给这个分区的未用空间大小(单位是Byte)
CREATE_TIME datetime YES 分区创建时间
UPDATE_TIME datetime YES 分区最近更新时间
CHECK_TIME datetime YES 分区Check时间
CHECKSUM bigint(21) unsigned YES checksum
PARTITION_COMMENT varchar(80) NO 分区注释
NODEGROUP varchar(12) NO 该分区所属的节点组,和NDB有关,其它的都是0
TABLESPACE_NAME varchar(64) YES 分区所属的表空间除了NDB外都是DEFAULT

从上表可以看出关于分区的信息还是很丰富的,平时我们也用不到这么多,基本上下面的例子就满足所有的需求了:

SELECT PARTITION_NAME, SUBPARTITION_NAME, TABLE_ROWS FROM INFORMATION_SCHEMA.PARTITIONS WHERE TABLE_NAME = 'e';

这里列出了分区名、子分区名以及对应的分区的行数。

3.5.4 SELECT EXPLAIN

当我们对分区表进行查询的时候,可以使用EXPLAIN来看一下查询使用到的分区。

创建下面的分区表:

CREATE TABLE trb1 (id INT, name VARCHAR(50), purchased DATE)
    PARTITION BY RANGE(id)
    (
        PARTITION p0 VALUES LESS THAN (3),
        PARTITION p1 VALUES LESS THAN (7),
        PARTITION p2 VALUES LESS THAN (9),
        PARTITION p3 VALUES LESS THAN (11)
    );

INSERT INTO trb1 VALUES
    (1, 'desk organiser', '2003-10-15'),
    (2, 'CD player', '1993-11-05'),
    (3, 'TV set', '1996-03-10'),
    (4, 'bookcase', '1982-01-10'),
    (5, 'exercise bike', '2004-05-09'),
    (6, 'sofa', '1987-06-05'),
    (7, 'popcorn maker', '2001-11-22'),
    (8, 'aquarium', '1992-08-04'),
    (9, 'study desk', '1984-09-16'),
    (10, 'lava lamp', '1998-12-25');

使用EXPLAIN

EXPLAIN SELECT * FROM trb1\G;

结果:

*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: trb1
   partitions: p0,p1,p2,p3
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 10
        Extra: Using filesort

可以看到,这个查询将会用到所有的分区。而这个查询EXPLAIN SELECT * FROM trb1 WHERE id < 5\G:

*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: trb1
   partitions: p0,p1
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 10
        Extra: Using where

用到了前两个分区。

4. 分区修剪

分区修剪(Partition Pruning)是一个策略,简单来说就是,忽略掉那些不可能匹配的分区,以此来减小搜索的数据。

以下面的分区表为例:

CREATE TABLE t1 (
    fname VARCHAR(50) NOT NULL,
    lname VARCHAR(50) NOT NULL,
    region_code TINYINT UNSIGNED NOT NULL,
    dob DATE NOT NULL
)
PARTITION BY RANGE( region_code ) (
    PARTITION p0 VALUES LESS THAN (64),
    PARTITION p1 VALUES LESS THAN (128),
    PARTITION p2 VALUES LESS THAN (192),
    PARTITION p3 VALUES LESS THAN MAXVALUE
);

考虑下面的查询语句:

SELECT fname, lname, region_code, dob
    FROM t1
    WHERE region_code > 125 AND region_code < 130;

查询的条件就是region_code,而这个字段刚好是分区的字段,这样就可以使用分区修剪了。条件是125 < region_code < 130,通过查看分区定义可知,所选数据只可能在p1p2分区中。

当一个查询条件可以总结成下面的形式时,就可以是用分区修剪:

  • partition_column = constant
  • partition_column IN (constant1, constant2, ..., constantN)

如果查询条件是等于,那么可以划为第一种情况;如果是不等式,那么可以划为第二种情况。

除了SELECT外,DELETEUPDATE也支持分区修剪。

和Range分区一样,List分区也可以像Range分区一样使用分区修剪。

对于Hash分区和Key分区来说,分区修剪只能用于整数类型的分区字段。

下面是一个Hash分区的例子:

CREATE TABLE t4 (
    fname VARCHAR(50) NOT NULL,
    lname VARCHAR(50) NOT NULL,
    region_code TINYINT UNSIGNED NOT NULL,
    dob DATE NOT NULL
)
PARTITION BY KEY(region_code)
PARTITIONS 8;

语句:

UPDATE t4 WHERE region_code = 7;

可以使用分区修剪。但是对于:

DELETE FROM t4 WHERE region_code BETWEEN 4 AND 12;

则不能使用分区修剪,因为这个查询的范围是9个值,但是表t4只有8个分区。

5. 指定分区

在分区修剪中,我们知道MySQL会对一些语句进行优化,只对符合条件的分区进行操作。分区修剪是自动完成的,我们还可以指定分区。

除了SELECT外,还有好多支持指定分区的操作:DELETEUPDATEINSERTREPLACELOAD DATALOAD XML等。

在语句中指定分区的格式很简单:

      PARTITION (partition_names)

      partition_names:
          partition_name, ...

比如:

SELECT * FROM employees PARTITION (p1);

指定分区之后,就只会返回指定分区的数据。

指定分区需要知道分区的名字,着对于Range分区和List分区来说很简单,因为表创建分区的时候就会指定名字。但是对于Hash分区和Key分区来说,没有指定名字。MySQL默认的分区名字是这样的p0p1p2等等。对于子分区,默认的名字是p0sp0p0sp1p0sp2等。

当然也可以直接查询SHOW CREATE TABLE ... 来查看分区定义,或者查询SELECT PARTITION_NAME, SUBPARTITION_NAME FROM INFORMATION_SCHEMA.PARTITIONS WHERE TABLE_NAME = 'e'来查看分区的名字。

指定分区也可以指定多个分区:

SELECT * FROM employees PARTITION (p0, p2);

更新删除插入操作也可以指定分区。

不过需要注意的是,对于插入多条语句来说,如果使用InnoDB,如果多条数据中有一条不符合指定分区的定义,那么即使有符合的也不会插入成功。

6. 分区的限制与局限

在MySQL中,对分区的支持是在存储引擎上的,且只有InnoDB和NDB支持分区,其它的存储引擎是不支持的。

使用分区需要注意下面几个方面的限制于局限。

6.1 分区与键

分区表达式中使用的所有字段,必须在所有的唯一键中(包括主键,主键也是唯一键)。

反过来,对于一个已分区的表添加唯一键,也只能添加分区表达式中使用的字段。

比如下面两张表:

CREATE TABLE t1 (
    col1 INT NOT NULL,
    col2 DATE NOT NULL,
    col3 INT NOT NULL,
    col4 INT NOT NULL,
    UNIQUE KEY (col1, col2)
)
PARTITION BY HASH(col3)
PARTITIONS 4;

CREATE TABLE t2 (
    col1 INT NOT NULL,
    col2 DATE NOT NULL,
    col3 INT NOT NULL,
    col4 INT NOT NULL,
    UNIQUE KEY (col1),
    UNIQUE KEY (col3)
)
PARTITION BY HASH(col1 + col3)
PARTITIONS 4;

都不能成功。第一个:分区使用的字段col3不在唯一键中;第二个:虽然col1col3都是唯一键,但是不同时在所有的唯一键中。

这样就可以了:

CREATE TABLE t1 (
    col1 INT NOT NULL,
    col2 DATE NOT NULL,
    col3 INT NOT NULL,
    col4 INT NOT NULL,
    UNIQUE KEY (col1, col2, col3)
)
PARTITION BY HASH(col3)
PARTITIONS 4;

CREATE TABLE t2 (
    col1 INT NOT NULL,
    col2 DATE NOT NULL,
    col3 INT NOT NULL,
    col4 INT NOT NULL,
    UNIQUE KEY (col1, col3)
)
PARTITION BY HASH(col1 + col3)
PARTITIONS 4;

着对于PRIMARY KEY来说是一样的。

如果表没有自定义的唯一键的话,那么就没有这个唯一键的限制了,任何满足分区要求的字段都可以使用。

反过来,如果一张表已经分区了,添加唯一键的时候也要满足这个限制。比如下面的分区表:

CREATE TABLE t_no_pk (c1 INT, c2 INT)
PARTITION BY RANGE(c1) (
    PARTITION p0 VALUES LESS THAN (10),
    PARTITION p1 VALUES LESS THAN (20),
    PARTITION p2 VALUES LESS THAN (30),
    PARTITION p3 VALUES LESS THAN (40)
);

要添加下面的主键:

ALTER TABLE t_no_pk ADD PRIMARY KEY(c1);

没问题。因为这个主键c1就是分区表达式使用的字段。

但下面这个就不成功了:

ALTER TABLE t_no_pk ADD PRIMARY KEY(c2);

会给出这个错误:

ERROR 1503 (HY000): A PRIMARY KEY must include all columns in the table's partitioning function

6.2 分区表达式支持的函数

MySQL中,只有下面列出的函数支持在分区表达式中使用:

函数 含义 函数 含义
ABS(x) 返回x的绝对值 MOD(N, M) 返回N%M
CEILING(x) 返回不小于x的最小整数 MONTH(x) 返回x的月份(1到12)
DATEDIFF(x, y) 返回xy之间的日期差 QUARTER(x) 返回x的季度(1到4)
DAY(x) DAYOFMONTH(X)一样 SECOND(x) 返回x的秒数(0到59)
DAYOFMONTH(x) 返回日期x当前月的第几天(1到31) TIME_TO_SEC(seconds) 将给定的秒数转成HH:MM:SS的格式
DAYOFWEEK(x) 返回星期,周日是1,周一是2,周六是7 TO_DAYS(x) 给定一个日期,返回总天数(从公元算起)
DAYOFYEAR(x) 返回日期x当前年的第几天(1到366) TO_SECONDS(x) 给定一个日期,返回总秒数(从公元算起)
EXTRACT(x) 从给定的日期中返回指定的格式 UNIX_TIMESTAMP(x) 返回给定日期的时间戳
FLOOR(x) 返回不大于x的最大整数 WEEKDAY(x) 返回星期(和DAYOFWEEK()不一样,这里周一是0,周日是6)
HOUR(x) 返回时间x的小时数(0到23) YEAR(x) 返回给定日期的年份
MICROSECOND(x) 返回x的毫秒数(0到999) YEARWEEK(x) 返回给定日期的周数(1到53)
MINUTE(x) 返回x的分钟数(0到59)

Conclusion

最后,以我个人的经验来看,分区是数据库优化的最后一个步骤。对于需要处理大量的流水型数据来说,使用Range分区是很有用的。在使用分区之前,也需要考虑一下分区的限制,比如唯一索引的限制、不允许外键等。


 Previous
《中国历代政治得失》笔记 《中国历代政治得失》笔记
利用周末一个下午的时间,重新刷了一遍钱穆的《中国历代政治得失》。能说“西方在政治经验上一般都比较短浅”这话的,只有中国人。这不是自夸,这是事实。从整本书中都能看出钱穆对中国古代政治经验的一种称赞。他推崇汉代的地方政府组织,推崇唐代的中央政
2020-03-30
Next 
All You Need to Know About MySQL Partitions (Part 1) All You Need to Know About MySQL Partitions (Part 1)
什么是分区?为什么要分区?该怎么分区?分区有什么好处?使用分区应该注意什么?通过对MySQL文档的梳理,在这篇文章以及Part 2中详细介绍一下关于MySQL分区的方方面面。 1. Overview of Paritioning in
2020-03-24
  You Will See...