mysql之表分区

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了mysql之表分区相关的知识,希望对你有一定的参考价值。

参考技术A

分区介绍:

一、什么是分区?

  所谓分区,就是将一个表分成多个区块进行操作和保存,从而降低每次操作的数据,提高性能。而对于应用来说则是透明的,从逻辑上看只有一张表,但在物理上这个表可能是由多个物理分区组成的,每个分区都是独立的对象,可以进行独立处理。

二、分区作用

  1.可以逻辑数据分割,分割数据能够有多个不同的物理文件路径。

  2.可以存储更多的数据,突破系统单个文件最大限制。

  3.提升性能,提高每个分区的读写速度,提高分区范围查询的速度。

  4.可以通过删除相关分区来快速删除数据

  5.通过跨多个磁盘来分散数据查询,从而提高磁盘I/O的性能。

  6.涉及到例如SUM()、COUNT()这样聚合函数的查询,可以很容易的进行并行处理。

  7.可以备份和恢复独立的分区,这对大数据量很有好处。

三、分区能支持的引擎

  mysql支持大部分引擎创建分区,入MyISAM、InnoDB等;不支持MERGE和CSV等来创建分区。同一个分区表中的所有分区必须是同一个存储引擎。值得注意的是,在MySQL8版本中,MyISAM表引擎不支持分区。

四、确认MySQL支持分区

  从MySQL5.1开始引入分区功能,可以如下方式查看是否支持:

  老版本用:SHOW VARIABLES LIKE \'%partition%\';

  新版本用:show plugins;

五、分区类型

  1. RANGE分区:基于属于一个给定连续区间的列值,把多行分配给分区。

    例如,可以将一个表通过年份划分成两个分区,2001 -2010年、2011-2020。

  2. LIST分区:类似于RANGE分区,LIST是列值匹配一个离散值集合中的某个值来进行选择。

    比如 根据字段 把值为1、3、5的放到一起,2、4、6的另外放到一起 等等...

  3. HASH分区:基于用户定义的表达式的返回值来进行选择分区,该表达式使用将要插入到表中的这些行的列值来进行计算,这个函数必须产生非负整数值。

  通过HASH运算来进行分区,分布的比较均匀

  4. KEY分区:类似于按HASH分区,由MySQL服务器提供其自身的哈希函数。

  按照KEY进行分区类似于按照HASH分区

六、使用分区注意事项

  1. 如果表中存在primary key 或者 unique key 时,分区的列必须是paimary key或者unique key的一个组成部分,也就是说,分区函数的列只能从pk或者uk这些key中取子集

  2. 如果表中不存在任何的paimary key或者unique key,则可以指定任何一个列作为分区列

  3. 5.5版本前的RANGE、LIST、HASH分区要求分区键必须是int;MySQL5.5及以上,支持非整形的RANGE和LIST分区,即:range columns 和 list columns (可以用字符串来进行分区)。

七、分区命名

  1. 分区的名字基本上遵循其他MySQL 标识符应当遵循的原则,例如用于表和数据库名字的标识符。应当注意的是,分区的名字是不区分大小写的。

  2. 无论使用何种类型的分区,分区总是在创建时就自动的顺序编号,且从0开始记录。

八、 创建分区

1. RANGE分区:

解读:以上为 uuid小于5时放到p0分区下,uuid大于5且小于10放到p1分区下,uuid大于10且小于15放到p2分区下,uuid大于15 一直到最大值的存在p3分区下

2. LIST分区:

解读:以上为uuid 等于1/2/3/5时放到p0分区,7/9/10放到p1分区,11/15放到p2分区。当时用insert into时 如果uuid的值不存在p0/p1/p2分区时,则会插入失败而报错。

3. HASH分区:

HASH分区主要用来确保数据在预先确定数目的分区中平均分布。在RANGE分区和LIST分区中必须明确指定一个指定的列值或列值集合以指定应该保存在哪个分区中。而在HASH分区中,MySQL会自动完成这些工作,要做的只是基于将要被哈希的列值指定一个表达式,以及指定被分区的表将要被分割成的分区数量,如:

