[MySQL 之五] 分区

  MySQL 分区是一种 "分而治之" 的思想,根据一定的规则,将一个表分解成更小的、更容易管理的部分,用来指导分区划分的列称为分区键;而对于访问数据库的应用来说,逻辑上只有一个表或一个索引,屏蔽了底层的复杂性,分区对应用来说是完全透明的,不影响应用的业务逻辑。


1、分区的优点

① 和单个磁盘或者文件系统分区相比,可以存储更多的数据;

② 优化查询:在 where 子句中包含分区条件时,可以只扫描一个或多个分区来提高查询效率;同时在涉及 SUM()、COUNT() 这类聚合函数的查询时,可以容易地在每个分区上并行处理,最终只需要汇总所有分区得到的结果;

③ 对于已经过期或者不需要保存的数据,可以通过删除与这些数据有关的分区来快速删除数据;

④ 跨多个磁盘来分散数据查询,以获得更大的查询吞吐量。

新版本的 MySQL 默认支持分区,通过 show plugins 可以检查当前版本是否安装了分区插件。



2、各大引擎对分区的支持

  MySQL 支持使用大部分引擎(比如 MyISAM、InnoDB、Memory 等)创建分区;不支持使用 MERGE 或 CSV 存储引擎来创建分区表。

  同一个分区表的所有分区必须使用相同的存储引擎,但是对不同的分区表可以使用不同的存储引擎。

  MySQL 分区会把数据和索引完全分区,而不是单独对数据或索引分区。



3、分区类型

  分区类型有 RANGE、LIST、HASH、KEY 四种。

  如果一个分区表定义了主键或唯一索引,则分区键必须被包含在主键或唯一索引的字段中,否则无法建表成功。这很好理解,使用主键或唯一索引是最常见、最高效检索数据的方式,如果使用主键或唯一索引做分区键,根据分区定义,可以直接定位数据在哪个分区,不需要查询其余分区,最大程度发挥分区的优点。

(1)RANGE 分区

  基于一个给定连续区间范围,把数据分配到不同的分区,且区间要连续并且不能重叠。

  下面的分区表定义了 3 个分区,分区键为 store_id

其值为 < 10 的记录存储在分区 p0

其值为 10~20(不包含20)的记录存储在分区 p1

其值为 20~30(不包含30)的记录存储在分区 p2

create table emp (
    id int not null,
    ename varchar(30),
    hired date not null default '1970-01-01',
    separated date not null default '9999-12-31',
    job varchar(30) not null,
    store_id int not null
)
partition by range (store_id) (
    partition p0 values less than (10),
    partition p1 values less than (20),
    partition p2 values less than (30)
);

  假如有一条 store_id ≥ 30 的记录插入分区表,则无法插入,因为该值不在分区范围内。

  所以要考虑好分区键值的范围,一般都会设定一个默认值放在最末尾,其结构就类似于程序中的 if... else if... else if... else... 中最后的 else。

  如果分区键的值不是整数,如常见的日期时间等数据类型,在 MySQL 5.1 之前需要借助 YEAR()TO_DAY() 函数来将数据转换成整数,也是唯二支持的两个分区辅助函数,分区的关键声明语句如:

...
partition by range (YEAR(separated)) (
...

  到了 MySQL 5.5 增加了一个支持函数 TO_SECONDS() 把日期转换成秒。并提供了 RANGE COLUMNS 分区支持非整数分区,这样创建日期分区就不需要通过函数进行转换了。

...
partition by range columns (separated) (
    partition p0 values less than ('1996-01-01'),
    partition p1 values less than ('2001-01-01'),
    partition p2 values less than ('2006-01-01')
);


(2)LIST 分区

  如果分区键的取值是一些离散的值,比如订单状态,可以使用 LIST 分区。

  LIST 分区是建立离散的值列表告诉数据库特定的值属于哪个分区,而 RANGE 分区是一个连续的数据段。

  如下面的费用表根据分类进行分区,其分区划分如下:

类别为 3、5 的记录放在分区 p0

类别为 1、10 的记录放在分区 p1

类别为 4、9 的记录放在分区 p2

类别为 2 的记录放在分区 p3

类别为 6 的记录放在分区 p4

create table expenses (
    expense_date date not null,
    category int,
    amount decimal(10,3)
)partition by list(category) (
    partition p0 values in (3,5),
    partition p1 values in (1,10),
    partition p2 values in (4,9),
    partition p3 values in (2),
    partition p4 values in (6)
);

  任何分区键离散值集合之外的值的记录都无法插入

  MySQL 5.1 之前,LIST 分区只支持分区键是整数,所以若用来分区的元数据字段非整数,则需要有个中间的转换表来转换为整数;MySQL 5.5 开始通过 LIST COLUMNS 支持非整数分区,不需要额外的转换表。

create table expenses (
    expense_date date not null,
    category varchar(30),
    amount decimal(10,3)
)partition by list columns (category) (
    partition p0 values in ('lodging', 'food'),
    partition p1 values in ('flights', 'ground transportation'),
    partition p2 values in ('leisure', 'customer entertainment'),
    partition p3 values in ('communications'),
    partition p4 values in ('fees')
);


(3)COLUMNS 分区

  Columns 分区是在 MySQL 5.5 引入的分区类型,解决之前的 RANGE 分区和 LIST 分区只支持整数分区,从而导致需要额外的函数计算得到整数或者通过额外的转换表来转换为整数再分区的问题。具体的语句是 RANGE COLUMNSLIST COLUMNS,支持整数、日期时间、字符串三大数据类型。

  • 整数:只支持整型,其他数据类型比如浮点数、定点数不支持。
  • 日期时间:只支持 date 和 datetime 类型。
  • 字符串:只支持 char、varchar、binary、varbinary。

  最大的亮点是支持多列分区,比如有一些主键或唯一索引是存在多个列的,这种场景下非常需要使用多列分区。对 RANGE 分区使用多列分区是,分区键的比较是对多列值的元组的比较。

  创建一个多列分区的 RANGE 表,定义如下:

create table rc (
    a int,
    b int
) partition by range columns(a,b) (
    partition p0 values less than (0,10),
    partition p1 values less than (10,10),
    partition p2 values less than (10,20),
    partition p3 values less than (10,maxvalue),
    partition p4 values less than (maxvalue,maxvalue)
);

  现在要插入一条记录 (1,10),则确定分区的过程如下:

  • 分区 p0 的元组是 (0,10),插入的数据 a > 0,所以不会插入分区 p0

  • 分区 p1 的元组是 (10,10),插入的数据 a < 10,所以插入分区 p1,比较 b < 10 与否是在 a = 10 的基础上进行的

插入之后,可以通过以下 SQL 来查看数据在哪个分区:

SELECT
    partition_name part,
    partition_expression expr,
    partition_description descr,
    table_rows
FROM
    INFORMATION_SCHEMA.partitions
WHERE
    TABLE_SCHEMA = schema()
    AND TABLE_NAME = 'rc';

  现在要插入一条记录 (10,9),则确定分区的过程如下:

  • 分区 p0 的元组是 (0,10),插入数据的 a > 0,所以不会插入分区 p0

  • 分区 p1 的元组是 (10,10),插入数据的 a = 10,b < 10,所以插入分区 p1


(4)HASH 分区

  Hash 分区有常规 HASH 分区和线性 HASH 分区,常规 HASH 使用的是取模算法,线性 HASH 使用的是一个线性的 2 的幂的运算法则。

// 常规 HASH
create table tablename (
    ...
) partition by hash(columnname) partitions num;

// 线性 HASH
create table tablename (
    ...
) partition by linear hash(columnname) partitions num;

如下面的表使用了常规 HASH 分区,一共分了 4 个区,分区键对 4 取模来决定放到哪个分区,插入的记录对应分区键的值为 234,MOD(234,2) = 2,所以数据会放到分区 2 中。

但是常规 HASH 存在一个缺陷就是,当需要新增或减少分区时,所有记录都需要重新计算分区,代价比较大,不适合需要灵活变动的需求。

使用线性 HASH 分区能解决这个问题,线性 HASH 采用一种类似于动态散列的技术,在分区维护(包含增加、删除、合并、拆分分区)时,能处理得更加迅速,缺点是各个分区之间数据分布不太均匀,解决办法是用一个随机性和分布性比较好的散列函数先计算分区键的散列值,再用分区值分区,HASH 分区允许使用用户自定义的表达式,如下所示:

// 线性 HASH
create table tablename (
    ...
) partition by linear hash(mermerhash(columnname)) partitions num;

Mermerhash 是一个比较流行的散列函数,它计算速度快,对分布规律的输入,也能获得很好的随机性,从而让数据分布均匀。


(5)Key 分区

  Key 分区类似 Hash 分区,但不允许使用用户自定义的表达式,需要使用 MySQL 服务器提供的 HASH 函数,HASH 只支持整数分区,而 Key 分区支持使用除 Blob、Text 类型外的其他类型的列作为分区键。

create table tablename (
    ...
) partition by key(columnname) partitions num;

  创建 Key 分区表时可以不指定分区键,默认会首先选择使用主键作为分区键

  在没有主键的情况下,会选择非空唯一键作为分区键。

  作为分区键的唯一键必须是非空的,否则会报错。

  在没有主键和唯一索引的情况下,必须指定分区键。

  Key 分区同样有常规 Key 分区和线性 Key 分区,语法如下:

create table tablename (
    ...
) partition by linear key(columnname) partitions partnum;


(6)子分区

  对每个分区再次进行分割,又成为复合分区

  如上面的子分区的定义,首先外围是定义了一个 RANGE 分区,一共有 3 个分区;子分区又会对这 3 个分区分别做 HASH 分区,每个分区分成两个子分区,所以一共会被分成 6 个子分区。



4、分区管理

(1)RANGE、LIST 分区管理

删除分区

alter table tablename drop partition partname;

  某个分区被删除后,后续插入的本来应该插入这个分区的值,可能会被分到其他分区。

  如下面创建了一个表,划分了 3 个分区,插入 9 条数据,每个分区有 3 个。

  现在删除分区 p1,则 11、12、13 的记录会被删除,再重新插入这几条记录,会被插入到分区 p2,因为 10 < 11,12,13 < 30。

  如果是 LIST 分区,因为分区值是离散值,并且各个分区不重叠,所以分区删除后,在分区列上包含该分区离散值的记录无法插入成功。


增加分区

alter table tablename add partition

  对于 RANGE 分区来说,只能在分区列表的大的一端添加新分区,原则是不会导致原来分区的数据需要重新调整分区,如果是在原分区范围内划分新的分区,则会添加分区失败,如下所示:

  对于 LIST 分区来说,添加新分区的分区值,不能包含原分区的离散值,否则添加新分区失败。


重新定义分区

alter table tablename reorganize partition info

  重新定义分区,可以拆分一个分区为多个新分区,也可以合并多个相邻的分区为一个分区。


(2)HASH、KEY 分区管理

减少分区

alter table tablename coalesce partition


增加分区

alter table tablename add partition partitions 
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,919评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,567评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,316评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,294评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,318评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,245评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,120评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,964评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,376评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,592评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,764评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,460评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,070评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,697评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,846评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,819评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,665评论 2 354

推荐阅读更多精彩内容

  • 获取关于分区的信息 本章讨论MySQL 5.1.中实现的分区。关于分区和分区概念的介绍可以在18.1节,“M...
    裘马轻狂大帅阅读 735评论 0 51
  • 分区是指根据一定的规则,数据库把一个表分解成多个更小的,更容易管理的部分。就访问数据库的应用而言,逻辑上只有一个表...
    微日月阅读 1,496评论 0 7
  • MySQL从5.1版本开始支持分区功能,它允许可设置的一定逻辑,跨文件系统分配单个表的多个部分,但是就访问数据库而...
    仔仔H阅读 3,014评论 0 1
  • mysql高级之表分区 下列说明为个人见解,欢迎交流指正。 1、表分区简介 1.1 问题概述 问题引出:假设一个商...
    道无虚阅读 33,383评论 1 19
  • 什么是数据库分区? Mysql数据库中的数据是以文件的形式保存在磁盘上的,默认放在mysql/data路径,不同的...
    但时间也偷换概念阅读 2,891评论 0 1