使用双机房 ODP 实例(阿里云版)

本文以搭建阿里云双机房数据访问代理(ODP)实例访问 OceanBase 为例,介绍如何使用阿里云双机房下的 ODP。

说明

本文以上海非金区(华东 2)为例,其他地域的操作步骤类似。

示例背景

部署拓扑:应用部署于上海非金可用区 A 与可用区 C,每个可用区的应用承载 50 个分片的流量(依赖流量调拨);同时创建部署于可用区 A 与可用区 C 的 ODP 实例(分别映射到可用区 A 与可用区 C 的交换机),应用连接当前可用区内的 ODP 实例。

数据拓扑:共分 100 个分片,即百库百表。使用公有云 OceanBase 的单集群双租户,每个租户分配 50 分片。其中,第一个租户的主可用区设置为可用区 A,分配前 50 分片;第二个可用区的主可用区设置为可用区 C,分配后 50 分片。

说明

前 50 分片的 leader 所在可用区 A 宕机后,OB 会将该 50 分片的 leader 切换至可用区 B

此时,前 50 分片的流量会调拨给 A 可用区内的应用,应用连接当前可用区 A 的 ODP(未产生跨机房),ODP 会连接主可用区在可用区 A 的 OceanBase 租户,访问该租户内的前 50 个分片物理库表(同样未产生跨机房)。

现实情况中,由于上海非金地域中 ODP 实例仅支持可用区 A、C、D,暂时不支持部署在可用区 E、F、G,而 OceanBase 目前仅支持机房 E、F、G 作为主可用区,因此暂时匹配不上,从而出现跨机房访问。

对于三可用区的 OB 集群而言,每个可用区均分布一台 OBServer。在主可用区为 A 的租户中创建的所有分区,其 leader 均为可用区 A;如某个分区的 leader 为可用区 A,则读写该分区时都默认访问位于可用区 A 的 OBServer。1

前 50 分片的 leader 所在可用区 A 宕机后,OB 会将该 50 分片的 leader 切换至可用区 B,并且应用流量也会全部调拨到可用区 B。此时,依然可以为 100 个分片提供数据访问服务(OB 多副本机制)。2

前置条件

  • 开通数据访问代理(ODP)组件服务。

  • 在华东 2(上海)地域创建一个专有网络 VPC,并在上海可用区 A 和可用区 C 分别创建一个虚拟交换机 vSwitch。详见 搭建专有网络专有网络 VPC

购买 ODP 实例

  1. 登录 ODP 控制台页面,左侧导航栏选择 实例,单击 创建实例

  2. 在实例购买页面,选择地域、可用区、实例规格以及类型等。

    • 地域:选择华东2(上海)。

    • 可用区:选择上海非金可用区-A。

    • 实例规格:选择 8 核 16 GB。

    • 类型:选择 OceanBase。

    • 专有网络:选择之前创建好的 VPC。

    • 虚拟交换机:选择位于上海可用区 A 的虚拟交换机实例 ID。

      购买 ODP 实例
      说明

      请尽量选择 ODP 实例所在可用区的虚拟交换机。如果 ODP与应用不在同一可用区,或者 ODP 没有映射到应用所在可用区的虚拟交换机,应用访问 ODP 时则需要进行跨可用区访问,将会导致较高的延时问题。

  3. 单击 立即购买 进入购买预览页面。

  4. 确认订单信息无误并阅读产品服务条款后,勾选 数据访问代理服务协议,单击 去开通 完成购买。

  5. 在购买成功后的提示页面,单击 管理控制台 返回 ODP 实例列表,可以看到刚刚创建的 ODP 实例。ODP 实例

  6. 单击该实例 ID 进入其详情页,您可以看到已有的一个机房,单击详情页右上角的 添加机房,为该逻辑实例添加第二个机房。添加机房

  7. 在新页面中,选择当前地域的另外一个可用区,如 上海非金可用区-C,且虚拟交换机也需选择位于可用区 C 的虚拟交换机实例 ID。新可用区

  8. 购买后,返回该实例详情页,即可看到已存在两个机房。此时,该逻辑实例便对应了两个物理实例(即逻辑实例在某一个机房中的真实实例),分别部署在上海非金可用区 A 和上海非金可用区 C,两个物理实例共享相同的元信息配置。两个机房

添加物理数据节点

  1. 登录 OceanBase 管理控制台,创建一个集群与两个租户,并设置主可用区分别为 华东 2(上海)可用区 EF。详见 创建集群创建租户OB 集群

  2. 将 ODP 地址加入到该 OceanBase 集群的白名单中:

    1. 在 ODP 控制台 > 运维 > 物理数据节点 页面,点击 添加节点,在弹出窗口中,获取 数据访问代理地址获取数据访问代理地址

    2. 返回 OB 集群工作台,单击 集群白名单 栏旁边的编辑图标,添加该 ODP 地址。

  3. 为该 OB 集群下的两个租户分别创建一个超级账号。详见 新建账号

  4. 返回 ODP 控制台 > 运维 > 物理数据节点 页面,分别添加两个租户的数据节点。

    • 物理数据节点 ID:格式为 masterintranet + 租户 ID,如 masterintranett22okxxxxxxxx

    • 链接地址:输入该物理数据节点的访问地址。

    • 数据库类型:选择 OceanBase。

    • 网络类型:选择 专有网络

    • VPC ID:输入物理节点所在专有网络 VPC 的 ID。添加两个租户的数据节点

  5. 添加完成后,在物理数据节点列表,即可看到这两个节点。节点

