MySQL分区是将一张表分割成独立的子表的技术。每个子表被称为分区,它们有着相同的结构和字段,但存储着不同的数据。这项技术可以提高查询速度,减少日志文件和磁盘空间的使用。
要建立MySQL分区,需要满足以下几个条件:
1.所需的MySQL版本:
MySQL 5.1.5及以上版本支持分区,但仅限于使用InnoDB和MyISAM存储引擎的表。
2.分区字段:
必须定义一个或多个分区字段来确定如何将数据行分配到各个分区中。分区字段必须是表的主键或唯一索引之一。
3.分区类型:
MySQL提供了多种分区类型,包括范围分区、哈希分区和列表分区。你需要根据数据特点和查询需求选择合适的分区类型。
4.分区数量:
决定分区数量需要考虑表的大小、查询的复杂度、硬件资源等因素。建议根据具体情况选取合适的分区数量,一般不宜超过1000个。
MySQL分区技术可以大大提高查询效率和管理的便利性,但在实际使用中需要根据具体情况选择合适的分区条件和数量,避免性能瓶颈和资源浪费。
MySQL 数据库中的数据是以文件的形势存在磁盘上的,默认放在 /var/lib/mysql/ 目录下面,我们可以通过 show variables like '%datadir%'; 命令来查看:
我们进入到这个目录下,就可以看到我们定义的所有数据库了,一个数据库就是一个文件夹,一个库中,有其对应的表的信息,如下:
在 MySQL 中,如果存储引擎是 MyISAM,那么在 data 目录下会看到 3 类文件:.frm、.myi、.myd,如下:
如果存储引擎是 InnoDB, 那么在 data 目录下会看到两类文件:.frm、.ibd,如下:
无论是哪种存储引擎,只要一张表的数据量过大,就会导致 *.myd、*.myi 以及 *.ibd 文件过大,数据的查找就会变的很慢。
为了解决这个问题,我们可以利用 MySQL 的分区功能,在物理上将这一张表对应的文件,分割成许多小块,如此,当我们查找一条数据时,就不用在某一个文件中进行整个遍历了,我们只需要知道这条数据位于哪一个数据块,然后在那一个数据块上查找就行了;另一方面,如果一张表的数据量太大,可能一个磁盘放不下,这个时候,通过表分区我们就可以把数据分配到不同的磁盘里面去。
通俗地讲表分区是将一大表,根据条件分割成若干个小表。
如:某用户表的记录超过了600万条,那么就可以根据入库日期将表分区,也可以根据所在地将表分区。当然也可根据其他的条件分区。
MySQL 从 5.1 开始添加了对分区的支持,分区的过程是将一个表或索引分解为多个更小、更可管理的部分。对于开发者而言,分区后的表使用方式和不分区基本上还是一模一样,只不过在物理存储上,原本该表只有一个数据文件,现在变成了多个,每个分区都是独立的对象,可以独自处理,也可以作为一个更大对象的一部分进行处理。
需要注意的是,分区功能并不是在存储引擎层完成的,常见的存储引擎如 InnoDB、MyISAM、NDB 等都支持分区。但并不是所有的存储引擎都支持,如 CSV、FEDORATED、MERGE 等就不支持分区,因此在使用此分区功能前,应该对选择的存储引擎对分区的支持有所了解。
MySQL分区有优点也有一些缺点,如下:
优点:
缺点:
在考虑使用分区时,需要综合考虑业务需求、查询模式、数据规模和硬件资源等因素,并权衡分区带来的优势和缺点。对于特定的应用和数据场景,分区可能是一个有效的解决方案,但并不适用于所有情况。
同时分区表也存在一些限制,如下:
限制:
分区表在以下情况下可以发挥其优势,适用于以下几种使用场景:
分区表并非适用于所有情况。在选择使用分区表时,需要综合考虑数据量、查询模式、存储资源和硬件能力等因素,并评估分区对性能和管理的影响。
分区有2种方式,水平切分和垂直切分。MySQL 数据库支持的分区类型为水平分区,它不支持垂直分区。
此外,MySQL数据库的分区是局部分区索引,一个分区中既存放了数据又存放了索引。而全局分区是指,数据存放在各个分区中,但是所有数据的索引放在一个对象中。目前,MySQL数据库还不支持全局分区。
RANGE分区是MySQL中的一种分区策略,根据某一列的范围值将数据分布到不同的分区。每个分区包含特定的范围。下面是RANGE分区的定义方式、特点以及代码示例。
定义方式:
RANGE分区的特点:
以下是一个使用RANGE分区的代码示例:
CREATE TABLE sales ( id INT, sales_date DATE, amount DECIMAL(10, 2) ) PARTITION BY RANGE (YEAR(sales_date)) ( PARTITION p1 VALUES LESS THAN (2020), PARTITION p2 VALUES LESS THAN (2021), PARTITION p3 VALUES LESS THAN (2022), PARTITION p4 VALUES LESS THAN MAXVALUE );
在上述示例中,我们创建了名为sales的表,使用RANGE分区策略。根据sales_date列的年份范围将数据分布到不同的分区。
RANGE分区允许根据列值的范围将数据分散到不同的分区中,适用于按范围进行查询和管理的情况。它提供了更灵活的数据管理和查询效率的提升。
定义方式:
LIST分区的特点:
以下是一个使用LIST分区的代码示例:
CREATE TABLE users ( id INT, username VARCHAR(50), region VARCHAR(50) ) PARTITION BY LIST (region) ( PARTITION p_east VALUES IN ('New York', 'Boston'), PARTITION p_west VALUES IN ('Los Angeles', 'San Francisco'), PARTITION p_other VALUES IN (DEFAULT) );
在上述示例中,我们创建了名为users的表,使用LIST分区策略。根据region列的具体取值将数据分布到不同的分区。
定义方式:
HASH分区的特点:
以下是一个使用HASH分区的代码示例:
CREATE TABLE sensor_data ( id INT, sensor_name VARCHAR(50), value INT ) PARTITION BY HASH (id) PARTITIONS 4;
在上述示例中,我们创建了名为sensor_data的表,使用HASH分区策略。根据id列的哈希值将数据分布到4个分区中。
KEY分区是根据某一列的哈希值将数据分布到不同的分区。不同于HASH分区,KEY分区使用的是列值的哈希值而不是哈希函数。下面是KEY分区的定义方式、特点以及代码示例。
定义方式:
KEY分区的特点:
以下是一个使用KEY分区的代码示例:
CREATE TABLE orders ( order_id INT, customer_id INT, order_date DATE ) PARTITION BY KEY (customer_id) PARTITIONS 5;
在上述示例中,我们创建了名为orders的表,使用KEY分区策略。根据customer_id列的哈希值将数据分布到5个分区中。
MySQL在5.5版本引入了COLUMNS分区类型,其中包括RANGE COLUMNS分区和LIST COLUMNS分区。以下是对这两种COLUMNS分区的详细说明:
下面是一个RANGE COLUMNS分区的代码示例:
CREATE TABLE sales ( id INT, sales_date DATE, region VARCHAR(50), amount DECIMAL(10,2) ) PARTITION BY RANGE COLUMNS(region, sales_date) ( PARTITION p1 VALUES LESS THAN ('East', '2022-01-01'), PARTITION p2 VALUES LESS THAN ('West', '2022-01-01'), PARTITION p3 VALUES LESS THAN ('East', MAXVALUE), PARTITION p4 VALUES LESS THAN ('West', MAXVALUE) );
在上述示例中,我们创建了一个名为sales的表,并使用RANGE COLUMNS分区策略。根据region和sales_date两列的范围将数据分布到不同的分区。每个分区根据这两列的范围值进行划分。
下面是一个LIST COLUMNS分区的代码示例:
CREATE TABLE users ( id INT, username VARCHAR(50), region VARCHAR(50), category VARCHAR(50) ) PARTITION BY LIST COLUMNS(region, category) ( PARTITION p_east VALUES IN (('New York', 'A'), ('Boston', 'B')), PARTITION p_west VALUES IN (('Los Angeles', 'C'), ('San Francisco', 'D')), PARTITION p_other VALUES IN (DEFAULT) );
在上述示例中,我们创建了一个名为users的表,并使用LIST COLUMNS分区策略。根据region和category两列的离散值将数据分布到不同的分区。每个分区根据这两列的离散值进行划分。
在 MySQL5.6.1 之前可以通过命令 show variables like '%have_partitioning%' 来查看 MySQL 是否支持分区。如果 have_partitioning 的值为 YES,则表示支持分区。
从 MySQL5.6.1 开始,have_partitioning 参数已经被去掉了,而是用 SHOW PLUGINS 来代替。若有 partition 行且 STATUS 列的值为 ACTIVE,则表示支持分区,如下所示:
CREATE TABLE sales ( id INT, sales_date DATE, amount DECIMAL(10,2) ) PARTITION BY RANGE (YEAR(sales_date)) ( PARTITION p1 VALUES LESS THAN (2020), PARTITION p2 VALUES LESS THAN (2021), PARTITION p3 VALUES LESS THAN (2022), PARTITION p4 VALUES LESS THAN MAXVALUE );
ALTER TABLE sales ADD PARTITION ( PARTITION p5 VALUES LESS THAN (2023) );
ALTER TABLE sales DROP PARTITION p3;
ALTER TABLE sales REORGANIZE PARTITION p1, p2, p5 INTO ( PARTITION p1 VALUES LESS THAN (2020), PARTITION p2 VALUES LESS THAN (2022), PARTITION p3 VALUES LESS THAN MAXVALUE );
ALTER TABLE sales COALESCE PARTITION p1, p2;
ALTER TABLE sales ANALYZE PARTITION p1;
MySQL分区表概述
我们经常遇到一张表里面保存了上亿甚至过十亿的记录,这些表里面保存了大量的历史记录。 对于这些历史数据的清理是一个非常头疼事情,由于所有的数据都一个普通的表里。所以只能是启用一个或多个带where条件的delete语句去删除(一般where条件是时间)。 这对数据库的造成了很大压力。即使我们把这些删除了,但底层的数据文件并没有变小。面对这类问题,最有效的方法就是在使用分区表。最常见的分区方法就是按照时间进行分区。
分区一个最大的优点就是可以非常高效的进行历史数据的清理。
1. 确认MySQL服务器是否支持分区表
命令:
show plugins; |
2. MySQL分区表的特点
在逻辑上为一个表,在物理上存储在多个文件中
HASH分区(HASH)
HASH分区的特点
如何建立HASH分区表
以INT类型字段 customer_id为分区键
CREATE TABLE `customer_login_log` ( `customer_id` int(10) unsigned NOT NULL COMMENT '登录用户ID', `login_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '用户登录时间', `login_ip` int(10) unsigned NOT NULL COMMENT '登录IP', `login_type` tinyint(4) NOT NULL COMMENT '登录类型:0未成功 1成功' ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户登录日志表' PARTITION BY HASH(customer_id) PARTITIONS 4;
以非INT类型字段 login_time 为分区键(需要先转换成INT类型)
CREATE TABLE `customer_login_log` ( `customer_id` int(10) unsigned NOT NULL COMMENT '登录用户ID', `login_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '用户登录时间', `login_ip` int(10) unsigned NOT NULL COMMENT '登录IP', `login_type` tinyint(4) NOT NULL COMMENT '登录类型:0未成功 1成功' ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户登录日志表' PARTITION BY HASH(UNIX_TIMESTAMP(login_time)) PARTITIONS 4;
customer_login_log 表如果不分区,在物理磁盘上文件为
customer_login_log.frm # 存储表原数据信息 customer_login_log.ibd # Innodb数据文件
如果按上面的建HASH分区表,则有五个文件
customer_login_log.frm customer_login_log#P#p0.ibd customer_login_log#P#p1.ibd customer_login_log#P#p2.ibd customer_login_log#P#p3.ibd
演示
使用起来和不分区是一样的,看起来只有一个数据库,其实有多个分区文件,比如我们要插入一条数据,不需要指定分区,MySQL会自动帮我们处理
查询
范围分区(RANGE)
RANGE分区特点
如何建立RANGE分区
如果没有定义p3分区,当插入的customer_id大于29999时会报错,定义了则超过的数据都存入p3中
RANGE分区的适用场景
LIST分区
LIST分区的特点
如何建立LIST分区
如果插入一条login_type为10的数据行,则会报错
3. 如何为登录日志表(customer_login_log)分区
业务场景
登录日志表的分区类型及分区键
分区后的用户登录日志表
按年份分区存储,所以用YEAR函数进行了转化
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 PARTITION BY RANGE (YEAR(login_time))( PARTITION p0 VALUES LESS THAN (2017), PARTITION p1 VALUES LESS THAN (2018), PARTITION p2 VALUES LESS THAN (2019) )
插入并查询数据
查询指定表中的分区数据情况
SELECT table_name,partition_name,partition_description,table_rows FROM information_schema.`PARTITIONS` WHERE table_name = 'customer_login_log';
再插入2条18年的日志,会存入p2表中
之前说过建立分区表时,最好建立一个MAXVALUE的分区,这里之所以没有建立,是为了数据维护的方便,如果我们建立了MAXVALUE分区,很容易忽视一个问题,当我们2019年有的数据插入时,会自动存入那个MAXVALUE分区中,之后在做数据维护时会不方便,所以没有建立MAXVALUE分区
而是通过计划任务的方式,在每年年底的时候增加这个分区,比如我们现在在2018年年底,我们需要在日志表中为2019年建立日志分区,否则2019年的日志都会插入失败
我们可以通过下面语句
增加分区
ALTER TABLE customer_login_log ADD PARTITION (PARTITION p3 VALUES LESS THAN(2020))
增加分区,并插入数据
删除分区
假如我们现在要删除2016年到2017年间一年的数据,因为我们已经做了分区,所以只需要通过一条语句,删除p0分区即可
ALTER TABLE customer_login_log DROP PARTITION p0;
可以发现p0分区已被删除,且2016年的日志全部被清除了
归档分区历史数据
我们可能有另一种需求对数据进行归档
Mysql版本>=5.7,归档分区历史数据非常方便,提供了一个交换分区的方法
分区数据归档迁移条件:
建表并交换分区
CREATE TABLE `arch_customer_login_log` ( `customer_id` INT unsigned NOT NULL COMMENT '登录用户ID', `login_time` DATETIME NOT NULL COMMENT '用户登录时间', `login_ip` INT unsigned NOT NULL COMMENT '登录IP', `login_type` TINYINT NOT NULL COMMENT '登录类型:0未成功 1成功' ) ENGINE=InnoDB ; ALTER TABLE customer_login_log exchange PARTITION p1 WITH TABLE arch_customer_login_log;
可以发现,原customer_login_log表中的2017年的数据(p1分区中的数据)已转移到了arch_customer_login_log表中,但是p1分区未删除,只是数据转移了,所以我们还需要执行DROP命令删除分区,以免有数据插入其中
将归档数据的存储引擎改为归档引擎
最后我们将归档数据的存储引擎改为归档引擎,命令为
ALTER TABLE customer_login_log ENGINE=ARCHIVE;
使用归档引擎的好处是:它比Innodb所占用的空间更少,但是归档引擎只能进行查询操作,不能进行写操作
4. 使用分区表的主要事项
关于MyISAM和Innodb的索引区别
1.关于自动增长
myisam引擎的自动增长列必须是索引,如果是组合索引,自动增长可以不是第一列,他可以根据前面几列进行排序后递增。
innodb引擎的自动增长咧必须是索引,如果是组合索引也必须是组合索引的第一列。
2.关于主键
myisam允许没有任何索引和主键的表存在,
myisam的索引都是保存行的地址。
innodb引擎如果没有设定主键或者非空唯一索引,就会自动生成一个6字节的主键(用户不可见)
innodb的数据是主索引的一部分,附加索引保存的是主索引的值。
3.关于count()函数
myisam保存有表的总行数,如果select count(*) from table;会直接取出出该值
innodb没有保存表的总行数,如果使用select count(*) from table;就会遍历整个表,消耗相当大,但是在加了wehre 条件后,myisam和innodb处理的方式都一样。
4.全文索引
myisam支持 FULLTEXT类型的全文索引
innodb不支持FULLTEXT类型的全文索引,但是innodb可以使用sphinx插件支持全文索引,并且效果更好。(sphinx 是一个开源软件,提供多种语言的API接口,可以优化mysql的各种查询)
5.delete from table
使用这条命令时,innodb不会从新建立表,而是一条一条的删除数据,在innodb上如果要清空保存有大量数据的表,最 好不要使用这个命令。(推荐使用truncate table,不过需要用户有drop此表的权限)
6.索引保存位置
myisam的索引以表名+.MYI文件分别保存。
innodb的索引和数据一起保存在表空间里。