解读:MySQL自动创建3个分区,在执行insert into时,根据插入的uuid通过算法来自动分配区间。

注意:

  (1) 由于每次插入、更新、删除一行,这个表达式都要计算一次,这意味着非常复杂的表达式可能会引起性能问题,尤其是在执行同时影响大量行的运算(例如批量插入)的时候。

  (2) 最有效率的哈希函数是只对单个表列进行计算,并且它的值随列值进行一致的增大或减小,因为这考虑了在分区范围上的“修剪”。也就是说,表达式值和它所基于的列的值变化越接近,就越能有效地使用该表达式来进行HASH分区。

3.1:线性HASH分区

  线性HASH分区在“PARTITION BY”子句中添加“LINEAR”关键字。

  线性HASH分区的有点在于增加、删除、合并和拆分分区将变得更加快捷,有利于处理含有及其大量数据的表。它的缺点在于各个分区间数据的分布不大可能均衡。

4. KEY分区

类似于HASH分区,HASH分区允许用户自定义的表达式,而KEY分区则不允许使用用户自定义的表达式;HASH分区只支持整数分区,KEY分区支持除了blob和text类型之外的其他数据类型分区。

与HASH分区不同,创建KEY分区表的时候,可以不指定分区键,默认会选择使用主键或唯一键作为分区键,没有主键或唯一键,就必须指定分区键。

解读:根据分区键来进行分区

5. 子分区

子分区是分区表中,每个分区的再次分割,适合保存非常大量的数据。

解读:主分区使用RANGE按照年来进行分区,有3个RANGE分区。这3个分区中又被进一步分成了2个子分区,实际上,整个表被分成了3 * 2 = 6个分区。每个子分区按照天进行HASH分区。小于2017的放在一起,2017-2020的放在一起,大于2020的放在一起。

注意:

  (1) 在MySQL5.1中,对于已经通过RANGE或LIST分区了的表在进行子分区是可能的。子分区既可以使用HASH分区,也可以使用KEY分区。这也被称为复合分区。

  (2) 每个分区必须有相同数量的子分区。

  (3) 如果在一个分区表上的任何分区上使用SUBPARTITION来明确定义任何子分区,那么就必须定义所有的子分区。

  (4) 每个SUBPARTITION子句必须包含(至少)子分区的一个名字。

(5) 在每个子分区内,子分区的名字必须是惟一的,目前在整个表中,也要保持唯一。例如:

子分区可以用于特别大的表,可以在多个磁盘间分配数据和索引。例如:

九、MySQL分区处理NULL值的方式

十、分区管理概述

  可以对分区进行添加、删除、重新定义、合并或拆分等管理操作。

① RANGE和LIST分区的管理

  1. 删除分区语句如:alter table tbl_test drop partition p0;

  注意:

    (1) 当删除了一个分区,也同时删除了该分区中所有的数据。

    (2) 可以通过show create table tbl_test;来查看新的创建表的语句。

    (3) 如果是LIST分区的话,删除的数据不能新增进来,因为这些行的列值包含在已经删除了的分区的值列表中。

  2. 添加分区语句如:alter table tbl_test add partition(partition p3 values less than(50));

  注意:

    (1) 对于RANGE分区的表,只可以添加新的分区到分区列表的最高端。

    (2) 对于LIST分区的表,不能添加已经包含在现有分区值列表中的任意值。

  3. 如果希望能不丢失数据的条件下重新定义分区,可以使用如下语句:

REORGANIZE会对分区的数据进行重构。

    ALTER TABLE tbl_name REORGANIZE PARTITION partition_list INTO(partition_definitions)

    (1) 拆分分区如:

    ALTER TABLE tbl_name REORGANIZE PARTITION partition_list INTO(partition s0 values less than(5),partition s1 values less than(10));

    或者如:

    ALTER TABLE tbl_name REORGANIZE PARTITION p0 INTO(partition s0 values in(1,2,3), partition s1 values in(4,5));

    (2) 合并分区如:ALTER TABLE tbl_name REORGANIZE PARTITION s0,s1 INTO(partition p0 values in(1,2,3,4,5));

  4. 删除所有分区,但保留数据,形式:ALTER TABLE tbl_name remove partitioning;

