在企业数据库里有一种需求是读写分离,本文介绍 OB 的读写分离方案的部署和测试过程,希望可以减少分布式数据库选型时不必要的基本功能测试。读写分离听起来简单,实际内部设计还是有很多巧妙之处,刚学习完 OBCP 的 朋友们不妨也看看。
本文测试内容如下,有相应截图:
- OCP 的安装部署
- OB 集群部署
- OB 只读副本部署及其只读访问测试
- OB 只读副本和全功能副本的在线转换
- OB 单副本备集群部署及其只读访问测试
机器准备
硬件资源
机器全部使用公有云 ECS,配置是 32c128G2T。
[root@observer00:/root/t-oceanbase-antman]# lscpu Architecture: x86_64 CPU op-mode(s): 32-bit, 64-bit Byte Order: Little Endian CPU(s): 32 On-line CPU(s) list: 0-31 Thread(s) per core: 2 Core(s) per socket: 16 Socket(s): 1 NUMA node(s): 1 Vendor ID: GenuineIntel <...> [root@observer00:/root/t-oceanbase-antman]# free -g total used free shared buff/cache available Mem: 125 1 104 0 19 118 Swap: 0 0 0 [root@observer00:/root/t-oceanbase-antman]# df -h Filesystem Size Used Avail Use% Mounted on /dev/vda1 50G 42G 5.1G 90% / devtmpfs 63G 0 63G 0% /dev tmpfs 63G 0 63G 0% /dev/shm tmpfs 63G 820K 63G 1% /run tmpfs 63G 0 63G 0% /sys/fs/cgroup /dev/mapper/ob_vg-ocp_home 296G 2.0G 279G 1% /home /dev/mapper/ob_vg-docker_home 493G 73M 467G 1% /docker /dev/mapper/ob_vg-ob_data 2.2T 85M 2.1T 1% /data/1 /dev/mapper/ob_vg-ob_log 504G 73M 479G 1% /data/log1 [root@observer00:/root/t-oceanbase-antman]#
机器初始化
机器初始化是很重要的一步,使用 OceanBase 提供的软件包t-oceanbase-antman-1.3.6-1917679.alios7.x86_64.rpm
可以做一些自动化初始工作。
这部分不再重复描述。详情请参考 https://mp.weixin.qq.com/s/Q-4ksIRFWYa4xbl4Yccw1g
以前的初始化步骤。
OCP 部署
ocp 的部署可以先参考 OceanBase 2.x 试用版安装体验——OCP 2.3 。
镜像文件准备
一共三个 docker 镜像文件,分别是 OBProxy、OCP、OceanBase。
[root@observer00:/root/t-oceanbase-antman]# ls -lrth *.gz -rw-r--r-- 1 126593 users 404M Jan 3 22:18 obproxy173.tar.gz -rw-r--r-- 1 root root 688M Jan 17 14:02 ocp-all-in-one-2.5.0-x86.tar.gz -rw-r--r-- 1 root root 1.4G Jan 17 14:09 OB2273_x86_20201214.tar.gz [root@observer00:/root/t-oceanbase-antman]#
加载镜像文件
[root@observer00:/root/t-oceanbase-antman]# for img in `ls *.gz`;do echo $img; docker load -i $img; done OB2273_x86_20201214.tar.gz <...> Loaded image: reg.docker.alibaba-inc.com/antman/ob-docker:OB2273_x86_20201214 obproxy173.tar.gz <...> Loaded image: reg.docker.alibaba-inc.com/antman/obproxy:OBP173_20200603_1923 ocp-all-in-one-2.5.0-x86.tar.gz <...> Loaded image: reg.docker.alibaba-inc.com/oceanbase/ocp-all-in-one:2.5.0-1918031
查看镜像文件。不同版本的镜像文件在版本号上会有一点区别。注意看 tag 标签。镜像文件命名并不重要。实际版本以 tag 为准。
[root@observer00:/root/t-oceanbase-antman]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE reg.docker.alibaba-inc.com/oceanbase/ocp-all-in-one 2.5.0-1918031 2e635ca841b8 2 weeks ago 1.55GB reg.docker.alibaba-inc.com/antman/ob-docker OB2273_x86_20201214 4da7b6c59465 5 weeks ago 2.89GB reg.docker.alibaba-inc.com/antman/obproxy OBP173_20200603_1923 781b6520e237 7 months ago 1.15GB [root@observer00:/root/t-oceanbase-antman]#
OCP部署配置文件 obcluster.conf
下面直接给出关键的几个配置.
第一次跑的时候在 /root/t-antman-oceanbase/ 下没有这个目录。执行脚本初始化一个
[root@observer00:/root/t-oceanbase-antman]#sh init_obcluster_conf.sh [root@observer00:/root/t-oceanbase-antman]# vim obcluster.conf ## obcluster.conf ## SINGLE_OCP_MODE=TRUE ################################ 根据环境必须修改 / MUST CHANGE ACCORDING ENVIRONMENT ################################ ############ 填写机器IP和root/admin密码 / Edit Machine IP and Password Of root/admin ############ ZONE1_RS_IP=172.23.152.220 OBSERVER01_ROOTPASS=******* OBSERVER01_ADMINPASS=****** SSH_PORT=22 ############ 根据服务器CPU、内存设置容器资源编排 / Allocate Container Resources According To Server ############ OB_docker_cpus=20 OB_docker_memory=92G OCP_docker_cpus=6 OCP_docker_memory=16G OBProxy_docker_cpus=2 OBProxy_docker_memory=6G ############ 填写OCP各组件容器的版本信息 / Edit Docker Image, Repo And Tag of OCP Components ############ # OB docker docker_image_package=OB2273_x86_20201214.tar.gz OB_image_REPO=reg.docker.alibaba-inc.com/antman/ob-docker OB_image_TAG=OB2273_x86_20201214 # OCP docker ocp_docker_image_package=ocp-all-in-one-2.5.0-x86.tar.gz OCP_image_REPO=reg.docker.alibaba-inc.com/oceanbase/ocp-all-in-one OCP_image_TAG=2.5.0-1918031 # OBPROXY docker obproxy_docker_image_package=obproxy173.tar.gz obproxy_image_REPO=reg.docker.alibaba-inc.com/antman/obproxy obproxy_image_TAG=OBP173_20200603_1923
自动化安装
OCP的部署命令非常简单。只要机器环境没有问题,安装部署就是一个命令,自动过。
[root@observer00:/root/t-oceanbase-antman]# ./install.sh -i 1- run install.sh with DEBUG=FALSE, INSTALL_STEPS=1 2 3 4 5 6 7 8 CLEAR_STEPS= CONFIG_FILE=/root/t-oceanbase-antman/obcluster.conf [2021-01-23 15:51:01.148954] INFO [start antman API service] LB_MODE=none [2021-01-23 15:51:01.534776] INFO [step1: making ssh authorization, logfile: /root/t-oceanbase-antman/logs/ssh_auth.log] [2021-01-23 15:51:04.782001] INFO [step1: ssh authorization done] [2021-01-23 15:51:04.792041] INFO [step2: no action is required when LB_MODE=none] [2021-01-23 15:51:04.795448] INFO [step3: check whether OBSERVER port 2881,2882 are in use or not on 172.23.152.220] [2021-01-23 15:51:09.462947] INFO [step3: OBSERVER port 2881,2882, 2022 are idle on 172.23.152.220] [2021-01-23 15:51:09.466502] INFO [step3: installing ob cluster, logfile: /root/t-oceanbase-antman/logs/install_ob.log] [2021-01-23 15:51:35.760209] INFO [start container: docker run -d -it --name META_OB_ZONE_1 --net=host -e OBCLUSTER_NAME=obcluster -e DEV_NAME=eth0 -e ROOTSERVICE_LIST="172.23.152.220:2882:2881" -e DATAFILE_DISK_PERCENTAGE=90 -e CLUSTER_ID=100000 -e ZONE_NAME=META_OB_ZONE_1 -e OCP_VIP=172.23.152.220 -e OCP_VPORT=8080 -e METADB_CLUSTER_NAME=obcluster -e app.password_root=ro0T@20#Ra -e app.password_admin=ro0T@20#Ra -e OPTSTR="cpu_count=20,memory_limit=89G,__min_full_resource_pool_memory=1073741824,_ob_enable_prepared_statement=false,memory_limit_percentage=90" --cpu-period 100000 --cpu-quota 2000000 --cpuset-cpus 0-19 --memory 92G -v /home/admin/oceanbase:/home/admin/oceanbase -v /data/log1:/data/log1 -v /data/1:/data/1 -v /backup:/backup --restart on-failure:5 reg.docker.alibaba-inc.com/antman/ob-docker:OB2273_x86_20201214] 0dbbc671f4cbf3792067e64eade064a397f46cacabd65af63de699826c4d940c [2021-01-23 15:51:35.971611] INFO [installing OB docker and starting OB server on 172.23.152.220, pid: 19062, log: /root/t-oceanbase-antman/logs/install_OB_docker.log and /home/admin/logs/ob-server/ inside docker] [2021-01-23 15:51:39.090756] INFO [install_OB_docker.sh finished and reg.docker.alibaba-inc.com/antman/ob-docker:OB2273_x86_20201214 started on 172.23.152.220] [2021-01-23 15:51:39.094245] INFO [waiting on observer ready on 172.23.152.220] [2021-01-23 15:54:39.103957] INFO [waiting on observer ready on 172.23.152.220 for 3 Minitues] [2021-01-23 15:55:39.114452] INFO [waiting on observer ready on 172.23.152.220 for 4 Minitues] [2021-01-23 15:55:39.504591] INFO [observer on 172.23.152.220 is ready] [2021-01-23 15:55:39.514450] INFO [observer installation on all hosts done] [2021-01-23 15:55:39.521402] INFO [Now, start bootstrap on 172.23.152.220: alter system bootstrap REGION "OCP_META_REGION" ZONE "META_OB_ZONE_1" SERVER "172.23.152.220:2882"] [2021-01-23 15:56:59.700068] INFO [bootstrap done, observer now ready] [2021-01-23 15:56:59.710201] INFO [major_freeze start] [2021-01-23 15:57:59.759744] INFO [major_freeze done] [2021-01-23 15:57:59.763844] INFO [step3: installation of ob cluster done] [2021-01-23 15:57:59.767362] INFO [step4: initializing ocp metadb, logfile: /root/t-oceanbase-antman/logs/init_metadb.log] [2021-01-23 15:58:04.640267] INFO [step4: initialization of ocp metadb done] [2021-01-23 15:58:04.643938] INFO [step5: check whether OCP port 8080 is in use or not on 172.23.152.220] [2021-01-23 15:58:06.202247] INFO [step5: OCP port 8080 is idle on 172.23.152.220] [2021-01-23 15:58:06.206098] INFO [step5: installing temporary ocp, logfile: /root/t-oceanbase-antman/logs/install_tmp_ocp.log] [2021-01-23 15:58:26.476181] INFO [ob server is ready on host 172.23.152.220] [2021-01-23 15:58:26.625129] INFO [init metadb: docker run --net host --workdir=/home/admin/ocp-init/src/ocp-init --entrypoint=python reg.docker.alibaba-inc.com/oceanbase/ocp-all-in-one:2.5.0-1918031 create_metadb.py 172.23.152.220 2881 root@ocp_meta X21FRlH9{@ ocp root@ocp_monitor ^I4SH6HsU@ ocp_monitor] create_metadb.py:356: YAMLLoadWarning: calling yaml.load() without Loader=... is deprecated, as the default Loader is unsafe. Please read https://msg.pyyaml.org/load for full details. data = yaml.load(file) <...> [root@observer00:/root/t-oceanbase-antman]# [root@observer00:/root/t-oceanbase-antman]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 5333a8797e7b reg.docker.alibaba-inc.com/oceanbase/ocp-all-in-one:2.5.0-1918031 "/usr/bin/supervisor…" 7 minutes ago Up 7 minutes ocp 60bd06244095 reg.docker.alibaba-inc.com/antman/obproxy:OBP173_20200603_1923 "sh start_obproxy.sh" 8 minutes ago Up 8 minutes obproxy 0dbbc671f4cb reg.docker.alibaba-inc.com/antman/ob-docker:OB2273_x86_20201214 "/usr/bin/supervisor…" 20 minutes ago Up 20 minutes META_OB_ZONE_1 [root@observer00:/root/t-oceanbase-antman]#
如果安装报错了,就把报错的步骤 回滚掉 -c
。
OB 集群部署
部署规划
目标:
- 部署一个 1-1-1 的 OB 集群。
- 部署一个只读副本。
- 部署一个单副本备集群。
主机名IPREGIONIDCZone备注observer01172.23.152.221RG1IDC1ZONE_1OB 主副本observer02172.30.118.66RG1IDC2ZONE_2OB 备副本observer03172.30.118.67RG2IDC3ZONE_3OB 备副本observer04172.26.154.55RG2IDC22ZONE_4OB 只读副本observer05172.26.154.54RG3IDC4ZONE_1OB 备集群单副本
注意:这里的 REGION 和 IDC 信息都是特别设计的,后面可以看到效果。
做到后面发现这里的 IDC22 应该命名为 IDC33 更贴切一些。不过不好调整了。在实际生产环境里,IDC22 跟 IDC3 通常是同一个物理机房或者同一个城市,属于同一个 REGION。
机器资源池录入
- 新增机型
- 新增凭据
- 新增机房
- 添加主机
重复添加所有主机后,OCP 管理的机器资源如下:
部署 OB 集群
注意,选机器的时候,ZONE、IDC、IP 都要跟规划一致。
- 可选:指定一些 OB 集群参数。
这些参数是常用的,性能测试时会有用。不是必须的。有关参数介绍请参考:OB 开发测试建议 (中)
提交后生成安装任务。
OceanBase 集群的自动化安装任务相比手动安装是更严谨一些,这符合企业级数据库运维平台的基本要求。子任务切分很细,并且有很多环境检查准备等工作。OCP 提供了图形化的进度浏览。多个节点能同时安装。
OceanBase 集群的安装时间绝大部分都是这一步任务的时间。这一步是为了测试主机上 IO 的综合能力,然后生成一个文件 `/home/admin/oceanbase/etc/io_resource.conf 。
[admin@observer01:/home/admin]# ps -ef|grep ob_admin admin 7002 6963 99 17:47 ? 00:08:40 /home/admin/oceanbase/bin/ob_admin io_bench -c /home/admin/oceanbase/etc -d /data/1/obdemo admin 10848 5779 0 17:49 pts/0 00:00:00 grep --color=auto ob_admin [admin@observer01:/home/admin]#
部署 OBProxy 集群
给业务部署一个 OBProxy,用于业务读写。由于测试环境我没有 VIP,所以这里集群的 IP 填写了第一个后端 OBProxy 的 IP。
创建 OB 租户
新建 OB 的 ORALCE 租户
OCP 可以创建租户,选择兼容 MySQL 或者 ORACLE。这个细节就不重复了。
初始化 ORACLE 租户数据
这里用 BenchmarkSQL 工具初始化 TPCC 场景数据。
vim props.ob db=oceanbase driver=com.alipay.oceanbase.obproxy.mysql.jdbc.Driver conn=jdbc:oceanbase://172.30.118.66:2883/tpcc?useAffectedRows=true&emulateUnsupportedPstmts=false&characterEncoding=utf8&enableQueryTimeouts=false&useLocalSessionState=false&useLocalTransactionState=false&rewriteBatchedStatements=true&allowMultiQueries=true&socketTimeout=600000 user=tpcc@oboracle01#obdemo password=123456 warehouses=100 loadWorkers=10
OB 只读副本部署
新增一个 ZONE
在集群 obdemo
的管理界面里点击“新增 Zone”,选一个机器,用于只读副本。
新增一个只读副本
在租户里新增一个副本,类型选择“只读副本”。
OB 单副本备集群部署
新建备集群
在集群 obdemo
的集群管理界面,点击“新建备集群”。
新建备集群的任务里面也会有 IO 测试,bootstrap
操作。搭建备集群并不是增加一个备副本,而是搭建一个独立的 OB 集群。
新增 OBProxy
新增一个 OBProxy 访问只读副本。
目前 OCP 还不支持创建 2 个 OBProxy 集群关联到一个 OB 集群。所以这个 OBProxy 暂时只能手动创建。
OBProxy 启动的时候指定 obconfig_url
。
需要取这个值中的 IP 和端口部分,替换下面参数中的 IP和端口。
[admin@observer04:/opt/taobao/install/obproxy]# cd /opt/taobao/install/obproxy && bin/obproxy -p2883 -cobdemo -o "obproxy_config_server_url=http://172.23.152.220:8080/services?Action=GetObProxyConfig&User_ID=alibaba-inc&uid=ocpmaster,proxy_idc_name=IDC22" bin/obproxy -p2883 -cobdemo -o obproxy_config_server_url=http://172.23.152.220:8080/services?Action=GetObProxyConfig&User_ID=alibaba-inc&uid=ocpmaster,proxy_idc_name=IDC22 listen port: 2883 cluster_name: obdemo optstr: obproxy_config_server_url=http://172.23.152.220:8080/services?Action=GetObProxyConfig&User_ID=alibaba-inc&uid=ocpmaster,proxy_idc_name=IDC22 [admin@observer04:/opt/taobao/install/obproxy]# ps -ef|grep obproxy admin 6761 1 30 19:31 ? 00:00:01 bin/obproxy -p2883 -cobdemo -o obproxy_config_server_url=http://172.23.152.220:8080/services?Action=GetObProxyConfig&User_ID=alibaba-inc&uid=ocpmaster,proxy_idc_name=IDC22 admin 7192 31451 0 19:31 pts/0 00:00:00 grep --color=auto obproxy [admin@observer04:/opt/taobao/install/obproxy]#
注意:备副本的 OBPROXY 我在启动的时候特地指定了 proxy_idc_name=IDC22
这个参数。当通过这个 OBPROXY 发起弱一致性读
时,会按下面顺序路由:
- 同一个 IDC 下的备副本所在的 OBSERVER 节点。
- 同一个 REGION 下的其他 IDC 下的备副本所在的 OBSERVER 节点。
- 其他 REGION 下的备副本所在的 OBSERVER 节点
集群拓扑
- 主集群视角的拓扑
- 备集群视角的拓扑
也可以在集群里通过 SQL 确认:
select t1.name resource_pool_name, t3.unit_id, t2.`name` unit_config_name, t2.max_cpu, t2.min_cpu, round(t2.max_memory/1024/1024/1024) max_mem_gb, round(t2.min_memory/1024/1024/1024) min_mem_gb, t3.unit_id, t3.zone, concat(t3.svr_ip,':',t3.`svr_port`) observer,t4.tenant_id, t4.tenant_name from __all_resource_pool t1 join __all_unit_config t2 on (t1.unit_config_id=t2.unit_config_id) join __all_unit t3 on (t1.`resource_pool_id` = t3.`resource_pool_id`) left join __all_tenant t4 on (t1.tenant_id=t4.tenant_id) order BY t1.tenant_id ,t1.`resource_pool_id`, t2.`unit_config_id`, t3.unit_id ; SELECT * FROM __all_zone WHERE name IN ('region','idc');
测试验证
确认集群租户资源分布
这个不是必须的。只是顺道展示一下 OCP 新增的功能。也方便大家对只读副本的理解。
确认表(分区)的位置
以表bmsql_item
为例。这是个单表。
运行下面 SQL 查询这个表分区的位置
SELECT t.tenant_id, a.tenant_name, d.database_name, t.table_name, tg.tablegroup_name , t.part_num , t2.partition_id, t2.ZONE, t2.svr_ip, t2.ROLE, t2.data_size , a.primary_zone , IF(t.locality = '' OR t.locality IS NULL, a.locality, t.locality) AS locality FROM oceanbase.__all_tenant AS a JOIN oceanbase.__all_virtual_database AS d ON ( a.tenant_id = d.tenant_id ) JOIN oceanbase.__all_virtual_table AS t ON (t.tenant_id = d.tenant_id AND t.database_id = d.database_id) JOIN oceanbase.__all_virtual_meta_table t2 ON (t.tenant_id = t2.tenant_id AND (t.table_id=t2.table_id OR t.tablegroup_id=t2.table_id) AND t2.ROLE IN (1,2) ) LEFT JOIN oceanbase.__all_virtual_tablegroup AS tg ON (t.tenant_id = tg.tenant_id and t.tablegroup_id = tg.tablegroup_id) WHERE a.tenant_id IN (1001 ) AND t.table_type IN (3) AND d.database_name LIKE '%TPCC%' AND t.table_name IN ('BMSQL_ITEM') ;
从图中看表bmsql_item
的主副本在 221 上,其他节点都是备副本。
确认 SQL 路由节点的方法
OceanBase 里所有的 SQL 都会在sys
租户的内部视图gv$sql_audit
里记录。这个对排查问题非常方便。
下面语句是根据 SQL 文本查询执行信息
SELECT /*+ read_consistency(weak) ob_querytimeout(100000000) */ substr(usec_to_time(request_time),1,19) request_time_, s.svr_ip, s.client_Ip, s.sid, s.plan_type, s.query_sql, s.affected_rows, s.return_rows, s.ret_code FROM gv$sql_audit s WHERE s.tenant_id=1001 AND user_name LIKE '%TPCC%'AND query_sql LIKE '%TEST_RO%' ORDER BY request_time DESC LIMIT 10;
其中
client_ip
是 SQL 在 OceanBase 集群视角的客户端 IP。如果客户端是通过 OBPROXY 访问 OB,那么这个地址通常就是 OBPROXY 的地址。svr_ip
是 SQL 被路由到 OceanBase 集群的节点 IP。通常是 SQL 访问分区的主副本 IP,不过有时候也不是。这个跟 OBPROXY 路由策略有关。这个以后可以独立成篇去介绍。plan_type
是 SQL 的执行类型。1:表示在该节点(指svr_ip
)本机执行;2:表示是发送到其他节点远程执行。发送到哪个节点这里看不到(通常是指发送到分区主副本节点);3 表示是这个 SQL 是个分布式执行计划,会涉及到多个分区。有可能这些分区还跨节点了。
所以,上图的解释是这个 SQL 从 55 这个 IP 路由到 55 这个节点。由于访问的表的主副本不在这个节点,所以又再次路由到其他节点(实际是主副本所在节点,即 221 )。
业务读写验证
前面的 TPCC 场景的数据初始化使用的就是第一个 OBProxy 集群,包含读写操作。
首先我们看有一个实时读写的单表 SQL 会被路由到哪个节点。测试 SQL 类似
select /*+ test_rw */ count(*) cnt from bmsql_item\G
注意:/*+ */ 是 SQL 的注释语法,也是 HINT 语法。 test_rw
不是标准的 SQL HINT,所以会被 SQL 引擎忽略。写在这里只是为了标记这个 SQL,方面后面从 SQL 审计视图里定位到这个 SQL。
为了简化情况,让下面的测试案例可以复现。请严格按照测试方法做。每次执行 SQL 时都重新连接。不要复用老的连接。
开始测试,通过 observer02 上的 OBPROXY 连接集群看看。然后换 observer01 上的 OBPROXY 连接集群看看。
[root@observer00:/root]# obclient -hobserver02 -utpcc@oboracle01#obdemo -P2883 -p123456 -s -e "select /*+ test_rw_10 */ count(*) cnt from bmsql_item\G " obclient: [Warning] Using a password on the command line interface can be insecure. *************************** 1. row *************************** CNT: 100000 [root@observer00:/root]# obclient -hobserver01 -utpcc@oboracle01#obdemo -P2883 -p123456 -s -e "select /*+ test_rw_10 */ count(*) cnt from bmsql_item\G " obclient: [Warning] Using a password on the command line interface can be insecure. *************************** 1. row *************************** CNT: 100000
看 SQL 审计结果
所以,无论从哪个 OBPROXY 链接 OB 集群,通常单表 SQL 最后都会被路由到该表主副本所在的节点。这种路由规则叫强一致性读
。
本项验证的目的已经达到。下面演示弱一致性读
的路由规则。弱一致性读只允许就近读取备副本。需要指定 HINT (read_consistency(weak)
或者在会话级别设置。
obclient -hobserver01 -utpcc@oboracle01#obdemo -P2883 -p123456 -s -e "set session ob_read_consistency=weak; select /*+ test_ro_41 */ count(*) cnt from bmsql_item\G" obclient -hobserver01 -utpcc@oboracle01#obdemo -P2883 -p123456 -s -e "set session ob_read_consistency=weak; select /*+ test_ro_41 */ count(*) cnt from bmsql_item\G" obclient -hobserver02 -utpcc@oboracle01#obdemo -P2883 -p123456 -s -e "set session ob_read_consistency=weak; select /*+ test_ro_41 */ count(*) cnt from bmsql_item\G" obclient -hobserver02 -utpcc@oboracle01#obdemo -P2883 -p123456 -s -e "set session ob_read_consistency=weak; select /*+ test_ro_41 */ count(*) cnt from bmsql_item\G"
观察 SQL 路由信息
注意: SQL 审计视图按时间顺序是从下往上看。
多测试可以看出,如果选择备副本所在节点的 OBPROXY 连接 OB 集群,它就随机转发给备副本的 OBSERVER 节点了,然后 SQL 也是本地执行(plan_type
=1)。如果选择主副本所在节点的 OBPROXY 连接 OB 集群,它就随机转发给所有的 的 OBSERVER 节点。
除了用 SQL HINT 外,还有个办法就是通过 SESSION 变量来设置弱一致性读
。
[root@observer00:/root]# obclient -hobserver01 -utpcc@oboracle01#obdemo -P2883 -p123456 -s -e "set session ob_read_consistency=weak; select /*+ test_rw_14 */ count(*) cnt from bmsql_item\G" obclient: [Warning] Using a password on the command line interface can be insecure. *************************** 1. row *************************** CNT: 100000 [root@observer00:/root]# obclient -hobserver02 -utpcc@oboracle01#obdemo -P2883 -p123456 -s -e "set session ob_read_consistency=weak; select /*+ test_rw_14 */ count(*) cnt from bmsql_item\G" obclient: [Warning] Using a password on the command line interface can be insecure. *************************** 1. row *************************** CNT: 100000 [root@observer00:/root]#
弱一致性读通常用于少量的读写分离
场景。对于大量的读写分离需求,或者为了绝对不影响主副本的读写,那推荐使用只读副本。
只读副本访问验证
只读副本主要用于读写分离场景。对于一些业务上能接受些许延时的查询,可以访问“只读副本”减轻主副本的压力。只读副本的访问也必须使用弱一致性读
。
下面就验证只读副本的访问是否真的会路由到只读副本的节点。为了便于理解,我加了一些强一致性读进行对比。
obclient -h172.23.152.221 -utpcc@oboracle01#obdemo -P2883 -p123456 -s -e "select /*+ test_ro_28 */ count(*) cnt from bmsql_item\G" obclient -h172.23.152.221 -utpcc@oboracle01#obdemo -P2883 -p123456 -s -e "set session ob_read_consistency=weak; select /*+ test_ro_28 */ count(*) cnt from bmsql_item\G" obclient -h172.26.154.55 -utpcc@oboracle01#obdemo -P2883 -p123456 -s -e "select /*+ test_ro_28 */ count(*) cnt from bmsql_item\G" obclient -h172.26.154.55 -utpcc@oboracle01#obdemo -P2883 -p123456 -s -e "set session ob_read_consistency=weak; select /*+ test_ro_28 */ count(*) cnt from bmsql_item\G"
注意: SQL 审计视图按时间顺序是从下往上看。
从上图可以看出,发往只读副本所在节点的 OBPROXY(这个 OBPROXY 启动时指定了特别的 IDC 参数,见前面说明)的弱一致性读,会路由到同一个 IDC 下的 OBSERVER 节点(也是本机节点,测试环境受限就这么巧),而不是随机到其他备副本节点。这就是只读副本的目的。
同时也看出,发往只读副本所在节点的 OBPROXY 的强一致性读,还是会被路由到主副本所在的节点。这个就是 OB 的只读副本跟其他分布式数据库主从读写分离方案最大的不同。OB 绝对不会出现错误的写了只读副本(双写)。
然而 OB 更特殊的优势还在只读副本故障时的情形。
只读副本故障后验证
通常模拟故障就是直接登录节点 杀掉 OBSERVER 进程。最近 OCP 增加了停止节点的功能,这里就顺便演示一下。(以前有客户吐槽 OB 不能停机,现在可以了 😆)
同样的,停 OBSERVER 节点也会生成一个任务,并且拆分为很多原子任务。这个过程会比我直接 KILL 节点要长很多。这是因为 OCP 选择了一种严谨的稳妥的步骤。
确认节点状态
select a.zone,concat(a.svr_ip ,':',a.svr_port) observer, usec_to_time(b.last_offline_time) last_offline_time, usec_to_time(b.start_service_time) start_service_time, b.status, b.with_rootserver from __all_virtual_server_stat a join __all_server b on (a.svr_ip=b.svr_ip and a.svr_port=b.svr_port) order by a.ZONE, a.svr_ip ;
再跑弱一致性验证
obclient -h172.23.152.221 -utpcc@oboracle01#obdemo -P2883 -p123456 -s -e "select /*+ test_ro_29 */ count(*) cnt from bmsql_item\G" obclient -h172.23.152.221 -utpcc@oboracle01#obdemo -P2883 -p123456 -s -e "set session ob_read_consistency=weak; select /*+ test_ro_29 */ count(*) cnt from bmsql_item\G" obclient -h172.26.154.55 -utpcc@oboracle01#obdemo -P2883 -p123456 -s -e "select /*+ test_ro_29 */ count(*) cnt from bmsql_item\G" obclient -h172.26.154.55 -utpcc@oboracle01#obdemo -P2883 -p123456 -s -e "set session ob_read_consistency=weak; select /*+ test_ro_29 */ count(*) cnt from bmsql_item\G"
从图中可以看出,当只读副本不可访问时,原弱一致性读的 SQL 会被路由到同一个 REGION(RG2)的其他 IDC(IDC3) OBSERVER 节点。
也就是说即使只读副本故障了,那些只读查询业务也不会受影响(OBPROXY 会迅速把路由切换到其他候选的备副本所在 OBSERVER 节点)。这点也是 OB 跟其他分布式数据库中间件读写分离最大的不同。尽管后者也可以通过高可用 agent 去判断只读实例故障然后更改各个备实例的读写权重。读写权重那个设置是全局层面的,无法按业务切分。
只读副本跟全功能备副本之间的在线转换
OceanBase 更特别的地方就是只读副本跟全功能副本可以在线转换。
上图是编辑Zone3 的全功能副本,改为只读副本。也可以改 Zone4 的只读副本为全功能副本。基本上 1 分钟就生效。
任务图如下:
最终结果如下:
只读副本和全功能副本的在线转换这个能力,通常用于 OceanBase 集群在线跨机房跨城市搬迁。支付宝每年双11 大促前后常做这种在线搬迁。
备集群的访问
OB 现在也支持主备集群架构(DATAGUARD)。备集群的使用详情以后再介绍,这里只展示一下备集群的访问。
首先备集群的访问跟主集群一样,只是集群名字后面多了一个 :2
上图: obdemo:1
是主集群,obdemo:2
是备集群。
备集群访问方式:
obclient -h172.26.154.55 -utpcc@oboracle01#obdemo:2 -P2883 -p123456 -s -e "set session ob_read_consistency=weak; select /*+ test_ro_30 */ count(*) cnt from bmsql_item\G" obclient -h172.26.154.55 -utpcc@oboracle01#obdemo:2 -P2883 -p123456 -s -e "select /*+ test_ro_30 */ count(*) cnt from bmsql_item\G"
注意:备集群只能读不能写,并且这个读必须是弱一致性读
。
通常主集群的任意一个 OBPROXY 也可以访问备集群。
下面是上面 SQL 在备集群的 SQL 审计视图里的路由信息。
总结
综上所述,OB 的读写分离有三种方案:
- 三副本或者五副本架构下,个别 SQL 通过弱一致性读 HINT 或者会话设置,就近只读备副本。
- 三副本或五副本架构下,额外增加一个或多个只读副本,为只读副本配置单独的 OBPROXY ,只读业务走这个 OBPROXY 专门访问只读副本。
- 为 OB 集群搭建一个备集群,可以是单副本或者三副本。只读业务专门访问备集群。
至于要选择哪种方案呢,还是要看“读写分离”的目的。想在什么范围内隔离只读查询对读写业务的影响,决定了选择哪种方案更合适。
OB 读写分离方案的优势有两点:
- 无论什么状况都不用担心误写了“备副本或只读副本”,因为它不支持写,写操作会被路由到主副本。(当然备集群那个例外,不能跨集群路由)。
- 无论什么时候都不用担心“备副本或只读副本”故障了,因为 OBPROXY会就近路由到其他备副本。(当然单副本备集群不能,如果要防范这个风险,就搭建三副本备集群就行,自身有高可用)。
用惯了 ORACLE 的 ADG 或者 MySQL 的主从读写分离的,可能会觉得 OB 的读写分离方案复杂。这个就需要转变观念了。读写分离方案严格的说要考虑各种异常情况,OB 把这些异常都做到内部去了,但对用户使用是极其方便的,对运维的工作也是很友好的。当企业的数据库机器规模成百上千后,企业级的自动化运维平台应该长什么样呢?命令行下脚本运维肯定是不行,自动化任务失败率过高时也是不行。
小提示:仅仅通过 OCP 完成只读副本和备集群搭建还不算真正掌握这个能力,其背后具体的操作步骤(尤其是 SQL)才是关键,感兴趣的朋友,可以在任务列表里下载这些任务的日志进行详细研究。 HAVE FUN 😆!
更多文章请搜索个人公众号:OceanBase技术闲谈