9
博客如何在oceanbase上优雅的使用分区表

如何在oceanbase上优雅的使用分区表-c7电子娱乐

很多社区同学在使用 oceanbase 的时候,发现 oceanbase 官方推荐将表创建成分区表,但是在创建分区表的时候,会遇到很多问题,一方面是不清楚为什么要分区,另一方面是不知道怎么分区最合适。今天我们就来聊聊这些问题,解答大家心中的疑虑,以帮助大家后续使用 oceanbase 的时候,也能更加得心应手。

为什么要分区

在出现分布式数据库之前,主流数据库都是集中式数据库,如 oracle、mysql、sql server、postgresql 等。在使用这些数据库时,随着我们业务的数据量不断增长,单张表的数据量越来越大,慢慢就会出现包括性能及管理上的问题。因此逐渐有了将一张表拆分为多张表的需求,数据库也都开始支持分区表的能力,例如 mysql 5.1 版本开始支持分区功能。那么表做了分区之后有哪些好处呢?

    • 提高查询性能:通过将表分成多个分区,查询可利用到分区剪裁能力,很多查询只需在特定分区里进行,不需要整张表都查询或扫描,提升查询效率;
    • 方便数据维护:分区表的分区一般都是按照某种规则进行的分区,例如按照 range、hash、list、key等,这样对于数据的维护管理可按照分区维度,例如对分区进行单独的删除、新建、备份、恢复、索引重建等;
    • 方便存储管理:分区可以帮助更有效地管理存储空间。如将历史数据移动到不同的分区,以便更容易地进行归档或删除,这有助于降低整个数据库的存储成本;
    • 提升并发能力:在一些情况下,分区可以有效减少锁争用,提升请求并发能力。

当然分区可能还有一些其他的好处,但是在我们实际生产中可以看到,在这些集中式数据库中使用分区表的情况并不是很多,以 mysql 为例,更多用户使用的则是分库分表,这又是什么原因?

为什么要分库分表

既然有了分区表,为什么还要使用分库分表?这里也要从几个方面来讲:

    • 提升并发能力:分区虽然也能提升并发,但是提升是有限的,因为受限于单台机器的配置,并且配置的提升和获得的收益也不是线性的,总有一个上限。而分库分表可以将数据分到不同的机器和数据库实例上,并发可以大幅度的提升;
    • 提升扩展能力:集中式数据库即使用了表分区,但是想要再扩展存储或者计算能力,只能提升配置。而使用分库分表,则可以通过增加更多机器,将数据进一步做均衡从而实现线性的扩展,相比分区表的扩展能力,上限会更高;
    • 管理更方便:使用表分区,受限于分区能力的限制,例如 mysql 的分区表,虽然在引擎层是多个表,但是在 server 层实际还是一张表,因此所有分区共用同一个 mdl 锁,并且第一次打开分区表的时候,需要访问所有的分区等等;而分库分表则不存在这些问题,管理上会更方便很多。

因此基于以上分库分表的一些好处,实际我们看到很多公司在一些高并发、大数据量的场景,都会采用分库分表的方式,分库分表有多种方案,这里就不详细介绍了。

当然分库分表也是有一些限制,例如无法做到跨分区级别的事务、集群扩展相对麻烦、实例多且维护成本高、ddl 变更易出错、对业务有侵入性等问题。

所以随着 google 的 gfs、mapreduce,以及 bigtable 三篇论文问世之后,市面上基于这些论文,逐渐出现了各式各样的分布式数据库,力争解决以上这些问题。

分布式数据库

分布式数据库为了保证高可用、高扩展、高并发能力,大多都是采用数据分片的模式,即将数据分成很多分片,分片自动均衡在整个集群的多台物理服务器上,这也是 gfs 的核心特性之一。每个分片一般会有多个副本,副本之间基于 paxos 或者 raft 的分布式一致性协议,保证了数据的高可用能力,同时基于分布式两阶段提交,保证了跨机器跨分片的分布式事务。

这里的数据分片,一般会有两种模式,一种是自动分片,一种是手动分片。自动分片即当每个数据分片达到一定的大小之后,自动分裂成两个分片;手动分片类似于分库分表,用户根据规则提前将表创建成分区表,每个分区对应一个分片,然后分片由集群中的调度模块,自动调度到分布式集群中多台服务器上。

