MySQL数据库分区表应用

1 什么是分区表

首先来看以正常表的存放方式

MySQL数据库分区表应用_第1张图片

上面一部分是innodb引擎的,下面一部分是myISAM引擎的.

customer_login_log.frm #保存了了每个表的元数据,包括表结构以及相应的定义,⽆无论是什什么数
据引擎都有这个⽂文件
customer_login_log.ibd #innodb的存放数据⽂文件和索引的地⽅方  

如果表的数据太大,可能一个磁盘放不下,这个时候,我们可以把数据分配到不同的磁盘里面去。

表分区,是指根据一定规则,将数据库中的一张表分解成多个更小的,容易管理的部分。从逻辑上看,只有一张表,但是底层却是由多个物理分区组成。

MySQL数据库在5.1版本时添加了对分区的支持,分区的过程是将一个表或索引分解为多个更小、更可管理的部分。就访问数据库的应用而言,从逻辑上讲,只有一个表或一个索引,但是在物理上这个表或索引可能由数十个物理分区组成。每个分区都是独立的对象,可以独自处理,也可以作为一个更大对象的一部分进行处理。

查看数据库是否支持分区表

# MySQL v5.6以前
show variables like '%partition%'; 
# MySQL v5.6(含)版本后
mysql>show plugins;

MySQL数据库分区表应用_第2张图片

2 我们为什么要使用分区表

  • 经常会遇到上万甚至上亿的记录表数据
  • 查询困难,而且历史数据其实是不关心的
  • 要进行归档了,如何归档?
    • 一年前的数据通过where < 20190101 00:00:00 查出来insert进arch, bak
    • mysqldump是可以加where条件
    • 原表的历史数据要删除。delete table where time? 删除不要的
      • 我们3年的数据,只留近半年的:凌晨四点执行delete操作
        • innodb数据是由B+Tree结构组成的,如果delete where只会标记该数据被删除,不会真正删除,将来插入数据的时候空间可复用,磁盘文件大小不会缩小。
        • mysql>OPTIMIZE table my_table_name;
  • 如果我们有这样一种文件组织性:2017年的数据放一个文件,2018年的数据放一个文件,2019,这个时候就可以按照文件进行删除了,并且可以指定数据文件查询范围,就会提升查询效率

3 分区表类型

  • HASH分区
  • LIST分区
  • RANGE分区
  • KEY分区

HASH分区

  • 根据MOD(分区键)的值把数据存储到表的不同分区中
  • 基本可以平均分布于各个分区
  • HASH分区的键值必须是INT类型,可以通过函数转为INT类型
CREATE TABLE `customer_login_log` (
`customer_id` int(10) unsigned NOT NULL COMMENT '登录⽤用户ID',
`login_time` datetime NOT NULL COMMENT '⽤用户登录时间',
`login_ip` int(10) unsigned NOT NULL COMMENT '登录IP',
`login_type` tinyint(4) NOT NULL COMMENT '登录类型:0未成功 1成功'
) ENGINE=InnoDB DEFAULT CHARSET=utf8
PARTITION BY HASH(customer_id) PARTITIONS 4;

insert into customer_login_log values(1,'2020-06-20 22:30:01',1,1);
insert into customer_login_log values(2,'2020-06-20 22:30:02',2,1);
insert into customer_login_log values(3,'2020-06-20 22:30:03',3,1);
insert into customer_login_log values(4,'2020-06-20 22:30:04',4,1);

使⽤用函数进⾏行行INT输出

CREATE TABLE `customer_login_log` (
`customer_id` int(10) unsigned NOT NULL COMMENT '登录⽤用户ID',
`login_time` timestamp NOT NULL COMMENT '⽤用户登录时间', #注意这⾥里里的数据类型
`login_ip` int(10) unsigned NOT NULL COMMENT '登录IP',
`login_type` tinyint(4) NOT NULL COMMENT '登录类型:0未成功 1成功'
) ENGINE=InnoDB DEFAULT CHARSET=utf8
PARTITION BY HASH(UNIX_TIMESTAMP(login_time)) PARTITIONS 4;