② HASH和KEY分区的管理

  1. 减少分区数量语句如:ALTER TABLE tbl_name COALESCE PARTITION 2;

  2. 添加分区数量语句如:ALTER TABLE tbl_name add PARTITION partitions 2;

③ 其他分区管理语句

  1. 重建分区:类似于先删除保存在分区中的所有记录,然后重新插入它们,可用于整理分区碎片。如:ALTER table tbl_name REBUILD PARTITION p2,p3;

  2. 优化分区:如果从分区中删除了大量的行,或者对一个带有可变长度的行(也就是说,有VARCHAR,BLOB或TEXT类型的列)做了许多修改,可以使用 ALTER TABLE tbl_name OPTIMIZE PARTITION来收回没有使用的空间,并整理分区数据文件的碎片。如:ALTER TABLE tbl_name OPTIMIZE PARTITION p2,p3;

  3. 分析分区:读取并保存分区的键分布,如:ALTER TABLE tbl_name ANALYZE PARTITION p2,p3;

  4. 检查分区:检查分区中的数据或索引是否已经被破坏,如:ALTER TABLE tbl_name CHECK PARTITION p2,p3;

  5. 修补分区:修补被破坏的分区,如:ALTER TABLE tbl_name REPAIR PARTITION p2,p3;

十、查看分区信息

  1. 查看分区信息:select * from information_schema.partitions where table_schema=\'arch1\' and table_name = \'tbl_test\' G;

  2. 查看分区上的数据:select * from tbl_test partition(p0);

  3. 查看MySQL会操作的分区:explain partitions select * from tbl_test where uuid = 2;

十一、 局限性

  1. 最大分区数目不能超过1024,一般建议对单表的分区数不要超过50个。

  2. 如果含有唯一索引或者主键,则分区列必须包含在所有的唯一索引或者主键在内。

  3. 不支持外键。

  4. 不支持全文索引,对分区表的分区键创建索引,那么这个索引也将被分区。

  5. 按日期进行分区很合适,因为很多日期函数可以用。但是对字符串来说合适的分区函数不太多。

  6. 只有RANGE和LIST分区能进行子分区,HASH和KEY分区不能进行子分区。

  7. 临时表不能被分区。

  8. 分区表对于单条记录的查询没有优势。

  9. 要注意选择分区的成本,没插入一行数据都需要按照表达式筛选插入的分区。

  10. 分区字段尽量不要可以为null

Hive性能优化之表设计优化


1 分区表

1.1 Hive查询基本原理

Hive的设计思想是通过元数据将HDFS上的文件映射成表,基本的查询原理是当用户通过HQL
语句对Hive中的表进行复杂数据处理和计算时,默认将其转换为分布式计算MapReduce程序对
HDFS中的数据进行读取处理的过程。
例如,当我们在Hive中创建一张表tb_login并关联HDFS上的文件,用于存储所有用户的登录信
息,当我们对这张表查询数据时,Hive中的实现过程如下:
⚫ step1:创建表

--创建数据库
create database tb_part;
--创建表
create table tb_login(
userid string,
logindate string
) row format delimited fields terminated by ' '\\t' ';


◼ HDFS中自动在Hive数据仓库的目录下和对应的数据库目录下,创建表的目录


⚫ step2:关联数据
load data local inpath '/export/data/login.log' into table tb_login;

◼ 数据会被自动放入HDFS中对应的表的目录下

◼ 数据在表中可以被正常读取

⚫ step3:查询数据

-- 统计3月24号的登录人数
select
logindate,
count(*) as cnt
from tb_login
where logindate = '2021- - 03- - 24'
group by logindate;


◼ 当执行查询计划时,Hive会使用表的最后一级目录作为底层处理数据的输入
◼ 先根据表名在元数据中进行查询表对应的HDFS目录


