MySQL 分区

分区是指根据一定的规则,数据库把一个表分解成多个更小的,更容易管理的部分。就访问数据库的应用而言,逻辑上只有一个表或一个索引,但是实际上这个表可能由数 10 个物理分区对象组成,每个分区都是一个独立的对象,可以独自处理,可以作为表的一部分进行处理。分区对应用来说是完全透明的,不影响应用的业务逻辑

MySQL 分区的优点主要包括以下 4 个方面

  • 和单个磁盘或者文件系统分区相比,可以存储更多数据
  • 优化查询。在 where 子句中包含分区条件时,可以只扫描必要的一个或多个分区来提高查询效率;同时在涉及 sum()count() 这类聚合函数的查询时,可以容易的在每个分区上并行处理,最终只需要汇总所有分区得到的结果
  • 对于已经过期或者不需要保存的数据,可以通过删除与这些数据有关的分区来快速删除数据
  • 跨多个磁盘来分散数据查询,以获得更大的查询吞吐量

使用 show variables like '%partition%'; 命令来确定当前的 MySQL 是否支持分区

分区类型

  • range 分区:基于一个给定连续区间范围,把数据分配到不同的分区

  • list 分区:类似 range 分区,区别在 list 分区是基于枚举出的值列表分区,range 是基于给定的连续区间范围分区

  • hash 分区:基于给定的分区个数,把数据分配到不同的分区

  • key 分区:类似于 hash 分区

range 分区,list 分区,hash 分区都要求分区键必须是 int 类型,或者通过表达式返回 int 类型。key 分区可以使用其他类型的列(blob 或 text 列类型除外)作为分区键

无论是哪种 MySQL 分区类型,要么分区表上没有主键/唯一键,要么分区表的主键/唯一键都必须包含分区键,也就是说不能使用主键/唯一键字段之外的其他字段分区

分区的名字不区分大小写

range 分区

按照 range 分区的表是利用取值范围将数据分成分区,区间要连续并且不能互相重叠,使用 values less than 操作符进行分区定义,也使用 values less than maxvalue 子句提供给所有大于明确指定的最高值的值

create table tablename(value1 int)
partition by range(value1)(
    partition p0 values less than(10),
    partition p1 values less than(20),
    partition p2 values less than maxvalue
)

MySQL 支持在 values less than 子句中使用表达式

list 分区

list 分区是建立离散的值列表告诉数据库特定的值属于哪个分区,list 分区在很多方面类似于 range 分区,区别在 list 分区是从属于一个枚举列表的值的集合,range 分区是从属于一个连续区间值的集合

list 分区是通过使用 partition by list(expr) 子句来实现,expr 是某列值或一个基于某列值返回一个整数值的表达式,然后通过 values in(value_list) 的方式来定义分区,其中 value_lsit 是一个逗号分隔的整数列表。与 range 分区不同,list 分区不必声明任何特定的顺序

create table tablename(v1 int)
partition by list(v1)(
partition p0 values in(1,3,5),
partition p1 values in(2,4,6))

如果试图插入的列值(或者分区表达式的返回值)不包含分区值列表中时,那么 insert 操作会失败并报错。要重点注意的是,list 分区不存在类似 values less than maxvalue 这样包含其他值在内的定义方式。将要匹配的任何值都必须在值列表中找得到

columns 分区

columns 分区解决了 range 分区和 list 分区只支持整数分区,从而导致需要额外的函数计算得到整数或者通过额外的转换表来转换为整数再分区的问题

columns 分区分为 range columns 分区和 list columns 分区,它们支持整数,日期时间,字符串三个数据类型

columns 分区的亮点除了支持数据类型增加之外,另外就是它还支持多列分区

create table tname(v1 int,v2 int)
partition by range columns(v1,v2)(
    partition p0 values less than(0,10),
    partition p1 values less than(10,10),
    partition p2 values less than(10,20)
)

需要注意的是,range columns 分区键的比较是基于元组的比较,也就是基于字段组的比较。它先用插入的数据的第一个字段值和分区的第一个值进行比较,如果插入的第一个值小于分区的第一个值那么就不需要比较第二个值就属于该分区;如果第一个值等于分区的第一个值,开始比较第二个值同样如果第二个值小于分区的第二个值那么就属于该分区,以此类推

hash 分区

hash 分区主要用来分散热点读,确保数据在预先确定个数的分区中尽可能平均分布。对一个表执行 hash 分区时,MySQL 会对分区键应用一个散列函数,以此确定数据应当放在 N 个分区中的哪个分区中

MySQL 支持两种 hash 分区,常规 hash 分区线性 hash 分区(linear hash 分区)。常规 hash 使用的是取模算法,线性 hash 使用的是一个线性的 2 的幂的运算法则

使用 partition by hash(expr) partitions num 子句对分区类型,分区键和分区个数进行定义,其中 expr 是某列值或一个基于某列值返回一个整数值的表达式,num 是一个非负的整数,表示分割成分区的数量,默认 num 为 1

create table tn(v1 int)
partition by hash(v1)
partitions 4;

插入 234 ,可以通过取模算法 mod(234,4) 得知数字被插到第 2 个分区表

表达式 expr 可以是 MySQL 中有效的任何函数或者其他表达式,只要它们返回一个既非常数也非随机数的整数。每当插入/更新/删除一行数据时,这个表达式都需要计算一次,这意味着非常复杂的表达式可能会引起性能问题,MySQL 也不推荐使用涉及多列的哈希表达式