两种方式各有千秋,自动分片的方式,业务使用更方便些,完全不用关注底层如何分片,全由分布式数据库解决,但是也会带来一个问题,就是会产生更多的跨分片的分布式事务,因此无法达到一个非常极致的性能。而手动分片的方式,需要业务关注分区方式,业务请求尽量带分区键,从而达到集中式数据库的性能,有一定侵入性,另外手动分区可以人为控制经常关联查询的分区在同一台机器上,减少分布式事务。

oceanbase 为什么采用了手动分片的方式,主要是为了保证极致 tp 场景下及大数据量情况下的业务响应延迟始终保持快速和稳定。

以上铺垫了这么多,回到本文的主题,我们在使用 oceanbase 时,应该怎样分区,才能达到 oceanbase 的极致性能。

oceanbase 如何进行分区

oceanbase 架构特点

在介绍如何分区或分片之前,先简单看下 oceanbase 的架构,如下图所示:

1736220233

这是一个 oceanbase 典型的一个三副本部署架构,包含三个 zone,每个 zone 一份完整的数据副本,三副本保证了集群的高可用能力。每个 zone 中包含了两个 observer,一份完整的数据副本则均匀分布在同 zone 的两个 observer 上,这里的数据均衡的粒度是分区级别的。

为了方便了解,上图假设业务只有一张表t1,手动将表分成了 8 个分区 p1-p8,那么 p1-p8 就分别均衡每个 zone 到两个 observer 上,每个分区三个副本,从中选出一个 leader,通过 paxos 协议进行数据同步。同一台机器上的多个 leader 一般会在同一个 log stream 中,这种跨分片级别的事务,仍然是本地事务,如 p1 和 p2 的关联查询。这种架构相比传统单独使用分区表或者使用分库分表有什么好处呢?

    • 高可用:分区级别,少数派出现故障,可继续提供服务,tpo=0,rto<8s;
    • 高扩展:集群可通过快速加入新的 observer,分区会自动均衡到新 observer 上,业务无感知;
    • 业务透明:虽然需要我们提前手动分区,但是业务在使用上,依然像是在使用集中式数据库,数据库自己维护分布式事务、ddl 等;
    • 管理方便:具备分区表的所有的数据管理能力,并且可实现分区级别的调度,手动进行资源均衡;
    • 运维成本低:组件少、运维操作简单,节点故障等只需简单执行节点替换,数据会自动补齐;
    • 资源利用率高:每个 observer 都有分片的 leader,因此可以发挥每个 observer 的计算和存储资源。

oceanbase 上分区有这些优势,具体应该怎么分区呢?

oceanbase 分区介绍

  • 分区类型

oceanbase 支持多种分区类型,具体如下:

    • range 分区
    • range columns 分区
    • list 分区
    • list columns 分区
    • hash 分区
    • key 分区
    • 组合分区

组合分区,表示通常是先使用一种分区策略,然后在子分区再使用另外一种分区策略,适合于业务表的数据量非常大时。使用组合分区能发挥多种分区策略的优点。

  • 组合分区

组合分区支持非常多的组合形式,具体如下:

二级分区类型模板化二级分区表非模板化二级分区表
range range / range range columns / range list / range list columns / range hash / range key支持支持
range columns range / range columns range columns / range columns list / range columns list columns / range columns hash / range columns key支持支持
list range / list range columns / list list / list list columns / list hash / list key支持支持
list columns range / list columns range columns / list columns list / list columns list columns / list columns hash / list columns key支持支持
hash range / hash range columns / hash list / hash list columns / hash hash / hash key支持支持
key range / key range columns / key list / key list columns / key hash /key key支持支持

在这里介绍下什么是模版化二级分区和非模版化二级分区,模版化二级分区表示每个一级分区下的二级分区都按照模板中的二级分区定义,即每个一级分区下的二级分区定义均相同。而非模版化二级分区表示表的每个一级分区下的二级分区均可以自由定义,即每个一级分区下的二级分区的定义可以相同也可以不同。

同时分区也支持修改分区规则、添加分区、删除分区、truncate 分区等操作,这些在c7电子娱乐官网文档里有详细的介绍,这里就不啰嗦了,可参考:。

  • 分区索引