◼ 然后将整个HDFS中表的目录作为底层查询的输入,可以通过explain命令查看执行计
划依赖的数据

explain extended
select
logindate,
count ( * ) as cnt
from tb_login
where logindate = '2021- - 03- - 24'
group by logindate;

1.2 普通表结构问题

默认的普通表结构中,表的最后一级目录就是表的目录,而底层的计算会使用表的最后一级目
录作为Input进行计算,这种场景下,我们就会遇到一个问题,如果表的数据很多,而我们需要被
处理的数据很少,只是其中一小部分,这样就会导致大量不必要的数据被程序加载,在程序中被过
滤,导致大量不必要的计算资源的浪费。
例如,上面的需求中,只需要对2021-03-24日的数据进行计算,但实际上由于表结构的设计,
在底层执行MapReduce时,将整张表的数据都进行了加载,MapReduce程序中必须对所有数据进
行过滤,将3月24号的数据过滤出来,再进行处理。假设每天有1G的数据增量,一年就是365GB的
数据,按照业务需求,我们每次只需要对其中一天的数据进行处理,也就是处理1GB的数据,程序
会先加载365GB的数据,然后将364GB的数据过滤掉,只保留一天的数据再进行计算,导致了大量
的磁盘和网络的IO的损耗。

1.3 分区表设计思想

针对上面的问题,Hive提供了一种特殊的表结构来解决——分区表结构。分区表结构的设计思
想是:根据查询的需求,将数据按照查询的条件【一般都以时间】进行划分分区存储,将不同分区
的数据单独使用一个HDFS目录来进行存储,当底层实现计算时,根据查询的条件,只读取对应分
区的数据作为输入,减少不必要的数据加载,提高程序的性能。
例如,上面的需求中,我们可以将每天的用户登录数据,按照登陆日期进行分区存储到Hive
表中,每一天一个分区,在HDFS的底层就可以自动实现将每天的数据存储在不同的目录中,当用
户查询某天的数据时,可以直接使用这一天的分区目录进行处理,不需要加载其他数据。

1.4 分区表测试

基于分区表的设计实现将所有用户的登录信息进行分区存储
⚫ 创建分区表:按照登陆日期分区
–创建表
create table tb_login_part(
userid string
)
partitioned by (logindate string)
row format delimited fields terminated by ’ ‘\\t’ ';

⚫ 将所有登陆数据写入分区表,分区存储
–开启动态分区
set hive.exec.dynamic.partition.mode=nonstrict;
–按登录日期分区
insert into table tb_login_part partition(logindate)
select * from tb_login;
◼ HDFS中会自动在表的目录下,为每个分区创建一个分区目录

⚫ 查询2021-03-23或者2021-03-24的数据进行统计
select
logindate,
count ( * ) as cnt
from tb_login_part
where logindate = ‘2021- - 03- - 23’ or logindate = ‘2021- - 03- - 24’
group by logindate;

◼ 查询先检索元数据,元数据中记录该表为分区表并且查询过滤条件为分区字段,所以
找到该分区对应的HDFS目录
◼ 加载对应分区的目录作为计算程序的输入


⚫ 查看执行计划
◼ 如果不做分区表
explain extended
select
logindate,
count ( * ) as cnt
from tb_login
where logindate = ‘2021- - 03- - 23’ or logindate = ‘2021- - 03- - 24’
group by logindate;


◼ 如果做了分区表
explain extended
select
logindate,
count ( * ) as cnt
from tb_login_part
where logindate = ‘2021- - 03- - 23’ or logindate = ‘2021- - 03- - 24’
group by logindate;

2 分桶表

2.1 Hive中Join的问题

表的Join是数据分析处理过程中必不可少的操作,Hive同样支持Join的语法,Hive Join的底层
还是通过MapReduce来实现的,但是Hive实现Join时面临一个问题:如果有两张非常大的表要进行
Join,两张表的数据量都很大,Hive底层通过MapReduce实现时,无法使用MapJoin提高Join的性
能,只能走默认的ReduceJoin,而ReduceJoin必须经过Shuffle过程,相对性能比较差,而且容易
产生数据倾斜,如何解决这个问题?

