分区的过程是将一个表或索引分解为多个更小、更可管理的部分。就访问数据库的应用而言,从逻辑上讲,只有一个表或索引,但是在物理上这个表或索引可能由数十个物理分区组成。每个分区都是独立的对象,可以独自处理,也可以作为一个更大的对象的一部分进行处理。
MySQL数据库支持的分区类型为水平分区,并不支持垂直分区。此外,MySQL数据库的分区是局部分区索引,一个分区中既存放了数据又存放了索引。

可以通过以下命令查看当前数据库是否启用了分区功能:

mysql> SHOW VARIABLES LIKE '%partition%';
当前MySQL数据库支持以下几种类型的分区
分区 分区标准
RANGE分区 行数据基于属于一个给定连续区间的列值被放入分区
LIST分区 和RANGE分区类型一样,只是LIST分区面向的是离散的值
HASH分区 根据用户自定义的表达式的返回值来进行分区
KEY分区 根据MySQL数据库提供的哈希函数进行分区

RANGE分区

例:创建一个表id小于10时,数据放入p0分区大于10小于20放入p1分区。

CREATE TABLE t(id INT)ENGINE=INNODB
PARTITION BY RANGE(id)(
PARTITION p0 VALUES LESS THAN (10),
PARTITION p1 VALUES LESS THAN (20));

此时,在磁盘的物理文件中,表不再由一个ibd文件组成了,而是由分区时的各个分区ibd文件组成,如下:

-rw-rw----  1 mysql mysql 580 Nov  1 17:48 t.frm
-rw-rw----  1 mysql mysql  36 Nov  1 17:48 t.par
-rw-rw----  1 mysql mysql 64K Nov  1 17:48 t#P#p0.ibd
-rw-rw----  1 mysql mysql 64K Nov  1 17:48 t#P#p1.ibd

接着插入数据:

mysql> INSERT INTO t SELECT 9;
mysql> INSERT INTO t SELECT 10;
mysql> INSERT INTO t SELECT 15;

查询下PARTITIONS

*************************** 1. row ***************************TABLE_CATALOG: defTABLE_SCHEMA: testTABLE_NAME: tPARTITION_NAME: p0SUBPARTITION_NAME: NULLPARTITION_ORDINAL_POSITION: 1
SUBPARTITION_ORDINAL_POSITION: NULLPARTITION_METHOD: RANGESUBPARTITION_METHOD: NULLPARTITION_EXPRESSION: `id`SUBPARTITION_EXPRESSION: NULLPARTITION_DESCRIPTION: 10TABLE_ROWS: 1AVG_ROW_LENGTH: 16384DATA_LENGTH: 16384MAX_DATA_LENGTH: NULLINDEX_LENGTH: 0DATA_FREE: 0CREATE_TIME: 2022-11-01 17:48:40UPDATE_TIME: 2022-11-01 17:57:37CHECK_TIME: NULLCHECKSUM: NULLPARTITION_COMMENT:NODEGROUP: defaultTABLESPACE_NAME: NULL
*************************** 2. row ***************************TABLE_CATALOG: defTABLE_SCHEMA: testTABLE_NAME: tPARTITION_NAME: p1SUBPARTITION_NAME: NULLPARTITION_ORDINAL_POSITION: 2
SUBPARTITION_ORDINAL_POSITION: NULLPARTITION_METHOD: RANGESUBPARTITION_METHOD: NULLPARTITION_EXPRESSION: `id`SUBPARTITION_EXPRESSION: NULLPARTITION_DESCRIPTION: 20TABLE_ROWS: 2AVG_ROW_LENGTH: 8192DATA_LENGTH: 16384MAX_DATA_LENGTH: NULLINDEX_LENGTH: 0DATA_FREE: 0CREATE_TIME: 2022-11-01 17:48:40UPDATE_TIME: 2022-11-01 17:57:37CHECK_TIME: NULLCHECKSUM: NULLPARTITION_COMMENT:NODEGROUP: defaultTABLESPACE_NAME: NULL

对于表t,由于定义了分区,因此对于插入的值应严格遵守分区的定义,当插入一个不在分区内的值时,MySQL数据库会抛出异常:

