OceanBase TableAPI实践案例(Java)

2024年 5月 7日 67.2k 0

引子

之前的一篇文章《OceanBase 4.0 我回来给你点个赞》里面提到了关于OceanBase驱动/接口和扩展性的一些问题,其实无论是接口、驱动、扩展性,都是以不同的形式为客户提供访问/操作数据库的手段,手段是否丰富也可以侧面反应数据库生态的完备程度。但生态工具其实并不限于上篇文章提到的接口/驱动和插件扩展性,OceanBase其实还为用户提供了很多生态工具,能让用户在其基础上自行封装一些增值服务,比如本文下面要介绍的TableAPI

TableAPI 介绍

早在OceanBase 社区版 3.1.1中开放了一组新的 API 叫做 TableAPI,TableAPI 以 API而非 SQL 的方式提供了一种新的访问 OceanBase 数据的接口。它把 OceanBase 可靠和可扩展的分布式存储层能力直接提供给应用程序,提供了灵活(非关系模型)和轻量的数据访问接口(无连接状态),应用程序可以把 TableAPI 当做 key-value , Table-store , Hbase等多种数据模型的数据库来使用。在简单读写场景下,TableAPI 比 SQL 也有一定的性能优势。

TableAPI 提供了对表模型数据的操作接口。同时,在内部,TableAPI 定义了客户端和数据库服务端之间的一组通用的交互协议。

关于TableAPI的详细介绍可以参考OceanBase官方文档:

  • TableAPI 实现介绍
  • TableAPI 数据模型
  • 客户端简介与使用说明
  • 客户端参数设置

TableAPI工作原理

总体上看,应用层可通过 TableAPI 或 SQL 两种方式访问到 OBServer,分别对应 NOSQL 模式和 SQL 模式。

NOSQL 模式下,客户端接口 JavaClient 通过 RPC 协议与 OBServer 通信,同时客户端支持丰富多样的操作函数,功能上基本可以满足大部分场景的需求。

相比于 SQL 模式,整体流程上,TableAPI 并没有复杂的 SQL 语法语义等繁琐的解析以及生成执行计划等过程,但却具有与 SQL 模式几乎同等的事务以及存储能力,使用起来相对简单。整体流程图如下:

OceanBase TableAPI实践案例(Java)-1

与 SQL 模式比较

OceanBase TableAPI 提供的表模型 API 可以和 OceanBase SQL 无缝集成,甚至共同使用。下面分别介绍 TableAPI 和 SQL 模式相比的优缺点。

优点

  • TableAPI 基于分布式存储层设计,数据访问路径短,相同功能时性能更优;因为语义简单,更容易优化,性能预期更可控(Predictable)。
  • Batch 操作的语义比 SQL 模式更灵活高效。例如,multi-get 的不同行可以选取不同的列。
  • API 接口使用简单,直接提供 entity 语义,不需要复杂的 ER 映射。
  • 使用简单的 RPC 协议,没有传统 JDBC/ODBC 接口的重新连接,几乎不受连接数的限制(目前受 RPC 框架的限制)。

缺点

  • TableAPI 的查询(Query)功能无法和 SQL 模式同日而语,提供 get、scan、limit 等有限功能。如果您需要使用聚合、排序功能,应该使用 SQL 模式。
  • TableAPI 也不提供交互式的事务等复杂事务功能。不同于传统关系数据库,OceanBase 数据库本身作为一个 SQL 功能齐全的 NewSQL 关系数据库,高扩展性和高可用性并不是选择使用 TableAPI 的考量因素。
  • TableAPI 不支持全局索引,因而不能操作创建了全局索引相关的表。

实践案例

环境准备

本次实践使用普通个人电脑(12C8G)、操作系统:windows 11自带WSL (Ubuntu 22.04.1 LTS)、开发环境:IntelliJ IDEA Community Edition。

  • CPU
frank@DESKTOP-6NF3B9K:~$ lscpu
Architecture:            x86_64
  CPU op-mode(s):        32-bit, 64-bit
  Address sizes:         39 bits physical, 48 bits virtual
  Byte Order:            Little Endian
CPU(s):                  12
  On-line CPU(s) list:   0-11