常规 hash 分区在增加分区或者合并分区时会出现问题。假设原来是 5 个常规 hash 分区,现在需要新增一个常规 hash 分区,原来的取模算法是 mod(expr,5),根据余数 0~4 分布在 5 个分区中,现在新增一个分区后,取模算法变成 mod(expr,6),根据余数 0~5 分布在 6 个分区中,原来的 5 个分区中的数据大部分都需要通过重新计算重新分区。常规 hash 在分区管理上带来的代价太大了,不适合需要灵活变动分区的需求。为了降低分区管理上的代价,MySQL 提供了线性 hash 分区,分区函数是一个线性的 2 的幂的运算法则

线性 hash 分区和常规 hash 分区在语法上唯一区别是在 partition by 子句中添加 linear 关键字

create table tn(v1 int)
partition by linear hash(v1)
partitions 4;

同样的,使用线性 hash 时,指定记录保存在哪个分区是可以计算出来的,假设将要保存记录的分区编号为 N,num 是一个非负的整数,表示分割成分区的数量,那么 N 可以通过以下算法得到

  • 首先,找到下一个大于等于 num 的 2 的幂,这个值设为 V,V 可以通过下面的公式得到:V = Power(2,Ceiling(Log(2,num)))
  • 其次,设置 N=F(column_list)&(V-1)
  • N>=num。设置 N=N&(ceiling(V/2)-1)

有意思的是,当线性 hash 的分区个数是 2 的 N 次幂时,线性 hash 的分区结果和常规 hash 的分区结果是一致的

线性 hash 分区的优点是,在分区维护(增加,删除,合并,拆分分区)时,MySQL 能够处理的更加迅速;缺点是,对比常规 hash分区的时候,线性 hash 各个分区之间数据的分布不太均衡

key 分区

按照 key 进行分区非常类似于按照 hash 进行分区,只不过 hash 分区允许使用用户自定义的表达式,而 key 分区不允许使用用户自定义的表达式,需要使用 MySQL 服务器提供的 hash 函数;同时 hash 分区只支持整数分区,而 key 分区支持除 blob 和 text 类型外其他类型的列作为分区键

partition by key(expr) 子句来创建一个 key 分区表,expr 是零个或者多个字段名的列表

与 hash 分区不同,创建 key 分区表的时候,可以不指定分区键,默认会首先选择使用主键作为分区键;在没有主键的情况,会选择非空唯一键作为分区键,作为分区键的唯一键必须是非空的,如果不是非空的,会报错;在没有主键,也没有唯一键的情况下,就不能不指定分区键了

和 hash 分区类似,在 key 分区中使用关键字 linear 具有同样的作用,也就是 linear key 分区时,分区的编号是通过 2 的幂算法得到的,而不是通过取模得到的

子分区

子分区(subpartitioning)是分区表中对每个分区再次分割,又被成为复合分区(composite partitionning)。可以对 range 或者 list 分区进行再分区,子分区可以使用 hash 和 key

create table tn(v date)
partition by range(year(v))
subpartition by hash(to_days(v))
subpartitions 2
(partition p0 values less than(2000),
partitioni p1 values less than(2010))

分区处理 NULL 值的方式

MySQL 不禁止在分区键上使用 null,分区键可能是一个字段或者一个用户定义的表达式。一般情况下,MySQL 的分区把 null 当作零值,或者一个最小值进行处理

range 分区中,null 值会被当作最小值来处理;list 分区中,null 值必须出现在枚举列表中,否则不被接受;hash/key 分区中,null 值会被当作零值来处理

由于针对不同的分区类型,null 值时而被当作零值处理,时而被当作最小值处理,为了避免在处理 null 值出现误判,更推荐通过设置字段非空和默认值来绕开 MySQL 默认对 null 值的处理

分区管理

range 和 list 分区管理

从分区的表中删除一个分区,可以使用 alter table drop partition 语句

alter table tn drop partition p0;

增加一个分区,使用 alter table add partition 语句。对于 range 分区,只能 add partition 添加新的分区到分区列表的最大一端

alter table tn add partition (partition p3 values less than(40))
alter table tn add partition (partition p3 values in(1,2))

增加 list 分区时,不能添加一个包含现有分区值列表中的任何值的分区,也就是说对一个固定的分区键值,必须指定并且只能指定一个唯一的分区,否则会出现错误

可以在不丢失数据的情况下,通过 alter table reorganize partition into 语句重新定义 range 或 list 分区

alter table xixi reorganize partition p2 into (partition p2 values less than(2020),partition p3 values less than maxvalue);

重新定义分区可以用来拆分一个 range 或 list 分区为多个 range 或 list 分区,也可以用来合并多个相邻 range 或 list 分区为一个 range 或 list 分区或者多个range 或 list 分区

alter table xixi reorganize partition p1,p2 into(partition p1 values less than maxvalue);

重新定义 range 或 list 分区时,只能重新定义相邻的分区,不能跳过某个 range 或 list 分区进行重新定义,同时重新定义的分区区间必须和原分区区间覆盖相同的区间;也不能使用重新定义分区来改变分区表分区的类型

hash 和 key 分区管理

通过 alter table coalesce partition 来拆分 hash 分区或者 key 分区

alter table xixi coalesce partition 2;

coalesce 不能用来增加分区的数量,通过 alter table add partition 语句来实现

alter table xixi add partition partitions 4;

通过 alter table xixi add partition partitions n 语句新增 hash 分区或者 key 分区时,是对原表新增 n 个分区,而不是增加到 n 个分区

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

推荐阅读更多精彩内容