mysql> INSERT INTO t SELECT 30;
ERROR 1526 (HY000): Table has no partition for value 30

对于上述问题,可以对分区添加一个MAXVALUE值的分区。MAXVALUE可以理解为正无穷,因此所有大于等于20且小于MAXVALUE的值分别放入p2分区:

mysql> ALTER TABLE t ADD PARTITION(partition p2 values less than maxvalue);

在此之后插入记录:

mysql> INSERT INTO t SELECT 30;
Query OK, 1 row affected (0.001 sec)
Records: 1  Duplicates: 0  Warnings: 0

RANGE分区主要用于日期列的分区,如下例:

CREATE TABLE sales ( money INT UNSIGNED NOT NULL, date DATE) ENGINE = INNODB
PARTITION BY RANGE(year(date))
(
PARTITION p2008 VALUES LESS THAN (2009),
PARTITION p2009 VALUES LESS THAN (2010),
PARTITION p2010 VALUES LESS THAN (2011)
);mysql> INSERT INTO sales SELECT 100,'2008-01-01';
mysql> INSERT INTO sales SELECT 100,'2008-02-01';
mysql> INSERT INTO sales SELECT 200,'2008-01-02';
mysql> INSERT INTO sales SELECT 100,'2009-03-01';
mysql> INSERT INTO sales SELECT 200,'2010-03-01';

如果要删除2009年的数据,不需要执行DELECT FROM sales WHERE date>='2008-01-01' and date<'2000-01-01',只需删除2008年所在的分区即可:

ALTER TABLE sales drop partition p2008;
mysql> EXPLAIN PARTITIONSSELECT * FROM salesWHERE date>='2008-01-01' AND date<='2008-12-31'\G;

通过EXPLAIN PARTITION命令,在上述语句中,SQL优化器只需要去搜索p2008这个分区,而不会去搜索所有的分区——称为Partition Pruning(分区修剪),故查询的速度得到了大幅度的提升。
然后在条件改为:

mysql> EXPLAIN PARTITIONSSELECT * FROM salesWHERE date>='2008-01-01' AND date<'2009-01-01'\G;

时优化器会去搜索p2008和p2009两个分区。

再如:

mysql> CREATE TABLE sales(money INT UNSIGNED NOT NULL,date DATETIME
)ENGINE=INNODB
PARTITION BY RANGE(YEAR(date)*100+MONTH(date))(PARTITION p201001 VALUES LESS THAN (201002),PARTITION p201002 VALUES LESS THAN (201003),PARTITION p201003 VALUES LESS THAN (201004)
);

按照上述分区,优化器不会根据分区进行选择,即使编写的SQL已经符合了分区的要求。
产生这个问题的主要原因是对于RANGE分区的查询,优化器只能对YEAR(),TO_DAYS(),TO_SECONDS(),UNIX_TIMESTAMP()这类函数进行优化选择。

LIST分区

LIST分区和RANGE分区非常相似,只是分区列的值是离散的,而非连续的。

mysql> CREATE TABLE t (a INT,b INT)ENGIN=INNODBPARTITION BY LIST(b)(PARTITION p0 VALUES IN (1,3,5,7,9),PARTITION p1 VALUES IN (0,2,4,6,8)
);

不同于RANGE分区中定义的VALUES LESS THAN语句,LIST分区使用VALUES IN。因为每个分区的值是离散的,因此只能定义值。

例:

mysql> CREATE TABLE t(a INT,b INT)ENGINE=INNODBPARTITION BY LIST(b)(PARTITION p0 VALUES IN (1,3,5,7,9),PARTITION p1 VALUES IN (0,2,4,6,8));mysql> INSERT INTO t SELECT 1,1;
mysql> INSERT INTO t SELECT 1,2;
mysql> INSERT INTO t SELECT 1,3;
mysql> INSERT INTO t SELECT 1,4;mysql> SELECT table_name,partition_name,table_rowsFROM information_schema.PARTITIONSWHERE table_name='t' AND table_schema=DATABASE()\G;

可得到结果:


*************************** 1. row ***************************table_name: t
partition_name: p0table_rows: 2
*************************** 2. row ***************************table_name: t
partition_name: p1table_rows: 2

如果插入的值不在分区定义中,MySQL数据库同样会抛出异常:

mysql> INSERT INTO t SELECT 1,10;

其返回:

ERROR 1526 (HY000): Table has no partition for value 10

另外,在用INSERT插入多个行数据的过程中遇到分区未定义的值时,MyISAM和InnoDB存储引擎的处理不同。MyISAM引擎会将之前的行数据都插入,但之后的数据不会被插入。而InnoDB存储引擎将其视为一个事务,因此没有任何数据插入。

HASH分区

HASH分区的目的是将数据均匀分布到预先定义的各个分区中,保证各分区的数据量大致都是一样的。在RANGE和LIST分区中,必须明确指定一个给定的列值或列值集合应该保存到哪个分区中;而在HASH分区中,MySQL自动完成这些工作,用户所要做的只是基于将要进行哈希分区的列值指定一个列值或表达式,以及指定被分区的表将要被分割成的分区数量。

CREATE TABLE table_name(col_name[,colname]...)ENGINE=InnoDB
PARTITION BY HASH (expr)
PARTITONS num;
参数 含义
table_name 表名
col_name 字段名
expr 一个返回一个整数的表达式
num 一个非负整数,它表示表将要被分割成分区的数量,如果没有PARTITIONS子句,那么分区的数量将默认为1

另外MySQL还支持一种称为LINEAR HASH的分区,它使用一个更加复杂的算法来确定新行插入到已经分区的表中的位置。它的语法与HASH分区语法类似,只是将HASH改为LINEAR HASH
LINEAR HASH分区的优点在于,增加、删除、合并和拆分分区将变得更加快捷,这有利于含有大量数据的表。它的缺点在于,与使用HASH分区得到的数据分布相比,各个分区间数据的分布可能不大均衡。

KEY分区

KEY分区和HASH分区相似,不同之处在于HASH分区使用用户定义的函数进行分区,KEY分区使用MySQL数据库提供的函数进行分区。
在KEY分区中使用关键字LINEAR和HASH分区中使用具有同样的效果,分区的编号是通过2的幂算法得到的,而不是通过模数算法。

COLUMNS分区

前面介绍的RANGE、LIST、HASH和KEY这四种分区中,分区的条件是:数据必须是整形(interger),如果不是整形,那应该通过函数将其转化为整形,如YEAR()、TO_DAYS()、MONTH()等函数。
COLUMNS分区可以直接使用非整形的数据进行分区。此外,RANGE COLUMNS分区可以对多个列的值进行分区。

COLUMNS分区支持的数据类型
所有整形类型 如INT、SMALLINT、TINYINT、BIGINT。FLOAT和DECIMAL不予支持
日期类型 如DATE和DATETIME.其余的日期类型不予支持
字符串类型 如CHAR、VARCHAR、BINARY和VARBINARY。BLOB和TEXT类型不予支持

例:
类RANGE:

CREATE TABLE t_columns_range(a INT,b DATETIME
)ENGINE=INNODB
PARTITION BY RANGE COLUMNS(b)(PARTITION p0 VALUES LESS THAN ('2009-01-01'),PARTITION p1 VALUES LESS THAN ('2010-01-01')
);

类LIST:

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','Hogsby','Monsteras'),PARTITION pRegion_2 VALUES IN ('Vimmerby','Hultsfred','Vastervik'),PARTITION pRegion_3 VALUES IN ('Nassjo','Eksjo','Vetlanda'),PARTITION pRegion_4 VALUES IN ('Uppvidinge','Alvesta','Vaxjo')
);

多个列进行分区:

CREATE TABLE rcx(a INT,b INT,c CHAR(3),d INT
)ENGINE=INNODB
PARTITION BY RANGE COLUMNS(a,b,c)(PARTITION p0 VALUES LESS THAN (5,10,'ggg'),PARTITION p1 VALUES LESS THAN (10,20,'mmmm'),PARTITION p2 VALUES LESS THAN (15,30,'sss'),PARTITION p3 VALUES LESS THAN (MAXVALUE,MAXVALUE,MAXVALUE)
);