Vendor ID:               GenuineIntel
  Model name:            11th Gen Intel(R) Core(TM) i5-11400F @ 2.60GHz
    CPU family:          6
    Model:               167
    Thread(s) per core:  2
    Core(s) per socket:  6
    Socket(s):           1
    Stepping:            1
    BogoMIPS:            5183.99
    Flags:               fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constan
                         t_tsc rep_good nopl xtopology tsc_reliable nonstop_tsc cpuid pni pclmulqdq vmx ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_dea
                         dline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single ssbd ibrs ibpb stibp ibrs_enhanced tpr_shadow vnmi
                          ept vpid ept_ad fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid avx512f avx512dq rdseed adx smap avx512ifma clflushopt avx512cd sha_ni
                          avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves avx512vbmi umip avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg avx512_vpopcnt
                         dq rdpid fsrm flush_l1d arch_capabilities
Virtualization features:
  Virtualization:        VT-x
  Hypervisor vendor:     Microsoft
  Virtualization type:   full
Caches (sum of all):
  L1d:                   288 KiB (6 instances)
  L1i:                   192 KiB (6 instances)
  L2:                    3 MiB (6 instances)
  L3:                    12 MiB (1 instance)
Vulnerabilities:
  Itlb multihit:         Not affected
  L1tf:                  Not affected
  Mds:                   Not affected
  Meltdown:              Not affected
  Spec store bypass:     Mitigation; Speculative Store Bypass disabled via prctl and seccomp
  Spectre v1:            Mitigation; usercopy/swapgs barriers and __user pointer sanitization
  Spectre v2:            Mitigation; Enhanced IBRS, IBPB conditional, RSB filling
  Srbds:                 Not affected
  Tsx async abort:       Not affected

  • 内存
frank@DESKTOP-6NF3B9K:~$ free -h
               total        used        free      shared  buff/cache   available
Mem:           7.7Gi        98Mi       7.3Gi       0.0Ki       225Mi       7.3Gi
Swap:          2.0Gi          0B       2.0Gi
  • IDE

OceanBase TableAPI实践案例(Java)-2

Docker环境部署

  • 下载Docker Disktop

下载地址

  • Docker部署OceanBase 3.1.4
sudo docker run -p 2881:2881 -p 2882:2882 --name obstandalone -e MINI_MODE=1 -d oceanbase/oceanbase-ce:3.1.4

注意:这里需要注意的是,tag要加上3.1.4,否则默认latest,则是4.0.0版本,目前4.0.0版本TableAPI功能尚未开发。

OceanBase TableAPI实践案例(Java)-2

  • 验证

OceanBase TableAPI实践案例(Java)-4

OceanBase server设置

OB server端需要完成一些配置,如下:

  • 系统参数
alter system set _ob_enable_prepared_statement= true;
alter system set obconfig_url = 'http://127.0.0.1:8080/services?Action=ObRootServiceInfo&ObCluster=obcluster';
  • 创建测试用户
create user frank identified by "frank";
GRANT ALL ON *.* TO frank;
  • 创建测试表
CREATE TABLE IF NOT EXISTS `test_table` (
    `c1` bigint NOT NULL,
    `c2` int NOT NULL,
    `c3` varchar(20) DEFAULT NULL,
    PRIMARY KEY (`c1`)
);

ob-configserver部署

TableAPI的client需要获取OceanBase的rootservice list,客户端会先根据 table namerowkey 算出具体的分区号 partition id,进而根据分区号计算得出执行该指令的 observer 具体位置,然后再把 request 请求发送到对应 observer 节点。另外,客户端除了要保证基本的读写功能外,还承担着路由计算的重大使命。

能够提供rootservice list的有OCP和ob-configserver两个工具,考虑资源问题,这里使用资源占用较小的ob-configserver。

  • 编译

ob-configserver的源码在OceanBase源码目录下:${OB_SRC}/tools/ob-configserver

  • 获取OceanBase源码:git clone ``https://github.com/oceanbase/oceanbase.git
  • 安装golang编译器:sudo apt install golang
$ go version
go version go1.18.1 linux/amd64

$ git checkout 3.1
$ make build-release
  • 配置
## log config
log:
  # level: debug
  level: info
  filename: ./log/ob-configserver.log
  maxsize: 30
  maxage: 7
  maxbackups: 10
  localtime: true
  compress: true

## server config
server:
  address: "0.0.0.0:8080"
  run_dir: run

## vip config, configserver will generate url with vip address and port and return it to the client
## if you don't hava a vip, use the server address and port is ok, but do not use some random value that can't be connected
vip:
  address: "127.0.0.1"
  port: 8080

