本文主要是为OB开发和测试人员提供一些入门建议,分为上中下三篇。本文比较基础,适合初学者。在使用OB之前阅读,可以起到事半功倍的效果。希望能有帮助,欢迎留言讨论。
数据库都支持TCP连接,数据库连接至少要提供一个IP、端口、用户名和密码。数据库默认连接端口各不相同。比如说oracle是1521,mysql是3306。这些都可以通过配置改变,取决于DBA的考虑。
当那个IP的Port上注册有多个数据库实例或者数据库服务的时候,比如说oracle,还需要提供一个sid或服务名才能准确连接。oracle数据库实例里会有多个模式(schema),对应于多个用户,有些用户的权限支持跨schema访问。在mysql数据库实例了会有多个databases,只要用户有权限访问就行。所以连接还需要指定默认schema或者数据库。下面就是一个完整的mysql数据库的连接命令行:
mysql-h192.168.1.100-P3306-uuser01-p'passwd01'testOB通常以集群形式运行,至少3台机器,每个机器上启动一个observer进程,进程的连接端口是2881.正常情况下,连接集群的任何一个节点的2881端口,都能跟这个集群通信。但通常不建议这么做。因为如果该节点故障了,客户端还得换IP,不是很方便。OB集群会提供一个类似oracle监听的反向代理软件obproxy,监听端口是2883。obproxy可以运行在任何位置。比如说OB机器节点、应用服务器或者其他可靠的虚拟机上。obproxy就是一个传送门,通向一个OB集群。当然也有办法通向多个OB集群,这取决于obproxy的启动参数配置。你可以在任意位置开启这个传送门:)所以通常只需要记录obproxy的地址和监听端口(2883)即可,obproxy会把SQL转发到OB集群里某台节点上。这个功能叫路由。在分布式数据库或者分布式架构的产品里,路由是很重要的事情,关乎性能。
OB以集群形式运行,提供多实例服务。连接到OB的时候,连接的是集群中的某个实例。OB集群在诞生的时候,会有个内部实例,名字叫sys。每个实例都拥有一部分资源(CPU和内存),资源是实例提供数据库读写服务的基础。sys只会占用OB集群资源的少数资源,剩余的都是待分配资源。OB集群可以分配出多个实例,实例选择兼容ORACLE或者MySQL。在ORACLE实例里,也有一个默认用户叫sys。这里后面沟通的时候,注意区分是实例sys还是ORACLE实例里的用户名sys。
所以,连接OB的时候,需要提供:
OBProxy可以为多个OB集群提供连接服务,每个OB的集群里有多实例,OB的连接为了跟传统的连接习惯一致,并没有增加参数表示连接哪个集群和哪个租户(实例),而是在用户名里放入了集群名和租户名。
前面提到连接OB数据库的时候,实际是连接到具体的OB实例下的默认schema或者db。如果该实例是兼容MySQL的,则可以使用mysql客户端命令连接。OB的MySQL租户基本兼容MySQL5.6/5.7的连接协议,但不兼容MySQL8.0的连接协议(以后版本可能会修复)。所以MySQL8.0及其以后版本的客户端连接OB的MySQL实例会提示认证失败类错误(实际密码没有错误)。有兴趣的朋友可以通过tcpdump去分析不同版本MySQL连接协议的不同。
所以集群管理员连接OB集群(sys实例,兼容MySQL)的命令格式示例如下:
mysql-h192.168.1.100-P2883-uroot@sys#obtest-p'y6k%N_zwVzOzfW3@'oceanbase-c-A连接时建议习惯性的加上-c-A这两个参数。这样命令行客户端就不会把SQLHint当成注释忽略掉了。以及当数据看下很多表时,不要表名自动补全功能(有代价)。
OB的工具产品很多,有些名字可能容易引起混淆。当说obclient的时候,通常就是指这个客户端命令。obclient可以连接OB的ORACLE和MySQL实例。
示例如下:
#连接sys实例obclient-h192.168.1.100-P2883-uroot@sys#obtest-p'xxxxxxxxxxx'oceanbase-c-A#连接oracle实例的sys用户obclient-h192.168.1.100-P2883-usys@bmsql#obtest-p'xxxxxxxxxxx'sys-c#连接oracle实例的tpcc用户obclient-h192.168.1.100-P2883-usys@bmsql#obtest-p'xxxxxxxxxxx'tpcc-cobclient-h192.168.1.100-P2883-utpcc@bmsql#obtest-p'123456'tpcc-c以上是标准的连接,即通过OBProxy连接OB集群。有时候,你会看到直连OB节点的方法,即连接具体的OBServer的节点,端口2881。用户名里只需要提供租户名和用户名。格式也有两种:租户名:用户名或用户名@租户名。示例如下,只是为了便于理解,并不建议采取下列方式连接。因为可能某些时候会有问题。#连接sys实例obclient-h192.168.1.101-P2881-uroot@sys-p'xxxxxxxxxxx'oceanbase-c-A#连接oracle实例的sys用户obclient-h192.168.1.101-P2881-usys@bmsql-p'xxxxxxxxxxx'sys-c#连接oracle实例的tpcc用户obclient-h192.168.1.101-P2881-usys@bmsql-p'xxxxxxxxxxx'tpcc-cobclient-h192.168.1.101-P2881-utpcc@bmsql-p'123456'tpcc-c探索OB探索用户权限用户权限的大小,会影响解决问题的能力大小。
所以拿到连接信息的第一步是判断自己用户的权限。从用户名中的租户名可以判断是哪个实例,从租户的用户名运行下面语句可以判断自己的权限。
如果权限是ALLPRIVILEGESON*.*则表示这个用户是个管理员权限,那很后面看一些信息就方便多了。如果不是,可以找运维人员要一个能看实例(包括sys实例)所有信息的只读账户。比如说
obclient>grantselecton*.*toguest;QueryOK,0rowsaffected(0.04sec)obclient>showgrantsforguest;+--------------------------------+|GrantsforGUEST@%|+--------------------------------+|GRANTSELECTON*.*TO'GUEST'|+--------------------------------+1rowinset(0.02sec)obclient>
实例资源的大小,会限制实例的性能。
OB以集群形式运行,提供给业务服务的是实例。实例拥有的资源只是集群机器资源的一部分。所以机器好并不代表某个具体业务实例的性能就一定好。具体要看运维给这个业务实例分配了多少资源(多少CPU和内存)。
在OB的MySQL实例下可以查看视图gv$unit获取实例的资源信息。不过在那之前,得先确认一下当前实例的兼容类型。(业务实例的命名不一定像我的例子命名那样直白)。
obclient>showvariableslike'ob_compatibility_mode';+-----------------------+-------+|Variable_name|Value|+-----------------------+-------+|ob_compatibility_mode|MYSQL|+-----------------------+-------+1rowinset(0.01sec)--查看当前租户的资源SELECTtenant_name,svr_ip,unit_Id,unit_config_name,resource_pool_name,max_cpu,min_cpu,round(max_memory/1024/1024/1024,2)max_mem_gb,round(min_memory/1024/1024/1024,2)min_mem_gbFROMoceanbase.gv$unit;
租户的资源主要是CPU和内存。CPU对应租户内部的工作线程数(worker),数量默认是CPU的10倍。高并发时,CPU越多,worker数量就越多,自然总体能力就越高。内存的重要性可能比CPU更高。OB的数据库在高并发读写时,在CPU不少的情况下,往往内存更容易先到达瓶颈,其次才可能是磁盘IO。
如上图租户有12个CPU和40G内存。则内部会有120个worker。其中默认30%的worker资源会用于大查询。大查询的标准由sys租户的参数指定。这个后面再说。OB的读写在内存里是分离的。40G内存默认情况有一半用于存放增量数据(memstore),剩下的放读入的基线数据。如果写的比例比较高,增量内存不大的情况下,增量内存很快就会用尽,进而触发OB的合并或者转储操作。合并是OB内存中的增量数据和磁盘上的全量数据会在内存中合并生成新的数据版本再写回到磁盘上,这个会抢占一部分CPU、内存和IO资源,影响测试性能。转储时OB内存中的增量数据直接以sstable格式写入到磁盘数据文件中,对性能影响比较小。所以实际情况都会开启转储。合并和转储的力度都是可以通过参数调优的(以后再深入介绍)。
数据初始化有2种途径。一是把原来的数据库数据导入到OB,一种是应用程序重新造数据。
导数通常是离线同步,首先推荐用datax产品。datax是alibaba开源的开放的数据交换框架,能在不同数据库源之间同步数据。比如说oracle、mysql、sqlserver、db2、csv、hadoop等。在文档《OceanBaseORACLE租户开发者指南》里有详细介绍过datax的使用方法。datax的最大的不方便就是按表配置同步,所以如果熟悉了配置文件的写法,可以使用shell脚本批量生成同步配置文件。
如果源数据已经是文件了,导入到OB可以使用OB的loaddata或者datax。如果文件非常大的话,建议使用linux命令splitfile将大文件分割,然后利用datax的并行功能导入文件。这样效率最高。
当然,导数性能越高时,就意味着对OB内存写入速度越高。就可能有上面内存瓶颈的问题。一方面OB租户的内存尽量多分配一些,另外一方面OB集群层面设置一些转储参数,加速OB的转储性能,最终这个会达到一个平衡。
在datax的导数设置里,有一个batchsize的选项,控制事务的大小。通常建议设置为1000。datax会用jdbc的batchinsert功能,生成一个insertvalues后跟1000个记录的值。单笔事务大小就是1000。MySQL是支持这种写法,ORACLE并不支持,不过OB的ORACLE实例支持这种批量insert写法,可以降低网络请求次数,提升导数效率。
造数据性能的关键就在于事务的大小和日志盘的性能。这点跟传统数据库原理相同。事务太小,提交太频繁;事务太大,太耗数据库某种资源可能引发其他异常(在ORACLE里是UNDO,在OB里是内存)。所以批量写入是最佳建议。批量的大小可以再几千或几万之间尝试。
OB是通用的分布式数据库软件,功能非常丰富,并有相应的异常处理逻辑。下面仅介绍初学者常碰到的异常。
合并的另外一个触发条件就是内存使用达到某个阈值就进行,为了避免白天业务高峰期发生合并,OB还可以通过转储操作释放增量内存。转储就是把内存中增量数据直接写到数据文件中临时存放,后面在需要的时候再读回来。转储对性能的影响可以控制在2%以内。转储也有很多参数,在稳定性和速度方面取一个平衡,并且转储可以有多轮,比较类似传统数据库的checkpoint。
调大超时参数只是解决了报错问题,并没有解决性能问题。大表的统计查询建议加上SQLHint。如,
select/*+parallel(32)query_timeout(1000000000)*/count(*)frombig_table;并行度可以从16开始,32,64都尝试一下,以感受提高并行度对性能的提升。不过,也不是越高就越快。SQL里指定并行度之后,SQL有并发执行的时候,也要考虑OB服务端能提供多少个并行会话去处理这个。这个后面会再深入介绍。
在使用OB开发时,可能还会碰到连接断开的报错。其中有一种报错信息是:ERROR-02013:LostconnectiontoMySQLserverduringquery或者ERROR-02006:MySQLserverhasgoneaway。
这个通常有两种可能。一是OB集群正常但是obproxy进程挂了或者重启了。二是obproxy正常,客户端会话事务空闲超时了。无论是哪种,只要客户端有重连时,重试查询都会恢复。
一旦报错后,数据库会自动回滚掉事务(释放锁),但是事务状态会维持在“超时状态”,此时需要客户端发起一个commit或rollback命令才能清除掉超时状态。通常为了便于理解,发起rollback命令比较合适。
总结一下上面三个超时机制。都是由实例(租户)变量控制。
查看超时变量值方法如下。
obclient>showvariableswherevariable_namein('ob_query_timeout','ob_trx_idle_timeout','ob_trx_timeout','ob_trx_lock_timeout');+---------------------+-----------+|VARIABLE_NAME|VALUE|+---------------------+-----------+|ob_query_timeout|10000000||ob_trx_idle_timeout|120000000|
|ob_trx_lock_timeout|-1|
|ob_trx_timeout|100000000|+---------------------+-----------+3rowsinset(0.00sec)
四个超时变量各自生效机制是独立的。这些是分布式数据库OB的自我保护机制。跟传统数据库使用习惯有很大出入。初期的时候,DBA可以在实例全局层面调大这些参数,以避免开发人员使用过程中频繁报错。等熟悉OB特点后,再根据业务特点为这些参数设置一些合理的超时值。
开发也可以尝试修改这些变量值。如果账户没有权限修改全局层面的设置,那就修改会话级别的设置。也可以在语句级别设置。
obclient>setglobalob_query_timeout=1000000000;ERROR-00600:internalerrorcode,arguments:-5036,Accessdenied;youneed(atleastoneof)theSUPERprivilege(s)forthisoperationobclient>obclient>setsessionob_query_timeout=1000000000;QueryOK,0rowsaffected(0.00sec)obclient>showvariableslike'ob_query_timeout';+------------------+------------+|VARIABLE_NAME|VALUE|+------------------+------------+|ob_query_timeout|1000000000|+------------------+------------+1rowinset(0.00sec)obclient>update/*+query_timeout(300000000)*/t1setid=id+10;QueryOK,2rowsaffected(0.00sec)Rowsmatched:2Changed:2Warnings:0obclient>obclient>select/*+query_timeout(30000000)*/count(*)fromt1;+----------+|COUNT(*)|+----------+|2|+----------+1rowinset(0.00sec)