转换函数

ABS()
CEILING() (see CEILING() and FLOOR())
DAY()
DAYOFMONTH()
DAYOFWEEK()
DAYOFYEAR()
DATEDIFF()
EXTRACT() (see EXTRACT() function with WEEK specifier)
FLOOR() (seeCEILING() and FLOOR())
HOUR()
MICROSECOND()
MINUTE()
MOD()
MONTH()
QUARTER()
SECOND()
TIME_TO_SEC()
TO_DAYS()
TO_SECONDS()
UNIX_TIMESTAMP() (permitted beginning with MySQL 5.6.1 and fully supported
beginning with MySQL 5.6.3, with TIMESTAMP columns)
WEEKDAY()
YEAR()
YEARWEEK()

创建以后的内容

MySQL数据库分区表应用_第3张图片

查看分区表是否创建成功

mysql> explain partitions select * from customer_login_log;

查询每个分区存放多少数据

select table_name,partition_name,partition_description,table_rows from information_schema.PARTITIONS where table_name='customer_login_log';

查询具体的某个分区数据

select * from customer_login_log partition(p1,p2);
select * from customer_login_log partition(p3) where customer_id=3;

LIST 分区

  • 按照分区键取值的列表进行分区

  • 各分区的列表值不能重复

  • 每一行数据必须找到对应的分区列列表,否则插入失败

    CREATE TABLE `customer_login_log_list` (
      `customer_id` int(10) unsigned NOT NULL COMMENT '登录用户ID',
      `login_time` datetime NOT NULL COMMENT '用户登录时间',
      `login_ip` int(10) unsigned NOT NULL COMMENT '登录IP',
      `login_type` tinyint(4) NOT NULL
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8
    PARTITION BY LIST(login_type)(
    	PARTITION jishu VALUES in (1,3,5,7,9),
    	PARTITION oushu VALUES in (2,4,6,8)
    );
    
    insert into customer_login_log_list values(1,'2020-06-20 22:30:01',1,1);
    insert into customer_login_log_list values(2,'2020-06-20 22:30:02',2,2);
    insert into customer_login_log_list values(3,'2020-06-20 22:30:03',3,3);
    insert into customer_login_log_list values(4,'2020-06-20 22:30:04',4,4);
    

    如果分区键不在分区中

    mysql> insert into customer_login_log_list values(4,'2020-06-20 22:30:04',4,0);
    ERROR 1526 (HY000): Table has no partition for value 0
    

RANGE分区

  • 根据分区的不同范围值将数据放不同文件中

  • 多个分区要连续,不能重叠

  • 要有封口的MAXVALUE

    CREATE TABLE `customer_login_log_range` (
      `customer_id` int(10) unsigned NOT NULL COMMENT '登录用户ID',
      `login_time` datetime NOT NULL COMMENT '用户登录时间',
      `login_ip` int(10) unsigned NOT NULL COMMENT '登录IP',
      `login_type` tinyint(4) NOT NULL COMMENT '登录类型:0未成功 1成功'
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8
    PARTITION BY RANGE(YEAR(login_time))(
    	PARTITION y2017 VALUES LESS THAN (2017),
    	PARTITION y2018 VALUES LESS THAN (2018),
    	PARTITION y2019 VALUES LESS THAN (2019),
    	PARTITION y2020 VALUES LESS THAN (2020),
    	PARTITION maxyear VALUES LESS THAN MAXVALUE
    );
    
    insert into customer_login_log_range values(1,'2016-06-20 22:30:01',1,1);
    insert into customer_login_log_range values(2,'2017-06-20 22:30:02',2,2);
    insert into customer_login_log_range values(3,'2018-06-20 22:30:03',3,3);
    insert into customer_login_log_range values(5,'2019-06-20 22:30:04',4,4);
    insert into customer_login_log_range values(6,'2020-06-20 22:30:04',4,4);
    insert into customer_login_log_range values(7,'2021-06-20 22:30:04',4,4);
    insert into customer_login_log_range values(8,'2022-06-20 22:30:04',4,4);
    insert into customer_login_log_range values(9,'2023-06-20 22:30:04',4,4);
    

    如果我们需要后期新增range分区,就不能maxvalue封口

    CREATE TABLE `customer_login_log_range1` (
      `customer_id` int(10) unsigned NOT NULL COMMENT '登录用户ID',
      `login_time` datetime NOT NULL COMMENT '用户登录时间',
      `login_ip` int(10) unsigned NOT NULL COMMENT '登录IP',
      `login_type` tinyint(4) NOT NULL COMMENT '登录类型:0未成功 1成功'
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8
    PARTITION BY RANGE(YEAR(login_time))(
    	PARTITION y2017 VALUES LESS THAN (2017),
    	PARTITION y2018 VALUES LESS THAN (2018),
    	PARTITION y2019 VALUES LESS THAN (2019),
    	PARTITION y2020 VALUES LESS THAN (2020)
    );
    
    alter table customer_login_log_range1 add PARTITION(
    PARTITION y2021 VALUES LESS THAN (2021),
    PARTITION y2022 VALUES LESS THAN (2022),
    PARTITION y2023 VALUES LESS THAN (2023)
    )
    

    一个没有创建分区的表

    CREATE TABLE `customer_login_log_range_no` (
      `customer_id` int(10) unsigned NOT NULL COMMENT '登录用户ID',
      `login_time` datetime NOT NULL COMMENT '用户登录时间',
      `login_ip` int(10) unsigned NOT NULL COMMENT '登录IP',
      `login_type` tinyint(4) NOT NULL COMMENT '登录类型:0未成功 1成功'
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    
    insert into customer_login_log_range_no values(1,'2016-06-20 22:30:01',1,1);
    insert into customer_login_log_range_no values(2,'2017-06-20 22:30:02',2,2);
    insert into customer_login_log_range_no values(3,'2018-06-20 22:30:03',3,3);
    insert into customer_login_log_range_no values(5,'2019-06-20 22:30:04',4,4);
    insert into customer_login_log_range_no values(6,'2020-06-20 22:30:04',4,4);
    insert into customer_login_log_range_no values(7,'2021-06-20 22:30:04',4,4);
    insert into customer_login_log_range_no values(8,'2022-06-20 22:30:04',4,4);
    insert into customer_login_log_range_no values(9,'2023-06-20 22:30:04',4,4);
    
    alter table customer_login_log_range_no PARTITION BY RANGE(YEAR(login_time))(
    	PARTITION y2017 VALUES LESS THAN (2017),
    	PARTITION y2018 VALUES LESS THAN (2018),
    	PARTITION y2019 VALUES LESS THAN (2019),
    	PARTITION y2020 VALUES LESS THAN (2020),
    	PARTITION y2021 VALUES LESS THAN (2021),
    	PARTITION y2022 VALUES LESS THAN (2022),
    	PARTITION maxyear VALUES LESS THAN MAXVALUE
    )
    # 数据会按照分区规则进行数据重新组装,数据会进入相应分区
    

    如果要删除分区使用命令,不能直接删除文集

     alter table customer_login_log_range drop partition y2017;
    

日志数据进行备份的时候,在MySQL v5.7版本后加入了一个分区交换的概念

  • 表结构相同

  • 归档的这个表不能是分区表

  • 归档表不能有外键约束

  • ARCHIVE

    #归档表
    CREATE TABLE `arch_customer_login_log_2016` (
    `customer_id` int(10) unsigned NOT NULL COMMENT '登录⽤用户ID',
    `login_time` datetime NOT NULL COMMENT '⽤用户登录时间',
    `login_ip` int(10) unsigned NOT NULL COMMENT '登录IP',
    `login_type` tinyint(4) NOT NULL COMMENT '登录类型:0未成功 1成功'
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    
    ALTER TABLE customer_login_log exchange PARTITION p0 WITH TABLE
    arch_customer_login_log_2016;
    ALTER TABLE arch_customer_login_log_2016 ENGINE=ARCHIVE;
    

    删除分区文件

    ALTER TABLE customer_login_log DROP PARTITION p0;
    

    如果一个分区表有主键,那么分区键一定要是主键的一部分

    CREATE TABLE `im_product_variants` (
    `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
    `variants_id` varchar(50) NOT NULL DEFAULT '',
    `variants_title` varchar(100) NOT NULL DEFAULT '' COMMENT '属性名称,多个属性值⽤用/隔开',
    `sku` varchar(50) NOT NULL DEFAULT '',
    `original_price` decimal(10,2) NOT NULL COMMENT '原价',
    `sale_price` decimal(10,2) NOT NULL COMMENT '售价',
    `variants_weight` int(11) NOT NULL COMMENT '重量量单位g',
    `option_name1` varchar(50) NOT NULL DEFAULT '' COMMENT '属性名1',
    `option_name2` varchar(50) DEFAULT NULL COMMENT '属性名2',
    `option_name3` varchar(50) DEFAULT NULL COMMENT '属性名3',
    `option_value1` varchar(50) NOT NULL DEFAULT '' COMMENT '属性值1',
    `option_value2` varchar(50) DEFAULT NULL COMMENT '属性值2',
    `option_value3` varchar(50) DEFAULT NULL COMMENT '属性值3',
    `product_id` varchar(50) NOT NULL DEFAULT '' COMMENT '产品id',
    `image_src` varchar(300) NOT NULL DEFAULT '' COMMENT '属性图⽚片src路路径',
    `create_emp` varchar(50) NOT NULL DEFAULT '',
    `update_emp` varchar(50) NOT NULL DEFAULT '',
    `create_time` int(11) NOT NULL,
    `update_time` int(11) NOT NULL,
    PRIMARY KEY (`id`,`create_time`)
    ) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8
    PARTITION BY RANGE(create_time)(
    PARTITION p0 VALUES LESS THAN (1483200000),
    PARTITION p1 VALUES LESS THAN (1514736000),
    PARTITION p2 VALUES LESS THAN (1546272000),
    PARTITION p3 VALUES LESS THAN (1577808000)
    );
    

Q&A

问题1:

如果一个表不是分区表并且已经有数据了,还能不能变成分区表?

答案是可以

ALTER TABLE customer_login_log PARTITION BY RANGE(YEAR(login_time))(
PARTITION p0 VALUES LESS THAN (2017),
PARTITION p1 VALUES LESS THAN (2018),
PARTITION p2 VALUES LESS THAN (2019),
PARTITION p3 VALUES LESS THAN (2020)
);

问题2:

变成分区表后,数据是否按照分区重新排列?

答案是会重排数据,但数据量大的情况下I/O消耗很大

这种数据量大的表,要按照修改表结构的方式进行分区表操作

  • 建立新结构相同的分区表
  • 数据导入
  • 表名切换

问题3:

既然把表分开了,那我们分区表最大有多少个?

答案是1024个

4 分区表的限制和注意事项

  • 如果表里有主键必须包含分区表的分区键
  • 很多时候,使用分区表就不要使用主键,建立主键后可能会影响性能
  • 不要建立过多的分区
  • 分区表不支持外键
  • 分区规则必须要提前设立好,否则修改很麻烦

5 分区表应用场景

日志表的特征

  • 记录非常的多,非常的大
  • 明显的时间区间特征
  • 查询频次低,就近时间点查询频次高
  • 需要定期归档转储

在这样的表结构下分区表就是我们的最佳实践

你可能感兴趣的:(MySQL)