## storage config
storage:
  ## database type, support sqlite3 or mysql
  database_type: mysql
  # database_type: sqlite3

  ## database connection config, should match database_type above
  #connection_url: "user:password@tcp(127.0.0.1:3306)/oceanbase?parseTime=true"
  connection_url: "frank:frank@tcp(127.0.0.1:2881)/test?parseTime=true"
  # connection_url: "/tmp/data.db?cache=shared&_fk=1"
  # connection_url: "file:ent?mode=memory&cache=shared&_fk=1"

注意:关键是配置connection_url: "frank:frank@tcp(127.0.0.1:2881)/test?parseTime=true"

  • 启动
bin/ob-configserver -c etc/config.yaml

OceanBase TableAPI实践案例(Java)-3

  • 验证
curl 'http://127.0.0.1:8080/services?Action=ObRootServiceInfo&ObCluster=obcluster&database=test' | jq

OceanBase TableAPI实践案例(Java)-6

客户端

测试验证

这里使用的是OceanBase社区开源项目oceanbase / obkv-table-client-java 中提供的example。

OceanBase TableAPI实践案例(Java)-4

  • 在IDEA中新建Maven工程
  • 修改pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.oceanbase.example</groupId>
    <artifactId>simple-kv-demo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.oceanbase</groupId>
            <artifactId>obkv-table-client</artifactId>
            <version>0.1.3</version>
        </dependency>
    </dependencies>
