分区概述
分区的基本概念
MySQL分区和计算机中的分区,并不相同。MYSQL分区是一种将单个表或索引的数据分散到多个物理子表或子索引的技术,简单来说MYSQL将数据表拆分解为更小的、更易于管理的片段**(segment)**,这些片段称为"分区",有几个分区就有几个.idb文件,每个子表或子索引称为一个分区,但它们在逻辑上仍然被视为一个整体,物理存储上是独立的每个分区。
分区可以根据特定的规则存储数据表中的行,例如,一个基于时间的分区策略可能会将数据分成按年或按月进行分区,或者按照地区进行分区。
分区的工作原理
当执行查询时,MySQL会根据查询条件和分区策略,只扫描必要的分区,而不是整个表。这样可以大大减少I/O操作,提高查询速度。此外,分区还支持数据的并行处理,例如,可以在多个分区上同时执行数据的插入、更新和删除操作,进一步提高性能。
分区的优点
- 性能提升:通过减少I/O操作和并行处理,分区可以提高查询性能。
- 简化数据管理:可以独立地对单个分区进行备份、恢复和优化,而不是对整个表进行操作。
- 提高可扩展性:通过增加新的分区,可以轻松扩展表的容量。
分区存储引擎
标准分区
Mariadb中的分区功能通过插件形成存储引擎实现的,Mariadb默认的为标准分区(姑且这么称呼,官方文档里没有指出名称),使用简单,默认已经开启。
除了标准分区以外,Mariadb还支持如下特定分区引擎(需要单独安装)
MERGE存储引擎
MERGE存储引擎只支持MyISAM表;
SPIDER存储引擎
Spider基于MySQL分区表(MySQL Partition Table)来进行数据分片,不同的是分区表中各个分区的数据仍在该MySQL实例上,而Spider是将这些分区数据分片后放到其他MySQL实例(也支持放到非MySQL实例)上。
SPIDER 架构图:本地只存在索引表,真正的数据存通过分片,分布式存放在Backend1和Backend1
SPIDER分区为最佳实践,但是成本略高。虽然可以部署在同一台服务器上,但是有一定的开销(即连接localhost的时候),但读取多个分区的查询将使用并行线程
CONECT存储引擎
Conect引擎支持外部数据源进行交互(MySQL、Oracle、PostgreSQL、Mongodb),但是要有ODBC、JDBC、MySQL或MongoDB API支持或者其他兼容的数据库连接协议。同时也支持Json、XML,CSV等NOSQL文件。
分区类型
在Mariadb中支持如下分区类型
- RANGE
- LIST
- RANGE COLUMNS and LIST COLUMNS
- HASH
- LINEAR HASH
- KEY
- LINEAR KEY
- SYSTEM_TIME
其中经常用的类型为RANGE、LIST,以及RANGE COLUMNS and LIST COLUMNS
RANGE分区
它的原理主要是基于列的值范围来将表的数据分成不同的分区。RANGE分区确保每个分区中的数据量尽可能均匀分布,并且分区之间是有序,不重复的
RANGE分区原理
- 定义分区索引
首先,需要选择一个列作为分区索引。这些列的索引将用于确定数据行应该放在哪个分区中。
如果需要多个列作为索引值,可以参考RANGE的变种分区 [RANGE COLUMNS分区](#RANGE COLUMNS分区)
sql
-- 定义分区索引
CREATE TABLE log
(
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
dt DATETIME NOT NULL,
user INT UNSIGNED,
PRIMARY KEY (id, dt)
)
ENGINE = InnoDB
- 定义分区范围
然后,需要为每个分区定义一个范围。这些范围是基于分区键值,必须是连续的、不重复的,并且分区键的值必须是整数或可以转换为整数的类型(如日期和时间)。
例如,日志表根据日期列进行RANGE分区,定义分区时间范围:2013,2014...或者覆盖日期和时间'2020-01-01 00:00:00'到'2020-06-30 00:00:00'、'2020-07-01'到'2020-12-31 00:00:00'等。
sql
-- 定义分区范围
PARTITION BY RANGE (YEAR(dt))
(
PARTITION p0 VALUES LESS THAN (2013), -- 小于2013年的日志存放在p0分区
PARTITION p1 VALUES LESS THAN (2014),
PARTITION p2 VALUES LESS THAN (2015),
PARTITION p3 VALUES LESS THAN (2016)
);
-- 如果日志涵盖了日期、时间也可以使用UNIX_TIMESTAMP转换成整数
PARTITION BY RANGE (UNIX_TIMESTAMP(ts))
(
PARTITION p0 VALUES LESS THAN (UNIX_TIMESTAMP('2014-08-01 00:00:00')),
PARTITION p1 VALUES LESS THAN (UNIX_TIMESTAMP('2014-11-01 00:00:00')),
PARTITION p2 VALUES LESS THAN (UNIX_TIMESTAMP('2015-01-01 00:00:00')),
PARTITION p3 VALUES LESS THAN (UNIX_TIMESTAMP('2016-02-01 00:00:00'))
);
- 分配数据
当向表中插入数据时,MariaDB会根据分区键的值将数据分配到相应的分区中。如果分区键的值落在某个分区的范围内,那么该行数据就会被存储在那个分区中。
sql
-- 分配数据时,根据分区范围进行数据插入
INSERT INTO log(id,dt) VALUES
(1, '2015-01-01 01:01:01'),
(2, '2015-01-02 01:01:01');
-- 如果把数据分配到非指定范围内会报错
INSERT INTO log(id,dt) VALUES
(1, '2016-01-01 01:01:01'),
(2, '2015-01-01 01:01:01');
ERROR 1526 (HY000): Table has no partition for value 2016
- 查询优化
RANGE分区的一个主要优点是它可以优化查询性能。例如,如果你知道某个日期范围内的数据只存在于某个分区中,那么MariaDB就可以只查询那个分区,而不是整个表,从而大大提高查询效率**。**
sql
EXPLAIN SELECT * FROM RANGE WHERE dt > 2015
使用EXPLAIN查看执行计划,会发现查询语句只扫描了part00分区,而没有扫描其他分区。如果不使用分区的话,则会扫描全表。
注意:查询时一定要使用索引,不然会大大增加扫描时间;
- 维护和管理
RANGE分区还可以简化数据的管理和维护。可以独立地备份、恢复或优化单个分区,而不是整个表。
sql
-- mysqldump备份单个分区
mysqldump -u [username] -p[password] [database_name] [table_name] PARTITION ([partition_name]) > [backup_file.sql]
-- 重建分区索引
ALTER TABLE [table_name] OPTIMIZE PARTITION [partition_name];
-- 删除分区
ALTER TABLE [table_name] DROP PARTITION [partition_name];
LIST分区
LIST分区是MariaDB中另一种分区方法。与RANGE分区不同,LIST分区是基于列的值的,来划分数据列表的。LIST分区允许你为分区定义一系列离散的值,然后MariaDB会将具有这些离散值的行分配到相应的分区中。
LIST分区原理
LIST分区原理和RANGE分区差不多
sql
-- 定义分区索引
CREATE TABLE employees (
id INT PRIMARY KEY,
name VARCHAR(50),
department_id INT
)
-- 创建分区范围
PARTITION BY LIST (department_id) (
PARTITION p_sales VALUES IN (10, 20, 30),
PARTITION p_finance VALUES IN (40, 50),
PARTITION p_hr VALUES IN (60, 70)
);
-- 插入数据
INSERT INTO employees (id, name, department_id) VALUES
(1, 'John Doe', 10),
(2, 'Jane Smith', 20),
(3, 'Mike Johnson', 30),
(4, 'Sarah Williams', 40),
(5, 'David Lee', 50),
(6, 'Emily Brown', 60);
在这个例子中,employees表根据department_id被分成三个分区:p_sales、p_finance和p_hr。每个分区包含不同的department_id值。
RANGE COLUMNS分区
RANGE COLUMNS分区原理
RANGE COLUMNS分区类型允许根据一个或多个列的范围来划分表数据。与RANGE分区类似,RANGE COLUMNS分区也是基于列值的范围来分配数据到不同的分区,但它是基于多个列的组合来确定范围的,能够根据复合列的值范围进行更复杂的分区策略。
例如:假设我们有一个orders表,我们想要根据order_date和customer_id列的范围进行分区:
sql
CREATE TABLE orders (
order_id INT PRIMARY KEY,
order_date DATE,
customer_id INT,
amount DECIMAL(10, 2)
)
PARTITION BY RANGE COLUMNS(order_date, customer_id) (
PARTITION p0 VALUES LESS THAN ('2022-01-01', 100),
PARTITION p1 VALUES LESS THAN ('2023-01-01', 200),
PARTITION p2 VALUES LESS THAN MAXVALUE
);
在这个例子中,orders
表被分为三个分区:p0
、p1
和p2
。PARTITION BY RANGE COLUMNS(order_date, customer_id)
指定了分区键为order_date和``customer_id
的组合。每个分区的范围基于这两个列的值来确定。例如,分区p0包含所有order_date
小于2022-01-01且customer_id
小于100的行。
LIST COLUMNS分区
LIST COLUMNS分区原理
LIST COLUMNS分区类型允许根据一个或多个列的离散值来划分表数据。与LIST分区类似,LIST COLUMNS分区也是基于列值的列表来分配数据到不同的分区,但它是基于多个列的组合来确定值的,能够根据复合列的值进行更复杂的分区策略。
例如:假设我们有一个products表,我们想要根据category_id和subcategory_id列的离散值进行分区:
sql
CREATE TABLE products (
product_id INT PRIMARY KEY,
name VARCHAR(255),
category_id INT,
subcategory_id INT
)
PARTITION BY LIST COLUMNS(category_id, subcategory_id) (
PARTITION p_electronics_tv VALUES IN ((1, 1), (1, 2)),
PARTITION p_electronics_audio VALUES IN ((1, 3), (1, 4)),
PARTITION p_appliances VALUES IN ((2, NULL), (3, NULL))
);
在这个例子中,products表被分为三个分区:p_electronics_tv、p_electronics_audio和p_appliances。PARTITION BY LIST COLUMNS(category_id, subcategory_id)指定了分区键为category_id和subcategory_id的组合。每个分区都有一个值的列表,这些值是基于这两个列的组合来确定的。例如,分区p_electronics_tv包含所有category_id为1且subcategory_id为1或2的行。
方案建议
如果确定使用分区方案后,建议Mariadb升级到7.0版本,因为增加了一些新功能,比如可以方便快捷的将分区转换为表。
使用时做好规划,可以先从非业务表开始进行分区,比如LOG表之类的,慢慢过渡到非核心业务表,最后根据需求完全进行分区
文章来源
Mariadb官方文档:
https://mariadb.com/kb/en/partitioning-overview/
https://mariadb.com/kb/en/create-table/#partitions
CSDN:MariaDB 分区
https://blog.csdn.net/qq_35506960/article/details/120844282
知乎:MySQL原生的分库分表方案 - Spider存储引擎
https://zhuanlan.zhihu.com/p/47418626