共计 21190 个字符,预计需要花费 53 分钟才能阅读完成。
对于作者
谢振江,OceanBase 高级技术专家。2015 年退出 OceanBase, 从事存储引擎相干工作,目前在存储 - 索引与 DDL 组,负责索引,DDL 和 IO 资源调度相干工作。
回顾关系型数据库大规模利用以来的倒退,从单机到分布式无疑是一个要害转变,促成它的是层出不穷的新业务和暴发增长的数据量。
一方面,更大的数据量意味着社会经济倒退有了更多新可能,另一方面,它也对数据库提出了更高要求,以防止存储节点增多带来的运维成本上升和可能呈现的新故障。因而,做好分布式数据库的透明性,像应用单机数据库一样应用分布式数据库成为晋升用户体验的要害之一。而 DDL 作为数据库运维中常见的变更操作,它的执行天然应该对业务方和运维人员都通明。
“DDL 操作肯定要等到夜深人静时”、“DDL 操作执行工夫很长,有时甚至须要几周工夫”……这些问题不仅令一线运维人员抓狂,也是各个数据库厂商始终尝试冲破的中央。OceanBase 认为,上述问题的完满解决取决于实现高效且通明的 DDL,即数据库应该尽可能快地执行 DDL,并保障 DDL 执行期间不影响业务方和运维人员的操作。
在 OceanBase 4.0 中,咱们基于已有的原生 Online DDL 能力进行了翻新:首先,为了晋升 DDL 操作的可用性,咱们自研了一套专门用于旁路写入的数据同步办法;此外,咱们对 DDL 的单机性能和分布式执行的可扩展性进行优化,大幅晋升用户 DDL 操作的响应速度;最初,4.0 进一步欠缺了原生 Online DDL 的框架能力,反对了主键变更、分区规定批改、批改列类型和批改字符集等性能。
咱们心愿透过这些新变动,帮忙用户轻松应答各类简单业务场景。本篇文章将探讨 OceanBase 如何实现高效且通明的 DDL,介绍 OceanBase 4.0 DDL 的新能力及设计思路:
- 更适宜用户的 DDL 是什么样的;
- OceanBase 如何做好 DDL;
- 来看看 4.0 DDL 有哪些新变动;
- 4.0 DDL 新性能上手实测。
更适宜用户的 DDL,应该是什么样的?
要答复这个问题,咱们须要先理解 DDL 的概念。在理论的数据库运维过程中,除了 SELECT
,INSERT
,UPDATE
,DELETE
等这些罕用的对数据自身进行操作解决的命令,还有 CREATE
,ALTER
,DROP
,TRUNCATE
等对表构造等数据库对象进行变更、与数据定义相干的命令,这些命令被称为 DDL。举例来说,常见的 DDL 操作有在表上减少新列、或给某列增加索引等。
在数据库倒退晚期,执行 DDL 语句被视为最低廉的数据库操作之一,这是因为 DDL 操作通常会造成表不可读写,阻塞各类进行中的工作。如果表的数据量很大,会长工夫中断数据库服务来执行 DDL,这对于被要求时刻在线的要害业务是难以承受的。因而,Online DDL 应运而生,它的提出次要是为了可能在执行 DDL 时,不阻塞失常的用户申请。目前市面上大部分数据库的 Online DDL 并未做到对用户齐全通明:
- 大部分单机数据库在 Online DDL 过程中存在短暂加锁的操作,例如应用 MySQL 数据库在大事务场景中执行 DDL,可能会阻塞用户的申请;
- 受限于架构设计,目前业界许多分布式数据库实现的 Online DDL,在某些业务场景会对用户失常的申请造成影响;
- 此外,Online DDL 产生于单机数据库,它通常只关注 DDL 是否对失常用户申请产生影响,而对数据库节点产生异样,如宕机时的应答并未提及。
而随着数据量暴发时代的到来,DDL 的执行时长也会制约业务的迭代速度。在单机数据库中,通常会采纳并行排序来尽可能减速 DDL 的执行,但它的执行速度受限于单机的性能瓶颈;到了分布式数据库,业界广泛应用模仿用户插入的形式来补全数据,然而这种形式并不能充分利用单台服务器的性能,也漠视了分布式可扩大能力提供的潜在性能。
能够这样说,仅靠传统意义上的 Online DDL 已无奈很好地满足理论业务需要。
咱们认为,更适宜用户理论业务需要的 DDL 至多要具备以下两方面能力:一方面,DDL 的执行应防止对业务方的 DML、DQL 操作产生影响,分布式环境也不会被运维人员感知,即使产生宕机等异样,DDL 操作也能胜利;另一方面,DDL 操作应具备在单机和分布式上良好的并行处理能力,帮忙用户进行疾速的业务翻新。
OceanBase 如何做好 DDL
OceanBase 心愿为用户提供一款高效并且足够通明的数据库产品。
在透明性方面:不同于单机数据库,咱们须要解决 DDL 操作过程中分布式数据库多节点状态不统一的问题。在应答这个问题时,业界的大部分数据库采纳了“DDL 优先”设计思路,然而这种思路会在一些业务场景对用户申请产生影响。而 OceanBase 采纳的是“业务申请优先”设计思路,能防止对业务申请产生影响。此外,咱们也尽量屏蔽用户对分布式数据库的感知,让用户像应用单机数据库一样,在分布式数据库中执行 DDL。
在执行效率方面:思考到局部 DDL 操作会对数据进行补全,而该步骤往往是 DDL 操作中最耗时的,咱们没有应用其余分布式数据库罕用的模仿插入方式,而是借鉴了单机数据库的设计思路,并在分布式数据库中做到数据补全的性能可扩大,实现足够高效的 DDL。
▋ 业务申请优先的分布式 Online DDL
在介绍 OceanBase 的 Online DDL 前,不得不提的是分布式数据库畛域中较为风行的 Google F1 在线异步 Schema 变更算法(出自论文《Online, Asynchronous Schema Change in F1》),CockroachDB 等泛滥分布式数据库的 Online DDL 性能均基于这一算法实现。该算法的理论过程较为简单,如果尝试用简略的话解释能够这样表述:因为执行 DDL 过程中不能对表进行禁写,大概率会呈现不同节点有不同 schema 版本的情景,而该算法会通过引入多个中间状态的 schema,以保证数据的一致性。
进一步说,Google F1 数据库因为没有全局成员列表,所以在 DDL 执行过程中无奈思考机器和事务的状态,会强制地周期性往前推动 Schema 版本,而集群中须要保障同时应用的 Schema 版本不超过两个,因而,对于事务的执行时长有限度,并且如果产生节点获取不到最新 Schema 版本的状况,该节点会他杀退出,影响其上所有的事务执行。总结而言,F1 数据库会优先推动 DDL 的执行过程,而不思考对事务状态的影响,因而,咱们将 F1 数据库的设计思路称为 DDL 优先的设计。
与 Google F1 不同的是,OceanBase 的特点是有全局的成员列表,在 DDL 变更过程中,能够与 DDL 变更表格相干的成员进行协调,当所有节点的事务状态都满足 DDL 变更一致性所需的条件时,才推动接下来的 Schema 版本,这样能够防止限度失常事务的执行,在呈现节点无奈刷新到最新 Schema 版本时,OceanBase 不会杀掉节点,只会限度该节点正在执行 DDL 语句的表格相干的事务的执行,同时不影响其余表格的执行,因而,咱们将 OceanBase 的设计思路称为业务申请优先的设计。
咱们以建索引为例,测试 Google F1 和 OceanBase 在不同场景中执行 DDL 对业务申请的影响,后果如下:
OceanBase 3.x | Google F1 | |
---|---|---|
对失常事务的影响 | 无影响 | 限度执行时长 |
无奈获取最新 Schema 时 | 过程存活,影响正在执行 DDL 表格的相干的用户事务 | 事务完结,过程他杀 |
节点宕机时 | 执行工夫变长 | 无影响 |
表 1 OceanBase 3.x 与 Google F1 建索引影响比照
而在 OceanBase 4.0 中,Online DDL 除了具备业务申请优先,对业务通明的特点之外,还在数据库节点出现异常场景时,加强了 DDL 操作的高可用个性,即便在节点宕机时,执行工夫也不肯定会变长,具体内容会在后文中详细描述。
▋ 高效的数据补全形式
数据库中有局部 DDL 操作须要对数据进行补全,比方建索引、加列等,OceanBase 从 1.4 版本开始将这些须要补全数据的 DDL 操作划分为两种模式:一种只须要在 Schema 上批改,异步进行补数据的形式,或称为 Instant DDL;另一种则是须要实时补全数据的 DDL。
对于须要实时补全数据的 DDL,业内大多数分布式数据库采纳的是模仿用户插入的形式将数据补全,这种计划的长处是实现简略,能够复用 DML 的写入能力来实现数据写入,并同步到备正本、备库等,其毛病是性能差,数据写入时会通过 SQL、事务、内存排序的构造,在 LSM-Tree 存储架构中数据最终还要通过屡次 compaction,步骤繁琐。因而,OceanBase 借鉴了单机数据库排序和旁路写入的思路进行数据补全。但与单机数据库不同的是,OceanBase 须要进行分布式排序,以及联合 LSM-Tree 存储架构开展优化以取得更好的性能。
一、分布式排序
在 OceanBase 3.x 版本中,DDL 中的分布式排序复用了旧的 SQL 执行框架中的分布式排序能力,其特点是具备分布式的性能可扩展性,但在单机的执行效率上有晋升空间。而在 4.0 基于新的 SQL 执行框架进行分布式排序后,执行性能失去了大幅度晋升。
二、联合 LSM-Tree 存储架构开展优化
不同于传统数据库的 B+Tree 的原地更新存储模型,LSM-Tree 存储架构的特点是增量数据会更新到增量的数据源 Memtable 中,不会对已写入磁盘的数据(SSTable)进行原地更新,只有在 Compaction 时,才会写入新的磁盘数据。这一特点为 OceanBase 放慢 DDL 中的数据补全提供了极大的便当:一方面,对于加列这类操作,能够天然地做到 Instant DDL,只须要在 Compaction 的进行异步地数据补全;另一方面,对于建索引这类须要实时补全数据的 DDL 操作,DDL 与 DML 会协调取得一个补全数据的版本号,而该版本号之前的事务数据都已提交,能够间接将补全的数据写入到 SSTable 中,将 DML 产生的增量数据间接写入 Memtable 中,因而,建索引过程中的增量数据能够实时保护好,无需相似于原地更新存储的追数据过程。
通过数年的倒退迭代,OceanBase 用户当初能够在线进行绝大部分 DDL 操作,包含:索引操作、列操作、生成列操作、外键操作、表操作,分区操作。
性能分类 | 反对的操作 | 是否 Online | 须要操作哪些数据 |
---|---|---|---|
增加索引 | 是 | Schema 元数据,索引表数据 | |
删除索引 | 是 | Schema 元数据 | |
重命名索引 | 是 | Schema 元数据 | |
列操作 | 增加列 | 是 | Schema 元数据,主表数据(Instant) |
删除列 | 是 | Schema 元数据,主表数据(Instant) | |
重命名列 | 是 | Schema 元数据 | |
重排列 | 是 | Schema 元数据 | |
设置列默认值 | 是 | Schema 元数据 | |
删除列默认值 | 是 | Schema 元数据 | |
批改列类型 | 是 | Schema 元数据,主表数据(Instant) | |
设置列束缚为NULL |
是 | Schema 元数据 | |
设置列束缚为NOT NULL |
是 | Schema 元数据,查问数据 | |
生成列操作 | 增加 VIRTUAL 列 |
是 | Schema 元数据 |
删除 VIRTUAL 列 |
是 | Schema 元数据 | |
增加 STORED 列 |
是 | Schema 元数据,主表数据(Instant) | |
删除 STORED 列 |
是 | Schema 元数据,主表数据(Instant) | |
外键操作 | 增加外键 | 是 | Schema 元数据,查问表数据 |
删除外键 | 是 | Schema 元数据 | |
表操作 | 批改行格局 | 是 | Schema 元数据,主表数据(Instant) |
批改块大小 | 是 | Schema 元数据,主表数据(Instant) | |
重命名表 | 是 | Schema 元数据 | |
批改压缩算法 | 是 | Schema 元数据,主表数据(Instant) | |
优化表空间 | 是 | Schema 元数据,主表数据(Instant) | |
分区操作 | 增加分区 | 是 | Schema 元数据 |
删除分区 | 是 | Schema 元数据 | |
Truncate 分区 | 是 | Schema 元数据,索引表数据(可选) |
表 2 OceanBase 反对的 Online DDL 操作
来看看 4.0 DDL 有哪些新变动
▋ 随同业务成长的 DDL 新性能
在研发 4.0 之前,咱们理解到一些用户在新业务场景中须要常常对主键、分区等数据库对象的构造进行变更,因为这类 DDL 操作须要将原表的数据进行重写,咱们称这一类 DDL 变更为须要数据重整的 DDL。
什么样的场景会须要数据重整的 DDL 呢?
- 批改分区规定: 某业务在一开始的规模较小,在业务规模增长后,数据量或者负载超过单机的规模,而业务的查问更新语句中,须要把某些列作为条件。此时须要依照这些列进行分区,将数据量或者负载打散到多个节点上;
- 批改字符集: 某业务刚开发时,误将某一列的字符集 collation 设置大小写不敏感,起初心愿革新成大小写敏感的;
- 批改列类型: 某业务表的列一开始定义为 int 列,起初业务发生变化,int 不足以表白业务的场景,须要批改为 varchar 列;
- 批改主键: 某业务的主键一开始应用业务自定义的 ID 列作为主键,起初心愿应用自增列来作为主键。
用户的业务成长不仅在于业务规模的增长,也在于业务对数据库性能的应用更加深刻。因而,DDL 性能也必须长期撑持业务倒退,随同业务成长。咱们发现,目前市面上的分布式数据库并未能对这类 DDL 操作提供良好的反对,一是性能反对上不够欠缺,例如局部数据库并不反对主键变更、分区规定变更等;二是局部数据库在做数据重整时会采纳模仿用户从新插入数据的办法,将数据从新导出并插入一遍,效率较低并且可能与用户事务产生抵触。
在 4.0 之前的版本,OceanBase 对于数据重整的 DDL,通常会采纳手工数据迁徙的办法实现变更。这种办法总体上有四个步骤:在原表的根底上退出所做的 DDL 变更,创立出一张新的空表;将原表的数据导出;将导出后的数据写入新表;将原表重命名,并且将新表重命名为老表。这一办法存在许多有余,如:操作步骤简单;工作失败时,须要借助内部工具或者人工来回滚所做的操作;迁徙效率低;遇到节点宕机会进步幂等性的解决难度(比方无主键表场景)等。
4.0 能为用户提供原生的数据重整 DDL 变更性能,一条 DDL 命令即可实现所有操作,包含批改分区规定、主键操作、批改列类型、批改字符集等,用户毋庸关怀 DDL 变更过程中呈现的环境异常。
性能分类 | 反对的操作 |
---|---|
主键操作 | 增加主键 |
删除主键 | |
批改主键 | |
列操作 | 批改列类型 包含变短、变长、跨类型变更 |
批改自增列值 | |
增加自增列 | |
表操作 | 批改字符集 批改表中所有已有数据的字符集 |
分区操作 | 重分区 非分区表转成一级、二级分区;一级分区转成其余一级或者二级分区;二级分区转成一级分区或者其余二级分区; |
表 3 4.0 DDL 新性能
为了反对好这些新性能,咱们也对原生的 Online DDL 进行了扩大:
- 新增对多个依赖对象表格原子变更的能力;
- 大幅晋升了原生 Online DDL 的数据补全性能;
- 旁路导入产生的数据也实用高可用的数据同步;
- 对表上的数据以及表的依赖对象的数据都进行数据一致性校验,保障 DDL 变更产生的数据正确性。
▋ 原子变更保证数据同步更新
原子变更可能保障用户在执行完 DDL 后,如果 DDL 是胜利的,那么用户之后都会看到新变更后的表构造和数据,如果 DDL 是失败的,那么用户会看到变更之前的原表构造和数据。而进行须要重整数据的 DDL 变更会波及到两方面的操作,一方面,须要对表上已有数据依照新的表格局进行批改;另一方面,须要对表上的依赖对象依照新的表格局进行批改,如索引、束缚、外键和触发器等。
另外,因为分布式数据库中表上的数据可能散布在不同的节点上,这也会带来两个问题:
- 如何原子地将散布在不同节点的数据、多个依赖对象进行变更;
- 每个节点感知到最新变更后的表构造的工夫会呈现不同,如何保障在变更实现后,用户只看到变更后的表构造。
为此,咱们设计了一套分布式环境下表构造的变更流程,能保障原子地对数据、多个依赖对象进行变更,并且用户在执行完 DDL 变更后,可能应用最新的表构造来对表格进行查问、DML 等操作。
在理论业务场景中,进行 DDL 变更也有可能会碰到数据库内核无奈解决的异样。举例来说,咱们有一个列类型为 int 的表格,该列上有一个惟一索引,咱们将这个列类型批改为 tinyint,如果表中有多行的该列的列值超过了 tinyint 示意的范畴,那么这些行的所有列都会被截断成 tinyint 的上界,导致多行在该列的列值有反复,不满足唯一性束缚。此时,有可能数据已实现局部的重写,DDL 在碰到这类异样后被动回滚时,可能保障用户看到的是变更之前的原表的数据,而不是看到处于变更到一半的状态。
▋ 并行能力晋升数据补全速度
业界分布式数据库广泛采纳模仿用户插入作为迁徙数据的形式,该计划存在两方面问题,一是可能会和失常业务产生行锁抵触,二是因为计划中对事务的并发管制、内存索引构造的线程安全控制、以及理论写入过程屡次写入的状况,其性能与传统单机数据库会存在显著差距。
为了在设计层面升高 DDL 变更对业务的影响,同时让 DDL 具备更高的执行效率,咱们应用相似于 OceanBase 中索引创立采纳的分布式排序、旁路写入的形式将原表的数据迁徙到新表。分布式排序形式节俭事务、内存有序构造保护、屡次 compaction 等逻辑,能够帮忙用户节俭 CPU 开销。而旁路写入可能防止数据写入到 Memtable 中以及呈现屡次 compaction 的状况,节俭内存和 IO 开销。
OceanBase 4.0 基于新的并行执行框架,从新设计了 DDL 数据补全的分布式执行打算,该打算分为两局部,第一局部是采样和扫描算子,第二局部是排序和扫描算子。
图 1 OceanBase 4.0 DDL 数据补全分布式执行打算
该计划能充分利用分布式和单机并行能力:
第一、两个并行子打算可能是运行在多台机器上的,并且是新框架上同时调度起来的,可能造成并行子打算 1 往,并行子打算 2 吐行并解决的流水线;
第二、为了避免分区间的数据歪斜,咱们将每个分区拆分成多份由不同的 sort 算子解决,每个 sort 算子会解决多个分区的数据,再联合采样划分算法,让每个分区的数据划分绝对平衡,这样就能保障工作间的排序是平衡的。
另一方面,在单机执行效率上,咱们通过一些技术晋升了性能。如:在补数据流程中尽量应用向量化来进行批处理、写本机磁盘数据和网络同步数据并行化、更高效的采样算法、新框架的静态数据引擎防止行的元数据重复拷贝等。上述优化对于所有须要补全数据的场景,包含创立索引、数据重整的 DDL 性能晋升都是有帮忙的。
▋ 更严苛的可用性要求
分布式排序和旁路写入对数据补全的性能晋升有很大的帮忙,但同时会带来一个问题:旁路写入的数据如何同步到备正本和备库?OceanBase 4.0 版本中反对将旁路写入到 SSTable 的数据通过 PAXOS 同步到备正本和备库,在备正本和备库回放时,只须要将 SSTable 中宏块的数据地址和元数据信息回放到内存状态机中,这样做有如下益处:
- 旁路写入的数据也具备高可用能力,当少数派节点宕机时,不影响 DDL 执行;
- 旁路写入的数据是通过数据编码和通用压缩的,数据量个别比原表数据量少很多,数据同步性能好;
- 备库和备正本的执行逻辑对立,不再依赖非凡代码逻辑来解决数据同步,代码更易于保护。
▋ 更全面的数据一致性校验
在做数据重整的 DDL 时,须要将原表的数据迁徙到新表,咱们心愿在 DDL 变更后,用户的数据是正确无误的。OceanBase 4.0 可能通过一致性校验保障 DDL 胜利后,迁徙数据和原表数据的一致性,即使 DDL 过程中产生了一些预期外异样导致数据不统一,OceanBase 也会回滚掉该 DDL 操作。开展来讲,OceanBase 4.0 会校验新表、新表上的索引、束缚、外键等所有对象,只有所有表中的数据,依赖对象上的数据都是统一的,DDL 操作才可能胜利。
4.0 DDL 新性能上手实测
▋ 新功能测试
一、主键操作
主键操作
- 增加主键
OceanBase(admin@test)>create table t1(c1 int);
Query OK, 0 rows affected (0.18 sec)
-- 增加主键示例
OceanBase(admin@test)>alter table t1 add primary key(c1);
Query OK, 0 rows affected (1.28 sec)
-- 增加主键验证
OceanBase(admin@test)>show create table t1;
+-------+-------------------------------------------------------+
| Table | Create Table |
+-------+-------------------------------------------------------+
| t1 | CREATE TABLE `t1` (`c1` int(11) NOT NULL,
PRIMARY KEY (`c1`)
) DEFAULT CHARSET = utf8mb4 ROW_FORMAT = DYNAMIC COMPRESSION = 'zstd_1.3.8' REPLICA_NUM = 2 BLOCK_SIZE = 16384 USE_BLOOM_FILTER = FALSE TABLET_SIZE = 134217728 PCTFREE = 0 |
+-------+-------------------------------------------------------+
1 row in set (0.03 sec)
- 删除主键
OceanBase(admin@test)>create table t1(c1 int primary key);
Query OK, 0 rows affected (0.19 sec)
-- 删除主键示例
OceanBase(admin@test)>alter table t1 drop primary key;
Query OK, 0 rows affected (1.39 sec)
-- 删除主键验证
OceanBase(admin@test)>show create table t1;
+-------+-------------------------------------------------------+
| Table | Create Table |
+-------+-------------------------------------------------------+
| t1 | CREATE TABLE `t1` (`c1` int(11) NOT NULL
) DEFAULT CHARSET = utf8mb4 ROW_FORMAT = DYNAMIC COMPRESSION = 'zstd_1.3.8' REPLICA_NUM = 2 BLOCK_SIZE = 16384 USE_BLOOM_FILTER = FALSE TABLET_SIZE = 134217728 PCTFREE = 0 |
+-------+-------------------------------------------------------+
1 row in set (0.03 sec)
- 批改主键
OceanBase(admin@test)>create table t1(c1 int, c2 int primary key);
Query OK, 0 rows affected (0.18 sec)
-- 批改主键示例
OceanBase(admin@test)>alter table t1 drop primary key, add primary key(c2);
Query OK, 0 rows affected (1.38 sec)
-- 批改主键验证
OceanBase(admin@test)>show create table t1;
+-------+-------------------------------------------------------+
| Table | Create Table |
+-------+-------------------------------------------------------+
| t1 | CREATE TABLE `t1` (`c1` int(11) DEFAULT NULL,
`c2` int(11) NOT NULL,
PRIMARY KEY (`c2`)
) DEFAULT CHARSET = utf8mb4 ROW_FORMAT = DYNAMIC COMPRESSION = 'zstd_1.3.8' REPLICA_NUM = 2 BLOCK_SIZE = 16384 USE_BLOOM_FILTER = FALSE TABLET_SIZE = 134217728 PCTFREE = 0 |
+-------+-------------------------------------------------------+
1 row in set (0.03 sec)
批改分区规定
- 非分区表转成一级、二级分区
-- 非分区表转成一级分区示例
OceanBase(admin@test)>alter table t1 partition by hash(c1) partitions 4;
Query OK, 0 rows affected (1.51 sec)
-- 非分区表转成一级分区验证
OceanBase(admin@test)>show create table t1;
+-------+-------------------------------------------------------+
| Table | Create Table |
+-------+-------------------------------------------------------+
| t1 | CREATE TABLE `t1` (`c1` int(11) DEFAULT NULL,
`c2` datetime DEFAULT NULL
) DEFAULT CHARSET = utf8mb4 ROW_FORMAT = DYNAMIC COMPRESSION = 'zstd_1.3.8' REPLICA_NUM = 2 BLOCK_SIZE = 16384 USE_BLOOM_FILTER = FALSE TABLET_SIZE = 134217728 PCTFREE = 0
partition by hash(c1)
(partition p0,
partition p1,
partition p2,
partition p3) |
+-------+-------------------------------------------------------+
1 row in set (0.03 sec)
OceanBase(admin@test)>create table t1(c1 int, c2 datetime);
Query OK, 0 rows affected (0.18 sec)
-- 非分区表转换成二级分区示例
OceanBase(admin@test)>alter table t1 partition by range(c1) subpartition by key(c2) subpartitions 5 (partition p0 values less than(0), partition p1 values less than(100));
Query OK, 0 rows affected (1.96 sec)
-- 非分区表转换成二级分区验证
OceanBase(admin@test)>show create table t1;
+-------+-------------------------------------------------------+
| Table | Create Table |
+-------+-------------------------------------------------------+
| t1 | CREATE TABLE `t1` (`c1` int(11) DEFAULT NULL,
`c2` datetime DEFAULT NULL
) DEFAULT CHARSET = utf8mb4 ROW_FORMAT = DYNAMIC COMPRESSION = 'zstd_1.3.8' REPLICA_NUM = 2 BLOCK_SIZE = 16384 USE_BLOOM_FILTER = FALSE TABLET_SIZE = 134217728 PCTFREE = 0
partition by range(c1) subpartition by key(c2) subpartition template (
subpartition p0,
subpartition p1,
subpartition p2,
subpartition p3,
subpartition p4)
(partition p0 values less than (0),
partition p1 values less than (100)) |
+-------+-------------------------------------------------------+
1 row in set (0.04 sec)
- 一级分区转成其余一级或者二级分区
OceanBase(admin@test)>create table t1(c1 int, c2 datetime, primary key(c1, c2))
-> partition by hash(c1) partitions 4;
Query OK, 0 rows affected (0.20 sec)
-- 一级分区转换成其余一级分区示例
OceanBase(admin@test)>alter table t1 partition by key(c1) partitions 10;
Query OK, 0 rows affected (1.84 sec)
-- 一级分区转换成其余一级分区验证
OceanBase(admin@test)>show create table t1;
+-------+-------------------------------------------------------+
| Table | Create Table |
+-------+-------------------------------------------------------+
| t1 | CREATE TABLE `t1` (`c1` int(11) NOT NULL,
`c2` datetime NOT NULL,
PRIMARY KEY (`c1`, `c2`)
) DEFAULT CHARSET = utf8mb4 ROW_FORMAT = DYNAMIC COMPRESSION = 'zstd_1.3.8' REPLICA_NUM = 2 BLOCK_SIZE = 16384 USE_BLOOM_FILTER = FALSE TABLET_SIZE = 134217728 PCTFREE = 0
partition by key(c1)
(partition p0,
partition p1,
partition p2,
partition p3,
partition p4,
partition p5,
partition p6,
partition p7,
partition p8,
partition p9) |
+-------+--------------------------------------------------------+
1 row in set (0.03 sec)
OceanBase(admin@test)>create table t1(c1 int, c2 datetime, primary key(c1, c2))
-> partition by hash(c1) partitions 4;
Query OK, 0 rows affected (0.19 sec)
-- 一级分区转换成二级分区示例
OceanBase(admin@test)>alter table t1 partition by range(c1) subpartition by key(c2) subpartitions 5 (partition p0 values less than(0), partition p1 values less than(100));
Query OK, 0 rows affected (1.88 sec)
-- 一级分区转换成二级分区验证
OceanBase(admin@test)>show create table t1;
+-------+-------------------------------------------------------+
| Table | Create Table |
+-------+-------------------------------------------------------+
| t1 | CREATE TABLE `t1` (`c1` int(11) NOT NULL,
`c2` datetime NOT NULL,
PRIMARY KEY (`c1`, `c2`)
) DEFAULT CHARSET = utf8mb4 ROW_FORMAT = DYNAMIC COMPRESSION = 'zstd_1.3.8' REPLICA_NUM = 2 BLOCK_SIZE = 16384 USE_BLOOM_FILTER = FALSE TABLET_SIZE = 134217728 PCTFREE = 0
partition by range(c1) subpartition by key(c2) subpartition template (
subpartition p0,
subpartition p1,
subpartition p2,
subpartition p3,
subpartition p4)
(partition p0 values less than (0),
partition p1 values less than (100)) |
+-------+-------------------------------------------------------+
1 row in set (0.03 sec)
- 二级分区转成一级分区或者其余二级分区
OceanBase(admin@test)>create table t1(c1 int, c2 datetime, primary key(c1, c2)) partition by range(c1) subpartition by key(c2) subpartitions 5 (partition p0 values less than(0), partition p1 values less than(100));
Query OK, 0 rows affected (0.23 sec)
-- 二级分区转换成一级分区示例
OceanBase(admin@test)>alter table t1 partition by key(c1) partitions 10;
Query OK, 0 rows affected (1.98 sec)
-- 二级分区转换成一级分区验证
OceanBase(admin@test)>show create table t1;
+-------+-------------------------------------------------------+
| Table | Create Table |
+-------+-------------------------------------------------------+
| t1 | CREATE TABLE `t1` (`c1` int(11) NOT NULL,
`c2` datetime NOT NULL,
PRIMARY KEY (`c1`, `c2`)
) DEFAULT CHARSET = utf8mb4 ROW_FORMAT = DYNAMIC COMPRESSION = 'zstd_1.3.8' REPLICA_NUM = 2 BLOCK_SIZE = 16384 USE_BLOOM_FILTER = FALSE TABLET_SIZE = 134217728 PCTFREE = 0
partition by key(c1)
(partition p0,
partition p1,
partition p2,
partition p3,
partition p4,
partition p5,
partition p6,
partition p7,
partition p8,
partition p9) |
+-------+--------------------------------------------------------+
1 row in set (0.03 sec)
OceanBase(admin@test)>create table t1(c1 int, c2 datetime, primary key(c1, c2)) partition by range(c1) subpartition by key(c2) subpartitions 5 (partition p0 values less than(0), partition p1 values less than(100));
Query OK, 0 rows affected (0.24 sec)
-- 二级分区转换成其余二级分区示例
OceanBase(admin@test)>alter table t1 partition by hash(c1) subpartition by key(c2) subpartition template(subpartition sp0, subpartition sp1, subpartition sp2) PARTITIONS 5;
Query OK, 0 rows affected (2.07 sec)
-- 二级分区转换成其余二级分区验证
OceanBase(admin@test)>show create table t1;
+-------+-------------------------------------------------------+
| Table | Create Table |
+-------+-------------------------------------------------------+
| t1 | CREATE TABLE `t1` (`c1` int(11) NOT NULL,
`c2` datetime NOT NULL,
PRIMARY KEY (`c1`, `c2`)
) DEFAULT CHARSET = utf8mb4 ROW_FORMAT = DYNAMIC COMPRESSION = 'zstd_1.3.8' REPLICA_NUM = 2 BLOCK_SIZE = 16384 USE_BLOOM_FILTER = FALSE TABLET_SIZE = 134217728 PCTFREE = 0
partition by hash(c1) subpartition by key(c2) subpartition template (
subpartition sp0,
subpartition sp1,
subpartition sp2)
(partition p0,
partition p1,
partition p2,
partition p3,
partition p4) |
+-------+--------------------------------------------------------+
1 row in set (0.03 sec)
批改列类型
- 批改某列的列类型,包含变短、变长、跨类型变更、批改为自增列、批改字符集等等
OceanBase(admin@test)>create table t1(c1 varchar(32), c2 int, primary key(c1,c2));
Query OK, 0 rows affected (0.19 sec)
-- 批改列类型:变短示例
OceanBase(admin@test)>alter table t1 modify c1 varchar(16);
Query OK, 0 rows affected (1.47 sec)
-- 批改列类型:变短验证
OceanBase(admin@test)>show create table t1;
+-------+-------------------------------------------------------+
| Table | Create Table |
+-------+-------------------------------------------------------+
| t1 | CREATE TABLE `t1` (`c1` varchar(16) NOT NULL,
`c2` int(11) NOT NULL,
PRIMARY KEY (`c1`, `c2`)
) DEFAULT CHARSET = utf8mb4 ROW_FORMAT = DYNAMIC COMPRESSION = 'zstd_1.3.8' REPLICA_NUM = 2 BLOCK_SIZE = 16384 USE_BLOOM_FILTER = FALSE TABLET_SIZE = 134217728 PCTFREE = 0 |
+-------+--------------------------------------------------------+
1 row in set (0.03 sec)
OceanBase(admin@test)>create table t1(c1 varchar(32), c2 int, primary key(c1,c2));
Query OK, 0 rows affected (0.17 sec)
-- 批改列类型:变长示例
OceanBase(admin@test)>alter table t1 modify c1 varchar(48);
Query OK, 0 rows affected (0.17 sec)
-- 批改列类型:变长验证
OceanBase(admin@test)>show create table t1;
+-------+-------------------------------------------------------+
| Table | Create Table |
+-------+-------------------------------------------------------+
| t1 | CREATE TABLE `t1` (`c1` varchar(48) NOT NULL,
`c2` int(11) NOT NULL,
PRIMARY KEY (`c1`, `c2`)
) DEFAULT CHARSET = utf8mb4 ROW_FORMAT = DYNAMIC COMPRESSION = 'zstd_1.3.8' REPLICA_NUM = 2 BLOCK_SIZE = 16384 USE_BLOOM_FILTER = FALSE TABLET_SIZE = 134217728 PCTFREE = 0 |
+-------+--------------------------------------------------------+
1 row in set (0.01 sec)
OceanBase(admin@test)>create table t1(c1 int, c2 int, primary key(c1,c2));
Query OK, 0 rows affected (0.17 sec)
-- 批改列类型:跨类型变更示例
OceanBase(admin@test)>alter table t1 modify c1 varchar(48);
Query OK, 0 rows affected (1.38 sec)
-- 批改列类型:跨类型变更验证
OceanBase(admin@test)>show create table t1;
+-------+-------------------------------------------------------+
| Table | Create Table |
+-------+-------------------------------------------------------+
| t1 | CREATE TABLE `t1` (`c1` varchar(48) NOT NULL,
`c2` int(11) NOT NULL,
PRIMARY KEY (`c1`, `c2`)
) DEFAULT CHARSET = utf8mb4 ROW_FORMAT = DYNAMIC COMPRESSION = 'zstd_1.3.8' REPLICA_NUM = 2 BLOCK_SIZE = 16384 USE_BLOOM_FILTER = FALSE TABLET_SIZE = 134217728 PCTFREE = 0 |
+-------+--------------------------------------------------------+
1 row in set (0.03 sec)
OceanBase(admin@test)>create table t1(c1 int, c2 int, primary key(c1,c2));
Query OK, 0 rows affected (0.18 sec)
-- 批改列类型:将某列批改为自增列示例
OceanBase(admin@test)>alter table t1 modify c1 int auto_increment;
Query OK, 0 rows affected (0.58 sec)
-- 批改列类型:将某列批改为自增列验证
OceanBase(admin@test)>show create table t1;
+-------+-------------------------------------------------------+
| Table | Create Table |
+-------+-------------------------------------------------------+
| t1 | CREATE TABLE `t1` (`c1` int(11) NOT NULL AUTO_INCREMENT,
`c2` int(11) NOT NULL,
PRIMARY KEY (`c1`, `c2`)
) AUTO_INCREMENT = 1 AUTO_INCREMENT_MODE = 'ORDER' DEFAULT CHARSET = utf8mb4 ROW_FORMAT = DYNAMIC COMPRESSION = 'zstd_1.3.8' REPLICA_NUM = 2 BLOCK_SIZE = 16384 USE_BLOOM_FILTER = FALSE TABLET_SIZE = 134217728 PCTFREE = 0 |
+-------+--------------------------------------------------------+
1 row in set (0.01 sec)
OceanBase(admin@test)>create table t1 (c1 int, c2 varchar(32), c3 varchar(32), primary key (c1), unique key idx_test_collation_c2(c2));
Query OK, 0 rows affected (0.24 sec)
-- 批改列类型:批改某列的字符集示例
OceanBase(admin@test)>alter table t1 modify column c2 varchar(32) charset gbk;
Query OK, 0 rows affected (2.12 sec)
-- 批改列类型:批改某列的字符集验证
OceanBase(admin@test)>show create table t1;
+-------+-------------------------------------------------------+
| Table | Create Table |
+-------+-------------------------------------------------------+
| t1 | CREATE TABLE `t1` (`c1` int(11) NOT NULL,
`c2` varchar(32) CHARACTER SET gbk DEFAULT NULL,
`c3` varchar(32) DEFAULT NULL,
PRIMARY KEY (`c1`),
UNIQUE KEY `idx_test_collation_c2` (`c2`) BLOCK_SIZE 16384 LOCAL
) DEFAULT CHARSET = utf8mb4 ROW_FORMAT = DYNAMIC COMPRESSION = 'zstd_1.3.8' REPLICA_NUM = 2 BLOCK_SIZE = 16384 USE_BLOOM_FILTER = FALSE TABLET_SIZE = 134217728 PCTFREE = 0 |
+-------+--------------------------------------------------------+
1 row in set (0.05 sec)
批改字符集
- 批改表中所有已有数据的字符集
OceanBase(admin@test)>create table t1 (c1 int, c2 varchar(32), c3 varchar(32), primary key (c1), unique key idx_test_collation_c2(c2));
Query OK, 0 rows affected (0.23 sec)
-- 批改表中已有数据的字符集示例
OceanBase(admin@test)>alter table t1 CONVERT TO CHARACTER SET gbk COLLATE gbk_bin;
Query OK, 0 rows affected (2.00 sec)
-- 批改表中已有数据的字符集验证
OceanBase(admin@test)>show create table t1;
+-------+-------------------------------------------------------+
| Table | Create Table |
+-------+-------------------------------------------------------+
| t1 | CREATE TABLE `t1` (`c1` int(11) NOT NULL,
`c2` varchar(32) COLLATE gbk_bin DEFAULT NULL,
`c3` varchar(32) COLLATE gbk_bin DEFAULT NULL,
PRIMARY KEY (`c1`),
UNIQUE KEY `idx_test_collation_c2` (`c2`) BLOCK_SIZE 16384 LOCAL
) DEFAULT CHARSET = gbk COLLATE = gbk_bin ROW_FORMAT = DYNAMIC COMPRESSION = 'zstd_1.3.8' REPLICA_NUM = 2 BLOCK_SIZE = 16384 USE_BLOOM_FILTER = FALSE TABLET_SIZE = 134217728 PCTFREE = 0 |
+-------+--------------------------------------------------------+
1 row in set (0.05 sec)
▋ 性能测试
咱们以创立索引为例进行测试,往表格中导入肯定行数的数据,测试建索引的工夫,该工夫次要耗费在索引数据补全,通过该测试能够评测出各个数据库的数据补全性能。
试验配置
表构造: create table t1(c1 int, c2 varchar(755)) partition by hash(c1) partitions 10;
数据量: 1000 万行;
资源配置: 单机,并行度 10,排序内存 128M;
测试场景:
create index i1 on t1(c1) global;
create index i1 on t1(c2) global;
create index i1 on t1(c1,c2) global;
create index i1 on t1(c2,c1) global;
比照指标:创立索引的耗时,单位为 s;
比照产品:单机数据库 MySQL、某分布式数据库 A 以及 OceanBase 4.0。
性能测试后果
图 2 性能测试比照
如上图所示,其中数据库 A 是通过模仿插入的形式来进行补全的,测试结果显示,OceanBase 索引构建性能,绝对于数据库 A,晋升了 10-20 倍,绝对于 MySQL,晋升了 3-4 倍。因而,应用排序和旁路写入形式来补全数据,能大幅晋升索引构建性能。OceanBase 4.0 通过单机性能优化,数据补全的速度显著快于 MySQL。
写在最初
OceanBase 4.0 反对了常见的数据重整的 DDL 性能,包含批改主键、批改列类型、批改分区规定、批改字符集等。咱们心愿通过原子变更、更全面的数据一致性校验和高可用的数据同步技术,让用户只须要执行一条语句就能实现所须要的变更操作,毋庸思考分布式环境中呈现的异样场景,缩小业务对分布式环境的感知,应用上更加通明。同时,咱们晋升了 DDL 的单机和分布式并行能力,放慢 DDL 中数据补全的速度,使得 DDL 执行更加高效。
咱们期待 4.0 对 DDL 性能的欠缺和优化,能够帮忙用户从容应对多变的业务挑战。