子分区

子分区(subpartitioning)是在分区的基础上在进行分区,有时也称这种分区为复合分区(composite partitioning)

例:在RANGE分区下在进行HASH子分区:

mysql> CREATE TABLE ts(a INT,b DATE)ENGINE=INNODBPARTITION BY RANGE(YEAR(b))SUBPARTITION BY HASH(TO_DAYS(b))SUBPARTITIONS 2(PARTITION p0 VALUES LESS THAN (1990),PARTITION p1 VALUES LESS THAN (2000),PARTITION p2 VALUES LESS THAN MAXVALUE);

也可以这样:

mysql> CREATE TABLE ts(a INT,b date)ENGINE=INNODBPARTITION BY RANGE(YEAR(b))SUBPARTITION BY HASH(TO_DAYS(b))(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));

子分区建立需要注意以下几个问题:

  • 每个子分区的数量必须相同
  • 要在一个分区表的任何分区上使用SUBPARTITION来明确定义任何子分区,就必须定义所有子分区
  • 每个SUBPARTITION子句必须包括子分区的一个名字
  • 子分区的名字必须是唯一的

在表和分区之间交换数据

将表1的某个分区中的数据移动到表2:

ALTER TABLE 表名1 EXCHANGE PARTITION 分区名 WITH TABLE 表名2;

要使用ALTER TABLE...EXCHANGE PARTITION语句,必须满足下面的条件:

  • 要交换的表需和分区表有着相同的表结构,但是表不能含有分区
  • 在非分区表中的数据必须在交换的分区定义内
  • 被交换的表中不能含有外键,或者其他表含有对该表的外键引用
  • 用户除了需要ALTER、INSERT、CREATE权限外,还需要DROP的权限
    此外
  • 使用该语句时,不会触发交换表和被交换表上的触发器
  • AUTO_INCREMENT列将被重置

例:

mysql> 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)
);mysql> INSERT INTO e VALUES(1669,"Jim","Smith"),(337,"Mart","Jones"),(16,"Frank","White"),(2005,"Linda","Black");mysql> CREATE TABLE e2 LIKE e;
mysql> ALTER TABLE e2 REMOVE PARTITIONING;

此时查看:

mysql> SELECT PARTITION_NAME,TABLE_ROWSFROM INFORMATION_SCHEMA.PARTITIONSWHERE TABLE_NAME='e';

得到结果:

+----------------+------------+
| PARTITION_NAME | TABLE_ROWS |
+----------------+------------+
| p0             |          1 |
| p1             |          0 |
| p2             |          0 |
| p3             |          3 |
+----------------+------------+
4 rows in set (0.010 sec)

执行

mysql> ALTER TABLE e EXCHANGE PARTITION p0 WITH TABLE e2;

此时查看:

mysql> SELECT PARTITION_NAME,TABLE_ROWSFROM INFORMATION_SCHEMA.PARTITIONSWHERE TABLE_NAME='e';

得到结果:


+----------------+------------+
| PARTITION_NAME | TABLE_ROWS |
+----------------+------------+
| p0             |          0 |
| p1             |          0 |
| p2             |          0 |
| p3             |          3 |
+----------------+------------+
4 rows in set (0.001 sec)

而这时:

mysql> SELECT * FROM e2;

有:

+----+-------+-------+
| id | fname | lname |
+----+-------+-------+
| 16 | Frank | White |
+----+-------+-------+

p0的数据被移动到了e2