除了分区表,还需要了解下分区表的索引,分区表上的索引分全局索引和局部索引,默认情况下创建的索引为局部索引,局部索引的分区方式和主表相同,全局索引的分区方式可用户自定义,概念介绍可参照c7电子娱乐官网文档:,下面主要介绍几点全局索引和局部索引的注意事项:

    1. dml 未带分区键,如果过滤条件里也没有包含全局索引,那么这个 sql 将会访问所有的分区;如果包含了全局索引,则是可以根据全局索引定位到具体的分区上执行 sql;
    2. 如果某个字段要求全局唯一,那么在创建唯一键索引时,注意要创建成全局索引,否则这个唯一键只保证单分区内的唯一,无法保证全局唯一;
    3. 全局索引和主表的分区方式不同,因此对于数据库的增删改,不但要修改主表,还要修改全局索引表,很有可能产生分布式事务,因此建议全局索引不要太多,一般建议不超过 3 个;
    4. 全局索引创建时可使用分区的方式,也可以不使用分区的方式,分区数量可以参照表分区数量来规划,主要目的也是实现数据的打散和流量的均衡。

分区注意事项

那么在 oceanbase 数据库中创建分区表, 有些用户可能会忽略掉一些信息,导致分区表创建不成功,这里也简单列举一些注意事项:

    • 分区键必须是主键或唯一键的子集,只要有主键就必须是主键的子集;
    • 单个表最多支持创建的分区个数为由租户级配置项 max_partition_num 控制,默认为 8192 个;
    • 对于 hash 分区和 key 分区的一级分区,不支持添加分区;
    • 对于 range/range columns 分区,只能在最大的分区之后添加一个分区,不可以在中间或者开始的地方添加。如果当前的分区中有 maxvalue 的分区,则不能继续添加分区;
    • list/list columns 分区添加一级分区时,要求添加的分区不与之前的分区冲突即可。如果一个 list/list columns 分区有默认分区即 default partition,则不能添加任何分区;
    • 目前不支持添加二级分区。

基于这些注意事项,后续在使用分区表的时候可以避免很多问题。接下来就重点介绍下,在使用 oceanbase分区表时的一些最佳实践。

分区最佳实践

创建分区表是一项非常重要的工作,因为如果创建的不合适,后续业务性能就会有很大影响,再想去重新分区,就非常麻烦。因此接下来介绍一些最佳实践,从大家经常会产生疑问的几个问题的角度,来做一个介绍,经验之谈,欢迎指正:

  • 什么样的表适合用分区表?

首先,是不是所有的表都适合做分区表?这个不绝对,分区表的目的主要是为了实现表的扩展和并发能力,扩展指的是表数据量非常大的时候,单个存储无法容纳,需要多个存储,并且随着数据量的增加,可以进一步扩展到更多的存储上,而分区之后,数据的均衡可以按照分区的粒度均衡到多台机器上。并发扩展则是单张表上的访问请求过高,将表打散成多个分区,均衡到多台机器之后,每台机器可以做到均摊这张表上的请求流量。

如果是一张小表,数据量低于百万行,后续这张表的数据量基本不会有增长,查询量也不大的情况下,可以不用做分区,分区之后对于不带分区键的查询,如果创建全局索引,则会对写入、更新等有性能影响,如果不用全局索引,那么又会导致所有分区都被扫描,也会影响查询性能。

对于大表,数据量上千万行,数据量增长非常快,并且存在数据热点的情况下,可以考虑对表进行分区,尽量做到通过分区将数据打散,一方面缓解存储压力,另一方面,解决数据在某个范围或者某几个值上的热点问题。

  • 使用什么样的分区类型?

