摘要: 本文是面向初次接触OceanBase数据库的开发和运维人员,介绍OceanBase数据库的直观特点(所以没有高大上的理论和复杂的技术细节)。然后再以一个实际问题为引子,逐步展现OceanBase数据库的独特魅力。
目录
1 简介
2 开发视角看OceanBase
3 运维视角看OceanBase
4 再说分区Partition
5 数据拆分讨论
6 OceanBase部署案例介绍
7 其他
8 总结
1 简介本文是面向初次接触OceanBase数据库的开发和运维人员介绍OceanBase数据库的直观特点所以没有高大上的理论和复杂的技术细节。然后再以一个实际问题为引子逐步展现OceanBase数据库的独特魅力。稍微注意的是为了便于理解前面介绍的特点会偏简化后面在深入展开的时候会变复杂有些地方会改变前面的结论。实际上这也是OceanBase数据库在逐步发展过程中的特点。
2 开发视角看OceanBase我们假设开发人员已经接触过其他数据库如MySQL、SQL Server或者Oracle。公司如果有运维团队的话开发人员需要数据库只需要跟运维人员提个需求即可。
2.1 申请一个OceanBase数据库
开发申请数据库时需要说明业务需求。业务需求指的是预计要多大的空间、有多大的访问量、对数据库SQL响应时间要求多少等。
申请通过后开发会从运维拿到的OceanBase数据库信息是一个连接信息跟传统数据库一样这个信息包含数据库IP或者域名、端口、用户名和密码。如下
mysql -A –h10.100.1.100 –P3306 –uroot@app_tenant#obdev -pXXXXXXX
OceanBase的连接信息跟MySQL很像因为这个连接的协议就是兼容mysql的。用户可以通过MySQL客户端Java客户端Connector/J和通用客户端Connector/ODBC连接OceanBase。这么做是考虑到MySQL的普及性让更多人和应用能接受OceanBase。其中唯一有点不一样的是用户名的格式。如root@app_tenant#obdev 它的格式定义是用户名@租户名#OB集群名。用户名还支持另外一种格式OB集群名:租户名:用户名 。
2.2 租户和实例
租户tenant是OceanBase特有的概念等同于一个实例。开发拿到租户后往往不知道租户在哪台机器上。实际上租户存在某几台机器上但是不是固定的。并且也不一定独占所在机器。这些都是运维细节开发可以不关心。开发只需要理解如果把全部的机器当作一个资源池的话租户只是它的一个子集。这也是租户的本意。
上面给的数据库连接信息就是一个租户的连接信息开发人员可以当这个租户是一个独立的MySQL实例不同的是租户没有独立的进程。可以在租户里运行一些MySQL命令。如
$ mysql -h10.100.1.100 -P3306 -uroot@app_tenant#obdev -p******** app_db –A
create database app_db;
create user app_user identified by "app123";
grant all privileges on app_db.* to 'app_user';
show grants for app_user;
MySQL [app_db]> show databases;
--------------------
| Database |
--------------------
| oceanbase |
| information_schema |
| mysql |
| test |
| app_db |
--------------------
5 rows in set (0.02 sec)
创建一个tablegroup表分组后面用。
MySQL [app_db]> create tablegroup tg_sbtest;
Query OK, 0 rows affected (0.02 sec)
重新以用户 app_user 登录租户
$ mysql -h10.100.1.100 -uapp_user@app_tenant#obdev -P3306 -papp123 app_db
MySQL [app_db]> show databases;
--------------------
| Database |
--------------------
| information_schema |
| app_db |
--------------------
2 rows in set (0.00 sec)
MySQL [app_db]> show tables;
------------------
| Tables_in_app_db |
------------------
| sbtest1 |
| sbtest2 |
| sbtest3 |
| sbtest4 |
| sbtest5 |
| sbtest6 |
| sbtest7 |
| sbtest8 |
------------------
8 rows in set (0.02 sec)
MySQL [app_db]> show create table sbtest1\G
*************************** 1. row ***************************
Table: sbtest1
Create Table: CREATE TABLE `sbtest1` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`k` int(11) NOT NULL DEFAULT '0',
`c` char(120) NOT NULL DEFAULT '',
`pad` char(60) NOT NULL DEFAULT '',
PRIMARY KEY (`id`),
KEY `k_1` (`k`) BLOCK_SIZE 16384
) AUTO_INCREMENT = 1000001 DEFAULT CHARSET = utf8mb4 COMPRESSION = 'lz4_1.0' REPLICA_NUM = 3 BLOCK_SIZE = 16384 USE_BLOOM_FILTER = FALSE TABLET_SIZE = 134217728 PCTFREE = 10
1 row in set (0.01 sec)
MySQL [app_db]>
所以目前可以当租户是一个MySQL实例。未来OceanBase还会支持Oracle实例模式。
2.3 分布式和表分组
OceanBase是一个分布式数据库但目前开发还看不出分布式体现在哪里。这个细节需要从运维视角去看后面会详细描述。这里简单的描述一下当表很多的时候分布式数据库肯定会将这些表分散存储在多个节点上。而当SQL涉及多个表连接的时候如果跨节点了对SQL的性能会有些影响。
大部分分布式系统在面临跨节点请求时性能都会有些下降。产品本身会做优化这个不说。开发在设计表的时候可以避免这个情况发生。这就是表分组的作用。
表分组是表的一个属性用于做亲和性设置。当多个表设置为同一个表分组时OceanBase在设计这些表存储分布时会尽可能的将它们分配在同一台物理机上以达到减少跨节点请求的场景。
前面的例子里创建的 tg_sbtest 就是一个tablegroup。下面设置表的tablegroup。
MySQL [app_db]> alter table sbtest1 tablegroup='tg_sbtest';
Query OK, 0 rows affected (0.04 sec)
MySQL [app_db]> alter table sbtest2 tablegroup='tg_sbtest';
Query OK, 0 rows affected (0.04 sec)
MySQL [app_db]> alter table sbtest3 tablegroup='tg_sbtest';
Query OK, 0 rows affected (0.03 sec)
MySQL [app_db]> alter table sbtest4 tablegroup='tg_sbtest';
Query OK, 0 rows affected (0.04 sec)
MySQL [app_db]> show tablegroups;
----------------- ---------------------------- ---------------
| Tablegroup_name | Table_name | Database_name |
----------------- ---------------------------- ---------------
| oceanbase | NULL | NULL |
| tg_sbtest | sbtest1 | app_db |
| tg_sbtest | sbtest2 | app_db |
| tg_sbtest | sbtest3 | app_db |
| tg_sbtest | sbtest4 | app_db |
| tg_sbtest | __idx_1131397465031505_k_1 | app_db |
| tg_sbtest | __idx_1131397465031507_k_2 | app_db |
| tg_sbtest | __idx_1131397465031509_k_3 | app_db |
| tg_sbtest | __idx_1131397465031511_k_4 | app_db |
----------------- ---------------------------- ---------------
9 rows in set (0.01 sec)
表分组只是OceanBase提供的一个用于干预分布式存储细节的手段。其原理后面在运维人员视角里会详细阐述。实际上OceanBase还有其他类似的干预手段区别只在于干预的范围。比如说有设置还可以控制几个租户的所有分区的位置分布。
OceanBase目标是做一个通用的数据库然而数据库性能好不好除了跟数据库产品自身的能力有关外还跟应用的设计能力有关。应用和数据库需要互相配合开发和运维也需要配合。这样应用性能才好。
2.4 表、分区和分区组
OceanBase的表支持分区表分区策略支持RangeHash等。一个分区表有多个分区一个非分区表可以认为是只有一个分区。
在应用层面表是存储业务数据的不可分割的最小单元。在OceanBase层面分区Partition才是存储业务数据的不可分割的最小单元。所以一个分区是不能跨机器存储一个表的不同分区是可以跨机器存储的。当然如果分区表有二级分区这里说的分区就是指那个二级分区。所以当一个业务大表的数据大小远超出单台机器的磁盘容量时OceanBase可以通过分区的方式解决这个空间难题。
上面表分组说会将不同表尽可能的组织在一起存储但是当这些表很大单机放不下的时候显然不合适。所以大表会做成分区表这个表分组TableGroup会细化为多个分区组PartitionGroup。不同分区表设置为同一个表分组时其对应的分区就属于同一个分区组。当然前提是二者的分区策略和分区数目保持一致。如果有人说有些业务表就是没办法按相同策略分区这个问题的处理还是要顶层设计上解决就不在这里展开了。这里说的是OceanBase提供这种能力。
分区更大的意义在运维和调优它也是负载均衡和高可用的最小单元。后面会详细解释。
2.5 SQL兼容性和执行计划
OceanBase SQL基本兼容MySQL语法部分兼容Oracle SQL语法。目前1.x版本对于函数、游标和存储过程之类都还不支持2.x版本会逐步支持。
OceanBase的SQL解析执行是仿照Oracle的设计去做有Plan cache执行计划是基于Cost。跟SQL和执行计划相关的v$视图OceanBase也有。这些设计奠定了OceanBase的SQL性能水平。
图 OceanBase SQL执行路径
开发可以使用explain命令查看SQL的执行计划。OceanBase SQL执行计划支持全表扫描、索引扫描表连接算法支持Nested-Loop、Merge-Join、Hash-Join等算法。
MySQL [app_db]> explain select a.*, b.* from sbtest1 a join sbtest2 b on (a.id=b.k) where a.k = 5000\G
*************************** 1. row ***************************
Query Plan: =================================================
|ID|OPERATOR |NAME |EST. ROWS|COST|
-------------------------------------------------
|0 |NESTED-LOOP INNER JOIN| |103 |9591|
|1 | TABLE SCAN |a(k_1)|101 |612 |
|2 | TABLE SCAN |b(k_2)|2 |88 |
=================================================
Outputs & filters:
-------------------------------------
0 - output([a.id], [a.k], [a.c], [a.pad], [b.id], [b.k], [b.c], [b.pad]), filter(nil),
conds(nil), nl_params_([a.id])
1 - output([a.id], [a.k], [a.c], [a.pad]), filter(nil),
access([a.id], [a.k], [a.c], [a.pad]), partitions(p0)
2 - output([b.k], [b.id], [b.c], [b.pad]), filter(nil),
access([b.k], [b.id], [b.c], [b.pad]), partitions(p0)
1 row in set (0.01 sec)
MySQL [app_db]> explain select a.*, b.*
-> from sbtest1 a join sbtest2 b on (a.id=b.k)\G
*************************** 1. row ***************************
Query Plan: ============================================
|ID|OPERATOR |NAME |EST. ROWS|COST |
--------------------------------------------
|0 |MERGE INNER JOIN| |10123 |73368|
|1 | TABLE SCAN |a |10000 |6444 |
|2 | TABLE SCAN |b(k_2)|10000 |57312|
============================================
Outputs & filters:
-------------------------------------
0 - output([a.id], [a.k], [a.c], [a.pad], [b.id], [b.k], [b.c], [b.pad]), filter(nil),
equal_conds([a.id = b.k]), other_conds(nil)
1 - output([a.id], [a.k], [a.c], [a.pad]), filter(nil),
access([a.id], [a.k], [a.c], [a.pad]), partitions(p0)
2 - output([b.k], [b.id], [b.c], [b.pad]), filter(nil),
access([b.k], [b.id], [b.c], [b.pad]), partitions(p0)
1 row in set (0.02 sec)
MySQL [app_db]> explain select a.*, b.* from sbtest1 a join sbtest2 b on (a.k=b.k)\G
*************************** 1. row ***************************
Query Plan: ==========================================
|ID|OPERATOR |NAME|EST. ROWS|COST |
------------------------------------------
|0 |HASH INNER JOIN| |56370 |108982|
|1 | TABLE SCAN |a |10000 |6444 |
|2 | TABLE SCAN |b |10000 |6444 |
==========================================
Outputs & filters:
-------------------------------------
0 - output([a.id], [a.k], [a.c], [a.pad], [b.id], [b.k], [b.c], [b.pad]), filter(nil),
equal_conds([a.k = b.k]), other_conds(nil)
1 - output([a.k], [a.id], [a.c], [a.pad]), filter(nil),
access([a.k], [a.id], [a.c], [a.pad]), partitions(p0)
2 - output([b.k], [b.id], [b.c], [b.pad]), filter(nil),
access([b.k], [b.id], [b.c], [b.pad]), partitions(p0)
1 row in set (0.01 sec)
对于子查询、半连接、Union等OceanBase都有相应的优化策略这里就不展开了。
2.6 总结
描述计算机资源通常有三个指标CPU、内存和空间。每个机器包含了一定的资源一堆机器组成一个很大的资源池。OceanBase数据库就是管理这批资源池的。怎么管理资源是运维细节开发人员需要考虑的是新的应用需要多少资源运维人员就提供有相应资源的租户给应用。不同的应用使用不同的租户。租户内部的数据库、表、分区都可以理解为资源使用的粒度。不同的租户资源彼此隔离。如果大家了解云计算的话就会发现OceanBase天然就适合云计算业务。
下图就是开发人员理解的OceanBase数据库的细节。
图 开发视角的租户和数据库对象
租户就是一个实例在租户里开发可以建库建表每个表至少包含一个Partition。从图中看不出表和Partition具体在哪些机器上。如果要深入了解这个细节就从运维人员视角去看OceanBase。
3 运维视角看OceanBaseOceanBase数据库是一个集群至少需要三台机器。这个集群不需要共享存储机器之间也不需要网络直连彼此是通过网络访问即可。为了降低跨节点之间的请求延时对性能的影响OceanBase机器也建议用万兆网卡。相应的交换机也要支持万兆。
3.1 observer和机器
机器的内存建议在256G以上大内存。OceanBase的数据主要缓存在内存里数据不经常落盘所以内存越大越好。磁盘建议用SSD普通的即可。后面会解释SSD的特点跟OceanBase内部读写设计理念非常契合。容量也不要太小。网卡上面解释了尽量用万兆网卡。
OceanBase数据库在每个机器上的角色就是OBServer。它只有一个进程就是 observer。当observer进程启动的时候它会把绝大部分内存和大部分存储空间都独占住CPU无法独占只要没有其他进程跟它抢CPU资源可以理解为也是observer所有。所以OBServer的作用简单说就是占住机器资源然后提供数据库服务。注这是生产环境建议行为实际上OBServer对内存和空间的使用是可以配置的可以在进程启动参数里或者参数文件里配置。在开发测试环境允许单台机器跑多个OBServer进程。
图 OBServer相关进程和文件
OBServer提供SQL计算和数据存储两种能力并支持集群部署分布式访问。OBServer内部主要包含总控服务、SQL引擎和存储引擎。其中总控服务是可选的只存在于部分OBServer中。存储引擎就是管理分区的。observer进程运行会输出两个日志文件observer.log和observer.log.wf后者是前者的子集主要是warning级别以上的日志。如果包含RootService则还会输出rootservice.log和rootservice.log.wf 。日志文件按大小滚动输出。稍微不足的是目前日志文件格式和内容是面向OceanBase内核开发的对普通用户不是特别友好。这个后期会有改进。但对喜欢深入研究OceanBase数据库的技术人员这个日志还是能看出一些OceanBase内部运行机制。
OceanBase数据文件是一个大文件其格式内部称为SSTable。此外有Commit Log、Index Log和Storage Log文件。
3.2 OceanBase集群设计
OBServer支持集群化部署最少三台OBServer就可以组建一个OceanBase集群。这个OceanBase集群具有可扩展、自动故障切换Failover并且数据不丢的特性。用容灾的两个常用指标来说就是RPO为0RTO为20s左右。