1. OBKV 是什么?
OBKV,OceanBase的多模KV产品,专注低成本、大规模的结构化或半结构化数据存储,并提供高效访问性能的简易操作接口。
在实现层面,OBKV Bypass了SQL层,直接基于OceanBase的分布式存储构建了多种多模KV形态,如兼容HBase接口的OBKV-HBase、支持Redis协议的OBKV-Redis,以及通过表格接口提供的OBKV-Table等。这些多模产品形态之间,OBKV通过TableAPI这一框架层,为模型层提供了封装的存储和事务处理能力。详细的架构参见下图:
2. 基础性能优化介绍
在v4.2.3(本特性也会Patch到v4.3.3),相较于之前的版本,v4.2.3~v4.3.3的OBKV单行读写性能提升约70%,批量读写性能提升80-220%。下面介绍v4.2.3 OBKV性能优化的原理,主要包含两大优化点:
- 多行Batch操作的性能优化。
- 单行操作的性能优化。
2.1. Batch性能优化
OBKV提供了Batch接口,用户可以通过调用Batch接口,将多个单操作聚合为一个批量消息,提交给服务端。Batch能提升性能意味着减少了消息请求响应的数量,所以在网络IO量不变的情况下可以处理更多的消息。在OBKV中,单操作/单分区的Batch会保证事务语义,单分区的Batch中所有的操作会作为一个事务来提交,也减少了事务的开销。
但为什么说OBKV已有的Batch能力并没能完全发挥OceanBase内核的优势呢?主要原因有两点:
- OBKV的Batch是按照分区粒度组织,分区数量会对Batch性能产生较大的影响。
- OceanBase底层内核支持了Batch语义,OBKV没有把Batch语义下压给存储,有一定的性能损失。
因此,本次优化针对性地支持LS粒度的Batch,并将Batch语义下压给存储,提升性能。
2.1.1. 支持LS粒度的 Batch
OBKV v4.2.3之前的版本,Batch是分区粒度的,当分区数比较多的时候,一个Batch的操作比较有限,不能很好的发挥Batch的优势。OceanBase 4.0版本之后,底层分布式存储引擎引入了LS(Log Stream)的概念,LS是分区的逻辑容器,一般情况下,LS可以看成是节点级别的概念,一个LS内的事务的性能和单机事务的性能是一样的。在OBKV v4.2.3,引入了更匹配LS概念的消息格式,可以按照LS来组织Batch,也就是可以按照节点维度来做Batch,客户端以及服务端消息交互,以及服务端事务的开销,都有了大幅度降低。
新的消息格式,也做了一些消息请求瘦身的优化,比如:
- Batch消息中引入列名字典:之前Batch的消息格式,是单操作消息的集合,而每个单操作消息中都单独带了列名信息,有较大冗余。之所以考虑引入字典,是因为存在每个单操作消息需要访问的列名不同的场景(比如表结构中,列数比较多,但是每次插入操作,只有部分列有值)。引入字典后,各个单操作消息中需要访问的列的信息,只用持有列名字典的引用即可。
- 对元数据做了瘦身:列的元数据用于和服务端交互列的类型,是否为空等信息,之前的消息格式采用定长的元数据结构,在新的消息格式中,元数据结构中的内容做了精简的同时,也引入了变长的元数据格式,大幅度降低了消息包中元数据的开销。
2.1.2. Batch语义下压给存储
OceanBase存储内核,很早之前支持了Multi Get的能力,在v4.2.3支持了Multi Set的能力。为了简化描述,把存储的Multi Get/Set概念,按照TableAPI的Batch Get/Set概念来统一介绍。
接下来以Batch Get为例,分别解释在存储内外做Batch的区别:假设数据在存储中逻辑组织成一颗树,在存储外做Batch Get,每个Get都是一次对存储的调用,每次调用都会从存储树的根节点开始检索数据。在存储内做Batch Get,先对这一批单操作请求的主键做排序,只调用存储一次接口,在迭代一次存储树的过程中按序检索多条记录。也即Batch语义直接下压给存储,减少存储接口调用次数,减少了存储树重复迭代的次数。
OBKV在v4.2.3中,适配了存储的批量能力,一个Batch请求(如果是同一种操作),会在TableAPI层转换为存储的一次批量调用,进一步提升Batch的性能。
2.2. 单操作的性能优化
OBKV的单操作性能,是基于OceanBase底层分布式存储的性能,能够优化的空间有限。OBKV v4.2.3引入了组提交的思路,对不同客户端的单操作请求,在数据库服务端做聚合,基于服务端优化后的Batch能力,降低事务以及存储的调用开销。通过组提交的方式,在请求密集型的场景,请求RT不明显增加的情况下,单操作性能提升70%。
本次组提交作为OBKV的一个实验性功能,需要通过配置项控制,默认关闭:
打开:AlTER SYSTEM SET enable_kv_group_commit=true
组提交在使用上,和客户端Batch有一些不同:
- 首先,客户端Batch是请求级别,通过用户显式调用Batch接口,聚合一个客户端的多个请求。组提交在服务端做批量,自动聚合多个客户端的同一类型操作,比较适合单个客户端压力不是太大,但是客户端比较多,整体压力较大的场景。
- 其次,组提交的聚合是自适应的,业务不需要设置Batch Size。当压力较小的时候,组提交会退化为单操作。当压力比较大的时候,组提交会基于请求队列的积压情况,系统平均RT等因素,自动选择聚合的粒度以及提交的时机。基于自适应组提交,系统的平均RT也不会有明显的变化。
- 最后,组提交对业务的环境,编码要求更小,可以直接通过配置项来开启或者关闭组提交,也能获得一个相对不错的服务端批量性能。但是服务端为了把多个单操作模拟成Batch,做了很多适配,比如聚集消息,把结果集拆分为单操作的响应包等,也会有一定的性能损耗,Batch在一些简单场景,可以获得更极致的性能。
综上,组提交和客户端Batch,都是基于批量的思路优化OBKV的基础性能,提供类似自动档和手动档的方式,给业务纷繁的场景以不同的选择。