上面介绍了 oceanbase 支持很多分区类型,并且也支持组合分区,那么实际业务中,应该怎样选择分区类型呢?其实主要要做的就是将表模型和分区类型做好匹配,几种分区类型的特点如下:

    1. range 分区按照分区键的值的范围将数据映射到不同的分区中,分区键必须是整数类型或 year 类型,那么对于一些日志表、归档表等,就可以使用 range 分区,分区键不是整数类型或 year 类型的可使用函数进行转换;
    2. 如果想按照范围进行分区,但是字段不是整数类型或者 year 类型,或者分区键想用多个列,即向量列,那么就可以使用 range columns 的方式进行分区;
    3. list 分区则是用户显式的控制记录行映射到哪些分区,分区键也必须是整数或者 year 类型,也可以是一个表达式。例如控制id字段值为 1、3、5 的在一个分区,2、4、6 的在另一分区中就可以使用 list 分区;
    4. 同样当使用 list 分区为多个列或者字段类型不是整数或 year 类型,也可以使用 list columns 的分区方式;
    5. hash 分区则是通过对分区键上的值通过 hash 函数之后散列到不同分区中,分区键必须是整数类型或 year 类型,也可用表达式;hash 分区适合场景主要是:1、不能指定数据的分区键的列表特征;2、不同范围内的数据大小相差非常大,并且很难手动调整均衡;3、使用 range 分区后数据聚集严重;4、并行 dml、分区剪枝和分区连接等性能非常重要;
    6. key 分区和 hash 分区有些类似,通过对分区键应用 hash 算法后得到的整型值进行取模操作,从而确定数据应该属于哪个分区,key 分区有几个特点:1、分区键不要求为整型,可以为除 text 和 blob 以外的其他数据类型;2、分区键不能使用表达式;3、分区键支持向量;4、分区键中不指定任何列时,表示 key 分区的分区键是主键。
    7. 组合分区,一般主要目的是将数据在一级分区的基础上进一步打散,二级分区策略也可以参考以上一级分区的策略;

以上就是几种分区类型的特点,针对不同类型的表,选择适合的分区方式即可。

  • 分区表应该创建多少个分区合适?

创建多少个分区,并没有严格的规定。这里有几个维度可以作为参考:

    1. oceanbase 默认单表最多创建 8192 个分区,一般是不建议超过这个值。
    2. 租户内分区总数量受租户资源控制,默认租户每 1g 的内存,可创建 2w 个分区,如果租户内存为 8g,那么分区数量最多为 8*2w=16w,这里的分区数包括了索引表的分区数;
    3. 考虑未来集群最大规模,要想后续分区能够均衡到集群中每个 observer 上,那么分区数不能少于单 zone 的 observer 数;例如集群每个 zone 里 21 个 observer 节点,那么分区数不能少于 21,否则分区无法均衡到所有节点,分区数最好是 21 的倍数,如 42、84 个都可以;
    4. 一张大表分区之后,单分区大小一般建议控制在 100g - 200g 左右,当然这里并非强制要求;
    5. 一般情况下,我们集群都是三副本模式,如果 primary zone 使用的是 random 模式,为了保证每个 zone 中分区 leader 数量的均衡,分区数也一般建议是 3 的倍数。如 9 个分区,那么每个 zone 有三个分区 leader,如果是 5 副本,则建议是 5 的倍数;
    6. 在满足以上条件的情况下,分区数不建议过大,例如一张 10t 的表,集群部署模式为 7-7-7,primary zone 为 random,那么在保证单分区不大于 200g,分区数既是 7 的倍数,又是 3 的倍数,那么建议分区数可以是 63个分区,这样每个 observer 上 3 个分区,每个 zone 中 21 个分区 leader。
  1. 选用什么字段做分区键?

分区键的选择,对业务性能的影响是最大的。带分区键的 dml,sql 经过 obproxy 解析之后,可以快速定位到访问的表分区所在的 observer 上,然后直接将请求路由过去,那么一张大表的访问,就会变成一个单分区的放访问,性能会非常好;如果 dml 未带分区键,则需要引入全局索引,会影响写入、更新、删除的性能。

因此在选择分区键的时候,需要比较谨慎,一般情况下,尽量做到超过 80% 的 sql,查询过滤条件可以带上这个分区键,其余的 sql 可以通过全局索引来解决。

确认分区键之前,一般会先收集和分析下业务的 sql 模型,看下业务主要都是哪些 sql,以及每个 sql 的执行频率,根据这个这些信息,寻找过滤条件能覆盖大部分场景的字段。

确定好字段之后,因为分区键的限制,要求是主键或者唯一键的子集,如果有主键则必须是主键的子集。因此在 oceanbase 中创建表的时候,如果选到的分区键未包含在主键或者唯一键中,那么就需要对其进行调整,将分区键加入到主键中,也可以不使用主键。