创建逻辑库与物理库

  1. 进入 ODP 控制台 > 数据库 页面,单击 创建数据库

  2. 在弹出的 创建数据库 窗口中,选择正确的 ODP 实例,单击 创建

  3. 选择数据节点 页面,选择刚刚添加的两个物理数据节点。选择数据节点

  4. 单击 下一步,根据提示,填写或选择基本信息。根据本文的示例场景,需将 物理分库数 设置为 100,如下图所示。基本信息

  5. 单击 下一步,进入建库预览,确认各项信息无误后,选择 所有数据节点使用不同账号,并为两个数据节点配置账号信息,如下图所示。配置账号

  6. 信息确认完毕,单击 创建 开始创建数据库。

  7. 进入该数据库详情页,您可以查看到对应可用区-A(实际为 E)的租户承载前 50 分片,对应可用区-C(实际为 F)的租户承载后 50 分片。数据库

创建逻辑表与物理表

  1. 在刚刚创建的数据库详情页,单击右侧页面下方的 新增数据表

  2. 进入创建数据表向导,填写或选择各项数据表信息,勾选 现在创建物理表,并点击 下一步示例:以 user 表为例,创建拆分表,分表数为 100,分表规则为 MySQL 风格的字符串截断(-2 ~ 2),即取 user_id 字符串的最后两位作为 sharding_key(分库分表位)。创建数据表

  3. 输入 DDL 语句来进行创建数据表,单击 执行DDL 语句

  4. 返回数据库详情页,单击右上角的 配置生效,并 确定

应用连接逻辑库

在业务 VPC 内,您可以直接使用内网地址访问 ODP。您也可以申请公网地址,然后在本地通过 MySQL CLI 来访问 ODP。内网公网地址

使用逻辑库中的 MYSQL 客户端命令行命令,即可访问某个可用区的 ODP 实例中的逻辑库。MYSQL 客户端命令行命令

执行 show topology from $tableName,即可查看某张逻辑表的拓扑结构,如下图所示。topology

应用可以通过 dbp-connector 较为便捷地访问双机房 ODP。详细的配置方法,可参见 配置同城双活模式

配置时,需要特别注意以下几点:

  • 在引入 dbp-connector 的依赖时,版本号必须是 1.1.1 及以上。

  • 在配置连接池时,url 配置的格式为 jdbc:mysql://${阿里云 ODP 实例 ID}@zone@.sofaodp.aliyuncs.com:8306/${数据库名称}

    <bean id="simpleDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init">
         <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
         <property name="url" value="jdbc:mysql://${yourDbpInstanceId}@zone@.sofaodp.aliyuncs.com:8306/${yourDatabase}"/>
         <property name="username" value="${username}"/>
         <property name="password" value="${password}"/>
    </bean>

配置与访问示例

  1. 修改 Spring 文件。本文示例如下:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans  xmlns="http://www.springframework.org/schema/beans"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"
    default-autowire="byName">
    
        <bean id="dbpDiscovery"class="com.alipay.sofa.dbp.discovery.DbpDiscovery"/>
        
        <bean id="simpleDataSourceConnector"class="com.alipay.sofa.dbp.DbpDataSource" init-method="init">
            <property name="delegate" ref="simpleDataSource"/>
            <property name="appName" value="ldc_demo_app"/>
            <property name="database" value="ldc_demo"/>
            <property name="dbpInstanceId" value="sofaodp-l8eh5cdite4w"/>
            <property name="clientTracer" ref="clientTracer"/>
        </bean>
    
        <!-- dataSource pool -->
        <bean id="simpleDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init">
            <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
            <!--   这里为了本地测试使用了公网域名,实际应用部署时应该使用内网域名,即不带public     -->
            <property name="url" value="jdbc:mysql://sofaodp-lxxxxxxxtest@zone@.public.sofaodp.aliyuncs.com:8306/ldc_demo"/>
            <property name="username" value="${username}"/>
            <property name="password" value="${password}"/>
        </bean>
    
        <bean id="clientTracer" class="com.alipay.sofa.dbp.DbpClientTracer">
            <!-- 设置客户端日志才样的阈值,默认3ms(3ms内成功的sql按10%采样),设置为0 全部打印 -->
            <property name="sampleThreshold" value="3"/>
        </bean>
    </beans>
    
  2. 在应用代码(以 Java 为例)中,测试插入一条位于 23 分片的记录,然后查询该条记录。示例如下:

    public class DbpConnectorAliyunTest extends AbstractTestBase{
        @Autowired
        @Qualifier("simpleDataSourceConnector")
        private DataSource dataSource;
    
        static{
    // 应用部署时,发布部署系统会自动为您注入此系统变量为当前可用区,无需手动设置,此处仅为演示
            System.setProperty("com.alipay.ldc.datacenter","cn-shanghai-a");
        }
    
        @Test
        public void test() throws SQLException{
           // user_id的最后两位为分片值,即当前这条数据要落到23分片
            String userId ="随机生成的数字串23";
            String username ="user23";
            String insertSql ="insert into user (id, user_id, username) values (?, ?, ?)";
            Connection connection = dataSource.getConnection();
            PreparedStatement insertStmt = connection.prepareStatement(insertSql);
            insertStmt.setInt(1,0);
            insertStmt.setString(2, userId);
            insertStmt.setString(3, username);
            insertStmt.executeUpdate();
            insertStmt.close();
    
            String querySql ="select username from user where user_id = ?";
            PreparedStatement queryStmt = connection.prepareStatement(querySql);
            queryStmt.setString(1, userId);
            ResultSet resultSet = queryStmt.executeQuery();
            resultSet.next();
            Assert.assertEquals(username, resultSet.getString(1));
        }
    }
    
  3. 连接至物理库,验证数据路由的正确性。验证数据路由