</project>
  • 修改初始化代码,与之前创建的用户等信息匹配
    private String tableName = "test_table";

    public boolean initial() {
        try {
            obTableClient = new ObTableClient();
            obTableClient.setFullUserName("frank@sys#obcluster"); // e.g. root@sys#ocp
            obTableClient.setParamURL("http://172.19.227.101:8080/services?Action=ObRootServiceInfo&ObCluster=obcluster&database=test"); // e.g. http://ip:port/services?Action=ObRootServiceInfo&ObRegion=ocp&database=test
            obTableClient.setPassword("frank");
            obTableClient.setSysUserName("root@sys"); // e.g. proxyro@sys
            obTableClient.setSysPassword("");

            obTableClient.init();
        } catch (Exception e) {

注1:setParamURL使用的是ob-configserver的IP:PORT,Action是ObRootServiceInfo。

注2:URL中如果使用127.0.0.1,可能会报错,建议使用真是IP。

注3:建议不要使用sys租户下的用,我这里只是为了测试方便。

  • 输出结果如下:
initial table client success
Sat Nov 12 13:42:25 GMT+08:00 2022 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
del table data success, row = 1
del table data success, row = 1
del table data success, row = 1
get after delete ...
fail to get table data
key_1 -> null
fail to get table data
key_2 -> null
fail to get table data
key_3 -> null
put table data success
put table data success
put table data success
get after insert ...
key_1 -> c2:11, c3:c3_1
key_2 -> c2:22, c3:c3_2
key_3 -> c2:33, c3:c3_3
update table data success
key_1 -> c2:111, c3:update c3_1
insertOrUpdate table data success, row = 1
insertOrUpdate table data success, row = 1
get after insertOrUpdate ...
key_3 -> c2:333, c3:update old c3_3
key_4 -> c2:44, c3:insert c3_4
replace table data success, row = 0
key_2 -> c2:222, c3:replace c3_2
get after increment ...
key_4 -> c2:45, c3:insert c3_4
get after append ...
key_4 -> c2:45, c3:insert c3_4_append
batch Ops success.
get after batch ops ...
key_5 -> c2:55, c3:batch new c3_5
key_6 -> c2:7, c3:batch new c3_6
query result size: 5
c3,update c3_1
c1,1
c2,111
c3,replace c3_2
c1,2
c2,222
c3,update old c3_3
c1,3
c2,333
c3,insert c3_4_append
c1,4
c2,45
c3,batch new c3_5
c1,5
c2,55
query table success.

Process finished with exit code 0

接口使用说明

参考官方文档

  • insert 接口

您可使用该接口插入一行记录,但如果主键冲突(即行已存在),则插入失败。

  • get 接口

您可使用该接口取回一行记录,如果行存在,则取回行,否则返回 empty。

  • delete 接口

您可使用该接口删除一行记录,如果该行不存在,返回 affectrows = 0,否则返回具体删除的行数量(1)。

  • update 接口

您可使用该接口更新一行记录,如果目标行不存在,返回 affectrows = 0,否则返回具体更新的行数量(1)。

  • replace 接口

您可使用该接口替换一行记录,使用该接口会出现以下三种情况:

  • 目标行不存在,则插入记录。
  • 目标行已存在(即主键冲突),则删除原纪录,然后再插入这行记录。
  • 如果有唯一索引冲突,则删除所有引起冲突的行,然后再插入这行记录。
  • insertOrupdate 接口

您可使用该接口插入或修改一行记录,使用该接口会出现以下三种情况:

  • 目标行不存在,则插入记录。
  • 目标行已存在(即主键冲突),则更新这行记录。
  • 如果插入有唯一索引冲突,则也执行更新操作。
  • increment 接口

您可使用该接口把指定列的值原子地增加某个增量值(可以为负数),使用该接口会出现以下三种情况:

  • 计算结果溢出列类型的值域,报错。
  • 目标行不存在,则插入,把增量值设置为初值(即等价于原值为 0)。
  • 目标行存在,但是指定列的值为 NULL,把增量值设置为初值(即等价于原值为 0)。
  • append 接口

您可使用该接口把指定列的值原子地追加某个串,使用该接口会出现以下三种情况:

  • 计算结果溢出列类型的值域,报错。
  • 目标行不存在,则插入,把增量值设置为初值(即等价于原值为空串)。
  • 目标行存在,但是指定列的值为 NULL,把增量值设置为初值(即等价于原值为空串)。
  • scan 接口

您可使用该接口进行范围查找,支持根据主键或主键的前缀进行范围扫描,也可以不指定主键,根据索引或者索引前缀范围进行扫描查询,接口支持多 range 扫描和逆序扫描。

  • batch 接口

您可使用该接口批量执行多种操作组合。

关于OceanBase 4.0.0

  1. 目前了解到Queuing表在4.0是不支持了,4.1会采用自适应的方式来支持
  2. 看代码,4.0中增加了libtable的C/C++API。(本文使用的是Java API)

OceanBase TableAPI实践案例(Java)-8

在3.1.4的代码中看到了libobtable的客户端类型注释,但没有找到对应的实现 uint8_t client_type_; // 1: libobtable; 2: java client

class ObTableLoginRequest final
{
  OB_UNIS_VERSION(1);
public:
  uint8_t auth_method_;  // always 1 for now
  uint8_t client_type_;  // 1: libobtable; 2: java client
  uint8_t client_version_;  // always 1 for now
  uint8_t reserved1_;
  uint32_t client_capabilities_;
  uint32_t max_packet_size_;  // for stream result
  uint32_t reserved2_;  // always 0 for now
  uint64_t reserved3_;  // always 0 for now
  ObString tenant_name_;
  ObString user_name_;
  ObString pass_secret_;
  ObString pass_scramble_;  // 20 bytes random string
  ObString database_name_;
  int64_t ttl_us_;  // 0 means no TTL

总结

TableAPI给用户提供了更多的选择,对OceanBase周边的应用扩展和基于TableAPI的增值产品开发提供了更多的可能行。比如,蚂蚁集团内部的时序数据库 CeresDB 就是这么做的。也可以利用TableAPI性能的优势实现异构数据库的数据迁移。另外,因为TableAPI是基于RPC接口,因此可以扩展到更多的语言,处理本文的obkv-table-client-java Java语言客户端外,目前社区里面还提供了obkv-table-client-rs 基于rust语言的客户端。

写这篇文章之前检索了一下关于TableAPI的文章,确实比较少。一篇是竹翁(杨志丰)老师的,另外就是官网文档,但都对实践操作描述较少,因此这篇文章主要从实操的角度介绍TableAPI使用,抛砖引玉,希望感兴趣的小伙伴利用TableAPI实现更多的扩展应用。

参考

  1. 官方文档
  2. 开源数据库OceanBase源码解读(九):tableAPI和OB多模型
  3. OceanBase 4.0 我回来给你点个赞

相关文章

Oracle如何使用授予和撤销权限的语法和示例
Awesome Project: 探索 MatrixOrigin 云原生分布式数据库
下载丨66页PDF,云和恩墨技术通讯(2024年7月刊)
社区版oceanbase安装
Oracle 导出CSV工具-sqluldr2
ETL数据集成丨快速将MySQL数据迁移至Doris数据库

发布评论