如果使用自增主键做分区键的话,需要注意两点:1、oceanbase 数据库中,自增列的值全局唯一,但在分区内不保证递增;2、与其他分区方式相比,对使用自增列作为分区键的分区表的插入操作由于无法有效路由,会产生跨机事务,性能会有一定的下降;

  • 分库分表如何迁移到 oceanbase?

很多用户问到 mysql 上使用了分库分表,迁移到 oceanbase 之后,一方面是否需要做合表,将多个分表合并成一张分区表,一方面是如何做数据的迁移?

    • 是否合表

首先,是否合表需要和业务做一个沟通。如果合表,业务是否需要做修改,例如业务之前表的命名方式为 tbl_序号 的方式,如果要合表,表名肯定会发生变化,那么业务是否同意修改。如果业务不同意修改,建议直接在 oceanbase 上按照原来的方式创建表,每张表也不需要做分区,这种表分区方式通过业务控制,在oceanbase 上的性能也会很好。

如果业务同意修改,那么可以直接在 oceanbase 上创建一张大表,然后根据之前的分表的字段重新进行分区,这样就可以做到对业务完全透明,业务看到的就是一张单表,后续的运维会方便很多。

    • 如何迁移

如果没有合表,使用官方提供的 oms 直接迁移就可以,源端和目标端表是一对一关系,并且可以做数据校验。如果合表,就需要考虑几点:如何进行合表,源端分表的主键为自增 id,合表之后必然会冲突;以及合表之后,数据迁移工具如何做数据的迁移和校验。

对于主键为自增 id 的分表,迁移到 oceanbase 即使使用了分区表,id 也会出现重复,这个时候就无法再使用id字段继续作为主键,一种方式是使用联合主键,即将确认好的分区键加入到主键中,不过这个也需要确认加入分区键之后是否仍然存在冲突;一种是在同步的时候,过滤掉id字段,即在 oceanbase 端 id 字段仍然自增,但是不同步源端 id 的值,这样就可以保证 id 在 oceanbase 上不重复,为了提升性能,id 字段的自增建议采用 noorder 的模式;还有就是直接去掉 id 字段,重新创建一个主键。

以上这些场景的数据迁移,都可以使用 oms 进行同步,oms 支持多表汇聚,并且在主键不同的情况下,也可以支持数据的校验,不过需要源表和目标表都存在一个非空唯一键。oms 多表汇聚的迁移可参考:

  • 如何减少分布式事务?

前面提到如果一个事务涉及到的分区在不同的 log stream,就会产生一个分布式事务,分布式的事务性能相比单机事务性能会差一些,因此我们需要减少分布式的事务,这里有几种方式:

    1. 使用 table group,可以控制一组表在物理存储上的邻近关系,例如 a、b 两张表,join 的场景非常多,如果表分区 leader 总是在不同的机器上,那么这种 join 就会有很多分布式事务,通过 table group,可以将两张关联的表的分区 leader 在物理上绑定在同一个 log stream 中。table group 有几种设置模式,具体可参考官方文档:
    2. 如果业务端已经通过代码逻辑实现了分表,一般这种情况也基本控制了每张分表的大小,这个时候在 oceanbase 上就没必要为了分区而分区,反而可能会因为分区导致更多的分布式事务。直接将源端的表同步过来即可,然后将表的 leader 进行绑定;


以上根据个人的一些经验,大致总结了下,在 oceanbase 如何进行分区,从而发挥出比较好的性能,如果大家有更好的实践或者想法,也可以写在评论区一起讨论。



点赞9
收藏

声明

本网站下的“博客”等板块为技术爱好者提供分享、交流的平台。发布者发布的任何内容、信息等,并不反映或代表本网站的观点、立场或政策。本网站不对其任何内容和信息的错误以及由此产生的损失或损坏承担任何责任。

尊重知识产权是本网站的基本原则之一,如您在使用本网站过程中发现本网站中存在侵犯您或其他第三人合法知识产权的情况,请您即可将侵权材料及初步证据提交至下述邮箱:obcompliance@oceanbase.com 。本网站将在收到材料后尽快进行审核及处理。

洪波

已发布 20 篇博文

网站地图