MySQL数据库表的分区相关推荐

  1. mysql数据库建新分区_mysql数据库分区

    日常开发中我们经常会遇到大表的情况,所谓的大表是指存储了百万级乃至千万级条记录的表.这样的表过于庞大,导致数据库在查询和插入的时候耗时太长,性能低下,如果涉及联合查询的情况,性能会更加糟糕.分表和表分 ...

  2. 运维利器之mysql进行表的分区

    运维利器之mysql进行表的分区 特别声明一下哈,小编不是DBA,所以相对来说对mysql的理解不是那么的透彻,小编可以做到的是确保数据的安全,知道数据怎么存储,才能更好的保护数据,知己知彼,方能百战 ...

  3. Mysql数据库调优——分区

    Mysql数据库调优--分区 参考资料: MySQL分区:https://www.bilibili.com/video/BV1E7411q7Nx MySQL_基础+高级篇- 数据库 -sql -mys ...

  4. unicode表_Python数据库操作 Mysql数据库表引擎与字符集#学习猿地

    # Mysql数据库表引擎与字符集 ![](./imgs/752951346A5F4E7EBDE362FA97107707.png) ### 1.服务器处理客户端请求 其实不论客户端进程和服务器进程是 ...

  5. 程序员面试之MySQL数据库表的设计

    如果要选择一门程序员必备的技能,那答案无疑是数据库,而MySQL是首选.很多企业在面试过程中会提问MySQL数据库表设计要注意什么,接下来小千就给大家讲解一下. MySQL相较于MSSQL SERVE ...

  6. 一个漂亮的输出MySql数据库表结构的PHP页面

    经常为了方便和直观,我们会首先直接在数据库中设计出表,但是接下来又要将表的结构和设计编写在设计文档中,以便编码的时候可以直观的查询,一旦数据库表非常多,字段非常多的时候,这无疑是件非常郁闷的工作. 这 ...

  7. 查看MySQL数据库表的命令介绍

    如果需要查看MySQL数据库中都有哪些MySQL数据库表,应该如何实现呢?下面就为您介绍查看MySQL数据库表的命令,供您参考. 进入MySQL Command line client下 查看当前使用 ...

  8. php修改mysql数据库中的表格,如何修改mysql数据库表?

    修改mysql数据库表的方法:使用"ALTER TABLE"语句,可以改变原有表的结构,例如增加字段或删减字段.修改原有字段数据类型.重新命名字段或表.修改表字符集等:语法&quo ...

  9. 7.Mysql数据库表引擎与字符集

    7.Mysql数据库表引擎与字符集 1.服务器处理客户端请求 其实不论客户端进程和服务器进程是采用哪种方式进行通信,最后实现的效果都是:客户端进程向服务器进程发送 一段文本(MySQL语句),服务器进 ...

最新文章

  1. 使用etcd+confd管理nginx配置
  2. SQL Server统计信息:问题和解决方式
  3. C++ :: 的用法小结
  4. 微信小程序正式上线 可置于聊天窗口顶部
  5. C语言函数最多有有多少个参数,C中子函数最多有几个形参
  6. 协同过滤—基于图的方法
  7. 第一季2:视频设备开发的技术流
  8. 游戏服务器维护启动需要多久,游戏服务器开机需要多长时间
  9. matlab 实现行归一化和列归一化
  10. 关于Windows 1803版本内核隔离打开后无法关闭的解决方案
  11. 数据管理之元数据管理
  12. Python糗百爬虫(精简版)
  13. 什么是物联网?常见IoT协议最全讲解
  14. 【Latex学习】在IEEEtran模板中使用algorithm环境
  15. 【横向移动】内网渗透之内网信息收集
  16. 树莓派sensehat画图游戏 Etch a sketch
  17. 薇电子学与计算机好中吗,微电子学与计算机是什么级别的期刊
  18. Spring Cloud 配置中心乱码解决
  19. 利用c语言编写一个时钟计时器(c语言基础练习)
  20. 英语口语8000句-随意的谈话

热门文章

  1. 跟着唐诗去旅行,别有一番滋味
  2. memcpy,memmove,memcmp,memset内存函数
  3. Formily 学习2 (Rxjs 知识)
  4. plain framework 1 pak插件说明(资源压缩加密)
  5. 小米电视65寸买哪款合适?小米电视65寸横评对比
  6. ps 计算机 性能设置,如何设置Adobe Photoshop CS6的性能?设置方法
  7. mysql查询语句不返回_mysql命令查询语句MTdata
  8. i7 1160g7参数 i71160G7怎么样
  9. 简单光照模型(Lambert 光照模型)
  10. Archlinux+Dwm关闭笔记本触摸板