2.2 分桶表设计思想

针对以上的问题,Hive中提供了另外一种表的结构——分桶表结构。分桶表的设计有别于分区
表的设计,分区表是将数据划分不同的目录进行存储,而分桶表是将数据划分不同的文件进行存储。
分桶表的设计是按照一定的规则【通过MapReduce中的多个Reduce来实现】将数据划分到不同的
文件中进行存储,构建分桶表。
如果有两张表按照相同的划分规则【按照Join的关联字段】将各自的数据进行划分,在Join时,
就可以实现Bucket与Bucket的Join,避免不必要的比较。
例如:当前有两张表,订单表有1000万条,用户表有10万条,两张表的关联字段是userid,现
在要实现两张表的Join。
我们将订单表按照userid划分为3个桶,1000万条数据按照userid的hash取余存储在对应的
Bucket中。

同理,我们再将用户表按照相同的规则,存储在3个桶中。

在Join时,只需要将两张表的Bucket0与Bucket0进行Join,Bucket1与Bucket1进行Join,Bucket2
与Bucket2进行Join即可,不用让所有的数据挨个比较,降低了比较次数,提高了Join的性能。

2.3 分桶表测试

当前有两份较大的数据文件,emp员工数据和dept部门数据,现在要基于Hive实现两张表的
Join,我们可以通过分桶实现分桶Join提高性能。
⚫ 构建普通emp表

create database if not exists db_emp;
use db_emp;
--创建普通表
create table tb_emp01(
empno string,
ename string,
job string,
managerid string,
hiredate string,
salary double,
jiangjin double,
deptno string
) row format delimited fields terminated by ' '\\t' ';
--加载数据
load data local inpath '/export/data/emp01.txt' into table tb_emp01;


⚫ 构建分桶emp表

use db_emp;
-- 创建分桶表
create table tb_emp02(
empno string,
ename string,
job string,
managerid string,
hiredate string,
salary double,
jiangjin double,
deptno string
)
clustered by(deptno) sorted by (deptno asc) into 3 buckets
row format delimited fields terminated by ' '\\t' ';
-- 写入分桶表
insert overwrite table tb_emp02
select * from tb_emp01;


⚫ 构建普通dept表

use db_emp;
-- 创建部门表
create table tb_dept01(
deptno string,
dname string,
loc string
)
row format delimited fields terminated by ',';
-- 加载数据
load data local inpath '/export/data/dept01.txt' into table tb_dept01;


⚫ 构建分桶dept表

use db_emp;
--创建分桶表
create table tb_dept02(
deptno string,
dname string,
loc string
)
clustered by(deptno) sorted by (deptno asc) into 3 buckets
row format delimited fields terminated by ',';
--写入分桶表
insert overwrite table tb_dept02
select * from tb_dept01;


⚫ 普通的Join执行计划

explain
select
a.empno,
a.ename,
a.salary,
b.deptno,
b.dname
from tb_emp01 a join tb_dept01 b on a.deptno = b.deptno;


⚫ 分桶的Join执行计划

--开启分桶 SMB join
set hive.optimize.bucketmapjoin = true;
set hive.auto.convert.sortmerge.join= true;
set hive.optimize.bucketmapjoin.sortedmerge = true;
--查看执行计划
explain
select
a.empno,
a.ename,
a.salary,
b.deptno,
b.dname
from tb_emp02 a join tb_dept02 b on a.deptno = b.deptno;

3 索引设计

3.1 Hive中的索引

在传统的关系型数据库例如MySQL、Oracle等数据库中,为了提高数据的查询效率,可以为表
中的字段单独构建索引,查询时,可以基于字段的索引快速的实现查询、过滤等操作。
Hive中也同样提供了索引的设计,允许用户为字段构建索引,提高数据的查询效率。但是Hive
的索引与关系型数据库中的索引并不相同,比如,Hive不支持主键或者外键。Hive索引可以建立在
表中的某些列上,以提升一些操作的效率,例如减少MapReduce任务中需要读取的数据块的数量。
在可以预见到分区数据非常庞大的情况下,分桶和索引常常是优于分区的。而分桶由于SMB
Join对关联键要求严格,所以并不是总能生效。官方文档:
https://cwiki.apache.org/confluence/display/Hive/LanguageManual+DDL#LanguageManualDD L-Create/Drop/AlterIndex

注意:官方明确表示,索引功能支持是从Hive0.7版本开始,到Hive3.0不再支持。

3.2 索引的原理及使用

Hive中索引的基本原理:当为某张表的某个字段创建索引时,Hive中会自动创建一张索引表,
该表记录了该字段的每个值与数据实际物理位置之间的关系,例如数据所在的HDFS文件地址,以
及所在文件中偏移量offset等信息。
Hive的索引目的是提高Hive表指定列的查询速度。没有索引时,类似’WHERE tab1.col1 = 10’ 的
查询,Hive会加载整张表或分区,然后处理所有的rows,但是如果在字段col1上面存在索引时,那
么只会加载和处理文件的一部分。
构建数据时,Hive会根据索引字段的值构建索引信息,将索引信息存储在索引表中
查询数据时,Hive会根据索引字段查询索引表,根据索引表的位置信息读取对应的文件数据。
下面我们来实现索引的构建,例如:
当前有一张分区表tb_login_part,默认查询数据时,是没有索引的,当查询登陆日期时可以通
过分区过滤来提高效率,但是如果想按照用户ID进行查询,就无法使用分区进行过滤,只能全表扫
描数据。

如果我们需要经常按照用户ID查询,那么性能就会相对较差,我们可以基于用户ID构建索引来
加快查询效率。
⚫ 可以使用Hive3.0以下版本测试
⚫ 创建索引

--为表中的 userid 构建索引
create index idx_user_id_login on table tb_login_part(userid)
--索引类型为 Compact,Hive 支持 Compact 和 Bitmap 类型,存储的索引内容不同
as 'COMPACT'
--延迟构建索引
with deferred rebuild;


⚫ 构建索引
alter index idx_user_id_login ON tb_login_part rebuild;
通过运行一个MapReduce程序来构建索引
⚫ 查看索引
desc default__tb_login_part_idx_user_id_login__;
select * from default__tb_login_part_idx_user_id_login__;

索引中记录了每个用户ID对应的文件以及在文件中的位置

⚫ 删除索引
DROP INDEX idx_user_id_login ON tb_login_part;

3.3 索引的问题与应用

⚫ 问题
Hive构建索引的过程是通过一个MapReduce程序来实现的,这就导致了Hive的一个
问题,每次Hive中原始数据表的数据发生更新时,索引表不会自动更新,必须手动执行
一个Alter index命令来实现通过MapReduce更新索引表,导致整体性能较差,维护相对
繁琐。例如:
◼ 表中数据发生新增或者修改

◼ 索引表没有更新

◼ 手动更新索引表
alter index idx_user_id_login ON tb_login_part rebuild;

⚫ 应用
由于Hive的索引设计过于繁琐,所以从Hive3.0版本开始,取消了对Hive Index的支
持及使用,不过如果使用的是Hive1.x或者Hive2.x在特定的场景下依旧可以使用Hive
Index来提高性能。
实际工作场景中,一般不推荐使用Hive Index,推荐使用ORC文件格式中的索引来代替Hive Index提高查询性能。

以上是关于mysql之表分区的主要内容,如果未能解决你的问题,请参考以下文章

Oracle 12C 新特性之表分区带 异步全局索引异步维护(一次addtruncatedropspiltmerge多个分区)

SQL Server数据库进阶之表分区实战演练

Hive 之表的种类以及相关操作

hadoop清理某个分区的数据

mysql 分区指定分区表数据存储路径,可以支持跨磁盘

mysql 分区PARTITIONS之基本使用