OceanBase数据负载均衡

OceanBase 数据库提供了水平扩缩容和数据动态均衡等负载均衡能力。

水平扩缩容能力指的是通过调整服务节点的数量来实现服务能力的扩缩容,例如,从单个服务节点扩展为两个服务节点,即可实现服务能力的扩容。同时,水平扩缩容还需要支持数据的重分布,例如从单个服务节点扩展为两个服务节点,需要让数据均衡的分布在两个服务节点上;反之,当两个服务节点缩容为单个服务节点时,需要让数据重分布到单个服务节点上。

数据动态均衡能力指的是服务节点不变的情况下,通过调整数据分布,以实现各个服务节点负载动态均衡的能力。例如,随着表和分区的动态创建和删除,不同服务节点上服务的分区个数会有很大差异,导致服务节点的负载不均衡,基于分区动态均衡能力,可以实现分区在各个服务节点上均衡分布,从而实现负载均衡。

水平扩缩容

OceanBase 数据库中租户的存储容量和读写服务能力主要受以下两方面的影响:

  • Unit Number,即每个 Zone 上提供服务的 Unit 个数

    通过增加或减少 Unit Number,从而增加或减少服务节点,实现读写服务和存储容量的水平扩缩容。

    通过调整 Unit Number 实现租户扩缩容的详细操作,请参见 通过调整 Unit Number 实现租户扩缩容

  • Primary Zone,即提供读写服务的 Zone 列表

    通过增加或减少第一优先级的 Primary Zone 的个数,从而增加或减少提供读写服务的 Zone,实现读写服务在 Zone 间的水平扩缩容。

    通过调整 Primary Zone 实现租户扩缩容的详细操作,请参见 通过调整 Primary Zone 实现租户扩缩容

用户通过动态调整 Unit Number 和 Primary Zone,可以实现租户的读写服务能力在 Zone 内和 Zone 间的水平扩缩容。负载均衡功能将根据用户服务能力的配置自适应调整日志流和分区分布。

分区均衡

分区均衡是指在表和分区动态变化的情况下,通过动态调整分区分布,实现分区个数以及存储空间在服务节点上的均衡。

OceanBase 数据库支持多种表类型,包括非分区表、一级分区表和二级分区表。不同类型的表的均衡策略不一样。为了方便描述均衡效果,OceanBase 数据库为不同的表分区划分了均衡组,在各个均衡组内要实现分区个数均衡和存储空间均衡。均衡组之间没有关系,内部自适应调整均衡组之间的分布关系。默认情况下,OceanBase 数据库的分区均衡策略如下:

  • 一级分区表:每个一级分区表是一个独立的均衡组,表的所有一级分区打散分布在各个服务节点上。

  • 二级分区表:每个一级分区下的所有二级分区形成一个独立的均衡组,每个一级分区下的所有二级分区打散分布在各个服务节点上。

  • 非分区表:所有的非分区表统一考虑,有且仅有一个均衡组,所有的非分区表打散分布在各个服务节点上。

为了更加灵活描述不同表数据之间的聚集和打散关系,OceanBase 数据库引入了表组(Table Group)的概念。

表组是一个逻辑概念,表示一组表的集合。表组内的表在物理存储上有临近关系,而多张具有关联关系的表往往具有相同的分区规则,通过将相同规则的分区聚集分布在一起,可以实现 Partition Wise Join,大大优化读写性能。

从 OceanBase 数据库 V4.2.0 版本开始,引入了表组的 SHARDING 属性来控制表组内表数据的聚集和打散关系。表组的 SHARDING 属性有三种取值:NONEPARTITION 和 ADAPTIVE

用户可以根据自己的使用场景,选择合适的 SHARDING属性值。

场景 1:表组内所有表聚集在一起

如果用户希望将任意类型的表聚集在一台机器上,以满足业务单机访问的需求,可以采用 SHARDING = NONE 的表组,将任意类型的表聚集在一起。

SHARDING = NONE 的表组的含义如下:

  • 支持加入任意分区类型的表,包括非分区表、一级分区表和二级分区表。
  • 将表组内所有表的所有分区聚集在一起,系统保证调度在一台机器上。

场景 2:表组内表数据水平打散

当单机承载不下单个业务的数据时,用户希望将数据打散分布在多台机器,以实现水平扩展,可以采用 SHARDING = PARTITION 或 SHARDING = ADAPTIVE 的表组来满足该需求。

SHARDING = PARTITION 的表组的含义如下:

  • 支持加入一级分区表和二级分区表。

    • 分区方式要求:一级分区的分区方式相同;对于二级分区表,系统仅校验一级分区的分区方式。因此,一级分区表和二级分区表可以同时存在,仅要求其一级分区的分区方式相同即可。
    • 分区对齐规则:相同一级分区值的分区聚集在一起,包括一级分区表的一级分区和二级分区表的对应一级分区下的所有二级分区。
  • 将表组内所有表按一级分区打散。同时,对于二级分区表,其一级分区下的所有二级分区聚集在一起。

SHARDING = ADAPTIVE 的表组的含义如下:

  • 支持加入的表为一级分区表或二级分区表。

    • 分区方式要求:全部为一级分区表,或者全部为二级分区表。如果是一级分区表,则要求一级分区的分区方式相同;如果是二级分区表,则要求一级分区和二级分区的分区方式均相同。

    • 分区对齐规则:

      • 一级分区表:一级分区值相同的分区聚集在一起
      • 二级分区表:一级分区值相同,并且二级分区值也相同的分区聚集在一起
  • 自适应打散方式。如果表组内的所有表均为一级分区表,则按一级分区打散;如果表组内的所有表均为二级分区表,则按每个一级分区下的二级分区打散。

更多表组的详细介绍及操作,请参见 创建和管理表组(MySQL 模式) 和 创建和管理表组(Oracle 模式)

数据负载均衡相关的配置项

  • enable_rebalance

    租户级配置项 enable_rebalance 在系统租户下用于控制是否进行租户间的负载均衡;在用户租户下用于控制是否进行租户内均衡。默认值为 true

    更多配置项 enable_rebalance 的详细说明,请参见 enable_rebalance

  • enable_transfer

    租户级配置项 enable_transfer 用于控制是否在租户下进行 Transfer,默认值为 true。其中:

    • 当配置项 enable_rebalance 的值为 false 时,无论配置项 enable_transfer 的值为 true 还是 false,系统均不会进行自动负载均衡。

    • 当配置项 enable_rebalance 的值为 true 且 enable_transfer 的值为 为 true 时,表示在进行租户扩缩容时,系统会自动调整租户内日志流的数量,通过日志流分裂、合并以及 Transfer 等操作,进行租户下的 Leader 均衡和分区均衡。

    • 当配置项 enable_rebalance 的值为 true 且 enable_transfer 的值为 为 false 时,表示在进行租户扩缩容时,系统不会进行 Transfer 并且不会出现日志流数量的变化,仅在现有日志流的基础上尽量保证日志流均衡。

    更多配置项 enable_transfer 的详细说明,请参见 enable_transfer

  • partition_balance_schedule_interval

    租户级配置项 partition_balance_schedule_interval 用于控制生成分区负载均衡任务的时间间隔。表示当 enable_rebalance 的值为 true 时,系统会将 partition_balance_schedule_interval 作为时间间隔来自动触发分区负载均衡任务。默认值为 2h,取值范围为 [0s, +∞),0s 表示关闭分区均衡。

    更多配置项 partition_balance_schedule_interval 的详细说明,请参见 partition_balance_schedule_interval

当自动的负载均衡无法满足用户对于特定分区的聚合和打散需求时,可以手动发起 Transfer Partition 操作。

使用限制及注意事项

  • 执行 Transfer Partition 操作的用户需要拥有 ALTER SYSTEM 权限。有关权限的详细介绍,请参见 用户和权限概述

  • 仅支持在主租户上执行 Transfer Partition 操作,并且只能 Transfer 用户租户下的分区。

  • 关闭租户的 Transfer 功能(即租户下配置项 enable_transfer 的值为 false)后,对该租户执行 TRANSFER PARTITION 语句会报错,已经执行成功了的 TRANSFER PARTITION 语句,其对应的 Transfer Partition 任务可能会被取消。

  • 不支持对系统表执行 Transfer Partition 操作。

  • 不支持对系统租户下的用户表执行 Transfer Partition 操作。

  • 不支持将普通表 Transfer 到广播日志流上,同时也不支持将复制表 Transfer 到普通日志流上。

  • 不支持 Transfer 非独立的分区,例如,局部索引表的分区和 LOB 表的分区不支持 Transfer。

  • 对于同一个分区,在 Transfer Partition 操作未完成之前,不允许再次发起 Transfer Partition 操作。

  • 如果当前集群中已经有一个均衡任务(Balance Job)正在处理,手动触发的 Transfer Partition 任务不会立刻开始调度。如果希望尽快调度 Transfer Partition 任务,可以先取消该 Balance Job 任务,取消 Balance Job 任务的详细操作,请参见 取消 Balance Job

前提条件

  • Transfer Partition 前,需要开启 Transfer 功能。Transfer 功能由租户级配置项 enable_transfer 控制,默认值为 true,表示开启 Transfer 功能。有关配置项 enable_transfer 的详细说明及设置,请参见 enable_transfer

  • 由于 Transfer Partition 策略与自动均衡策略可能会有冲突,为了保证分区位置固定,建议您在执行 Transfer Partition 操作前关闭自动均衡策略,即,将对应租户下配置项 enable_rebalance 的值设置为 false。有关配置项 enable_rebalance 的详细说明及设置,请参见 enable_rebalance

    Transfer Partition 操作本身不受配置项 enable_rebalance 的控制,但当 enable_rebalance 与 enable_transfer 的值均为 true 时,可能会出现用户将某个分区迁移到对应的日志流上后,系统的自动均衡策略又将该分区迁移到其他日志流上的情况。

系统租户 Transfer Partition

  1. 使用 root 用户登录到集群的 sys 租户。

    连接示例如下,连接数据库时请以实际环境为准。

    obclient -h10.xx.xx.xx -P2883 -uroot@sys#obdemo -p***** -A
    
  2. 查询视图 DBA_OB_TENANTS,获取目标租户的 TENANT_ID

    obclient [oceanbase]> SELECT TENANT_ID FROM oceanbase.DBA_OB_TENANTS WHERE TENANT_NAME = 'oracle_tenant';
    

    查询结果如下:

    +-----------+
    | TENANT_ID |
    +-----------+
    |      1006 |
    +-----------+
    1 row in set
    
  3. 确认分区信息。

    可以通过查询视图 CDB_OB_TABLE_LOCATIONS,获取表中指定分区对应的 TABLET_IDOBJECT_ID 以及 LS_ID

    • 获取非分区表中指定分区对应的 TABLET_IDOBJECT_ID 以及 LS_ID

      假设 oracle_tenant 租户下存在非分区表 T1,查询示例如下:

      obclient [oceanbase]> SELECT TABLE_ID AS TABLE_ID, OBJECT_ID, TABLET_ID, LS_ID FROM oceanbase.CDB_OB_TABLE_LOCATIONS WHERE TENANT_ID = 1006 AND DATABASE_NAME = 'SYS' AND TABLE_NAME= 'T1' LIMIT 1;
      

      查询结果如下:

      +----------+-----------+-----------+-------+
      | TABLE_ID | OBJECT_ID | TABLET_ID | LS_ID |
      +----------+-----------+-----------+-------+
      |   500002 |    500002 |    200001 |  1001 |
      +----------+-----------+-----------+-------+
      1 row in set
      
    • 获取一级分区表中指定分区对应的 TABLET_IDOBJECT_ID 以及 LS_ID

      对于一级分区表,查询时只需要指定具体的一级分区名即可。

      假设 oracle_tenant 租户下存在一级分区表 TBL1_LOG_R

      obclient [SYS]> CREATE TABLE tbl1_log_r(log_id INT,log_date DATE NOT NULL DEFAULT SYSDATE)
       PARTITION BY RANGE(log_date) 
        (PARTITION M202001 VALUES LESS THAN(TO_DATE('2020/02/01','YYYY/MM/DD'))
         , PARTITION M202002 VALUES LESS THAN(TO_DATE('2020/03/01','YYYY/MM/DD'))
         , PARTITION M202003 VALUES LESS THAN(TO_DATE('2020/04/01','YYYY/MM/DD'))
         , PARTITION M202004 VALUES LESS THAN(TO_DATE('2020/05/01','YYYY/MM/DD'))
         , PARTITION M202005 VALUES LESS THAN(TO_DATE('2020/06/01','YYYY/MM/DD'))
         , PARTITION M202006 VALUES LESS THAN(TO_DATE('2020/07/01','YYYY/MM/DD'))
         , PARTITION M202007 VALUES LESS THAN(TO_DATE('2020/08/01','YYYY/MM/DD'))
         , PARTITION M202008 VALUES LESS THAN(TO_DATE('2020/09/01','YYYY/MM/DD'))
         , PARTITION M202009 VALUES LESS THAN(TO_DATE('2020/10/01','YYYY/MM/DD'))
         , PARTITION M202010 VALUES LESS THAN(TO_DATE('2020/11/01','YYYY/MM/DD'))
         , PARTITION M202011 VALUES LESS THAN(TO_DATE('2020/12/01','YYYY/MM/DD'))
         , PARTITION M202012 VALUES LESS THAN(TO_DATE('2021/01/01','YYYY/MM/DD'))
         , PARTITION MMAX VALUES LESS THAN (MAXVALUE)
         );
      

      查询该一级分区表中分区 M202005 相关信息的示例如下:

      obclient [oceanbase]> SELECT TABLE_ID AS TABLE_ID, OBJECT_ID, TABLET_ID, LS_ID FROM oceanbase.CDB_OB_TABLE_LOCATIONS WHERE TENANT_ID = 1006 AND DATABASE_NAME = 'SYS' AND TABLE_NAME= 'TBL1_LOG_R' AND PARTITION_NAME = 'M202005' LIMIT 1;
      

      查询结果如下:

      +----------+-----------+-----------+-------+
      | TABLE_ID | OBJECT_ID | TABLET_ID | LS_ID |
      +----------+-----------+-----------+-------+
      |   500003 |    500009 |    200006 |  1002 |
      +----------+-----------+-----------+-------+
      1 row in set
      
    • 获取二级分区表中二级分区对应的 TABLET_IDOBJECT_ID 以及 LS_ID

      对于二级分区表,查询时需要同时指定一级分区名和二级分区名。

      假设 oracle_tenant 租户下存在二级分区表 T2_F_RL

      obclient [SYS]> CREATE TABLE t2_f_rl(col1 INT,col2 VARCHAR2(50))
       PARTITION BY RANGE(col1)
       SUBPARTITION BY LIST(col2)
       (PARTITION p0 VALUES LESS THAN(100)
         (SUBPARTITION sp0 VALUES('01'),
          SUBPARTITION sp1 VALUES('02')
          ),
        PARTITION p1 VALUES LESS THAN(200)
         (SUBPARTITION sp2 VALUES('01'),
          SUBPARTITION sp3 VALUES('02'),
          SUBPARTITION sp4 VALUES('03')
         )
        );
      

      查询该二级分区表中二级分区 SP2 相关信息的示例如下:

      obclient [oceanbase]> SELECT TABLE_ID AS TABLE_ID, OBJECT_ID, TABLET_ID, LS_ID FROM oceanbase.CDB_OB_TABLE_LOCATIONS WHERE TENANT_ID = 1006 AND DATABASE_NAME = 'SYS' AND TABLE_NAME= 'T2_F_RL' AND PARTITION_NAME = 'P1' AND SUBPARTITION_NAME = 'SP2' LIMIT 1;
      

      查询结果如下:

      +----------+-----------+-----------+-------+
      | TABLE_ID | OBJECT_ID | TABLET_ID | LS_ID |
      +----------+-----------+-----------+-------+
      |   500018 |    500023 |    200017 |  1003 |
      +----------+-----------+-----------+-------+
      1 row in set
      
  4. 选择 Transfer 目的端。

    1. 查询视图 CDB_OB_LS,获取租户的日志流状态和信息。

      obclient [oceanbase]> SELECT * FROM oceanbase.CDB_OB_LS WHERE TENANT_ID = 1006;
      

      查询结果如下:

      +-----------+-------+--------+--------------+---------------+-------------+---------------------+----------+---------------------+---------------------+------+
      | TENANT_ID | LS_ID | STATUS | PRIMARY_ZONE | UNIT_GROUP_ID | LS_GROUP_ID | CREATE_SCN          | DROP_SCN | SYNC_SCN            | READABLE_SCN        | FLAG |
      +-----------+-------+--------+--------------+---------------+-------------+---------------------+----------+---------------------+---------------------+------+
      |      1006 |     1 | NORMAL | zone1        |             0 |           0 |                NULL |     NULL | 1701244663685197789 | 1701244663685197789 |      |
      |      1006 |  1001 | NORMAL | zone1        |          1006 |        1001 | 1701239786827662637 |     NULL | 1701244663685197789 | 1701244663685197789 |      |
      |      1006 |  1002 | NORMAL | zone1        |          1007 |        1002 | 1701239786831568305 |     NULL | 1701244664066906860 | 1701244664066906859 |      |
      |      1006 |  1003 | NORMAL | zone1        |          1008 |        1003 | 1701239786834300282 |     NULL | 1701244664175263949 | 1701244664175263948 |      |
      +-----------+-------+--------+--------------+---------------+-------------+---------------------+----------+---------------------+---------------------+------+
      4 rows in set
      
    2. 查询视图 CDB_OB_TABLET_TO_LS,获取日志流上 Tablet 的分布信息。

      obclient [oceanbase]> SELECT LS_ID, COUNT(*) AS C FROM oceanbase.CDB_OB_TABLET_TO_LS WHERE TENANT_ID = 1006 GROUP BY LS_ID;
      

      查询结果如下:

      +-------+------+
      | LS_ID | C    |
      +-------+------+
      |     1 |  578 |
      |  1001 |    7 |
      |  1002 |    5 |
      |  1003 |    7 |
      +-------+------+
      4 rows in set
      

      根据上述展示的信息,选择合适的日志流作为 Transfer 目的端。

  5. 执行以下命令,进行 Transfer Partition 操作。

    ALTER SYSTEM TRANSFER PARTITION TABLE_ID [=] table_id, OBJECT_ID [=] object_id TO LS ls_id TENANT = 'tenant_name';
    

    其中:

    • table_id:表 ID。

    • object_id:分区的唯一标识。

    • ls_id:Transfer 目的端的日志流 ID。

    • tenant_name:待 Transfer 的分区所属的租户。

    以租户 oracle_tenant 为例,将其 SYS 库下的表 TBL1_LOG_R 的 M202005 分区,从当前 1002 号日志流迁移到 1003 号日志流上,示例如下。

    obclient [oceanbase]> ALTER SYSTEM TRANSFER PARTITION TABLE_ID = 500003, OBJECT_ID = 500009 TO LS 1003 TENANT = 'oracle_tenant';
    
  6. Transfer Partition 命令执行成功后,可以通过以下视图查看任务状态。

    1. 查询视图 CDB_OB_TRANSFER_PARTITION_TASKS,获取任务的 TASK_IDTRANSFER_TASK_ID 和 BALANCE_JOB_ID

      视图 CDB_OB_TRANSFER_PARTITION_TASKS 展示了所有租户当前正在处理的分区 Transfer 任务。查询示例如下:

      obclient [oceanbase]> SELECT TASK_ID, BALANCE_JOB_ID, TRANSFER_TASK_ID, STATUS FROM oceanbase.CDB_OB_TRANSFER_PARTITION_TASKS WHERE TENANT_ID = 1006 AND TABLE_ID = 500003 AND OBJECT_ID = 500009;
      

      查询结果的示例如下:

      +---------+----------------+------------------+--------+
      | TASK_ID | BALANCE_JOB_ID | TRANSFER_TASK_ID | STATUS |
      +---------+----------------+------------------+--------+
      |       1 |          17304 |                1 | DOING  |
      +---------+----------------+------------------+--------+
      1 row in set
      

      可以根据查询结果中 STATUS 的值确认任务状态,进一步查看任务执行进度:

      • WAITING: 表示任务在等待中,还没有开始调度。
      • INIT:表示任务已经构建 BALANCE_JOB 了,您可以根据 BALANCE_JOB_ID 查看关联的 BALANCE_JOB 执行进度。
      • DOING: 表示任务已经开始执行 Transfer 了,您可以根据 TRANSFER_TASK_ID 查看关联的 Transfer Partition 任务,TRANSFER_TASK_ID 会变化很多次,一个 Transfer Partition 任务会涉及多次 Transfer。

      如果视图 CDB_OB_TRANSFER_PARTITION_TASKS 的查询结果为空,可以通过视图 CDB_OB_TRANSFER_PARTITION_TASK_HISTORY 来查看任务结果。

    2. 根据获取的 BALANCE_JOB_ID 查询视图 CDB_OB_BALANCE_JOBS 或 CDB_OB_BALANCE_JOB_HISTORY,确认关联的 BALANCE_JOB 的执行状态。

      视图 CDB_OB_BALANCE_JOBS 展示了所有租户当前正在执行的负载均衡工作。每个租户同一时间只有一个负载均衡工作(BALANCE_JOB),每个工作会生成多个负载均衡任务(TRANSFER_TASK)。视图 CDB_OB_BALANCE_JOB_HISTORY 展示了所有租户已执行的负载均衡工作历史。查询示例如下:

      obclient [oceanbase]> SELECT * FROM oceanbase.CDB_OB_BALANCE_JOBS WHERE JOB_ID = 17304;
      
      obclient [oceanbase]> SELECT * FROM oceanbase.CDB_OB_BALANCE_JOB_HISTORY WHERE JOB_ID = 17304;
      

      其中,17304 需要替换为上一步获取的 BALANCE_JOB_ID

      查询结果中的 STATUS 列展示了 BALANCE_JOB 的执行状态:

      • DOING:表示正在执行负载均衡工作。
      • COMPLETED:表示负载均衡工作执行成功。
      • CANCELING:表示负载均衡工作取消中。
      • CANCELED:表示负载均衡工作已取消。
    3. 根据获取的 TRANSFER_TASK_ID 查询视图 CDB_OB_TRANSFER_TASKS 或 CDB_OB_TRANSFER_TASK_HISTORY,确认任务关联的 TRANSFER_TASK 的执行状态。

      视图 CDB_OB_TRANSFER_TASKS 展示了所有租户当前正在执行的负载均衡任务。同一时间可能有多个负载均衡任务正在执行,这些任务(TRANSFER_TASK)都属于同一个负载均衡工作(BALANCE_JOB)。视图 CDB_OB_TRANSFER_TASK_HISTORY 展示了所有租户已执行的负载均衡任务历史。查询示例如下:

      obclient [oceanbase]> SELECT * FROM oceanbase.CDB_OB_TRANSFER_TASKS WHERE TASK_ID = 1;
      
      obclient [oceanbase]> SELECT * FROM oceanbase.CDB_OB_TRANSFER_TASK_HISTORY WHERE TASK_ID = 1;
      

      其中,1 需要替换为前面步骤中获取的 TRANSFER_TASK_ID

      查询结果中的 STATUS 列展示了 TRANSFER_TASK 的执行状态:

      • INIT:表示正在创建任务。
      • START:表示开始执行 Transfer。
      • DOING:表示正在执行 Transfer。
      • ABORTED:表示 Transfer 任务执行失败,任务终止。
      • COMPLETED:表示 Transfer 任务执行成功。
      • FAILED:表示 Transfer 任务执行失败。
      • CANCELED:表示 Transfer 任务取消。
  7. 根据获取的 TASK_ID 查询视图 CDB_OB_TRANSFER_PARTITION_TASK_HISTORY,确认 Transfer Partition 任务的结果。

    视图 CDB_OB_TRANSFER_PARTITION_TASK_HISTORY 展示了所有租户已执行的 Transfer Partition 任务历史。

    obclient [oceanbase]> SELECT * FROM oceanbase.CDB_OB_TRANSFER_PARTITION_TASK_HISTORY WHERE TASK_ID = 1;
    

    其中,1 需要替换为前面步骤中获取的 TASK_ID

    查询结果中的 STATUS 列展示了 Transfer Partition 任务的结果:

    • COMPLETED:表示 Transfer Partition 任务执行成功。

    • FAILED :表示 Transfer Partition 任务执行失败,可以通过 COMMENT 列进一步确认任务失败的原因。COMMENT 列中常见的信息如下:

      • LS not exist or may be in DROPPING/WAIT_OFFLINE status:Transfer 目的端的日志流可能不存在,或者处于 DROPPINGWAITOFFLINE 的状态。
      • LS status is not NORMAL or is in BLOCK_TABLET_IN state:Transfer 目的端的日志流可能不是 NORMAL 的状态,例如,处于 CREATING 或 CREATED 等状态,或者日志流已经被 BLOCK_TABLET_IN,不可以迁入。
      • Table has beed dropped:待 Transfer 的分区所属的表被删除。
      • Partition has beed dropped:待 Transfer 的分区被删除。
      • Partition is already in dest LS:分区已经在 Transfer 目的端上。
      • Need retry, partition may be dropped:在 Transfer 执行时,分区已经被删除了,系统会在下次生成负载均衡任务的时候重新检查确定分区是否存在。
      • Need retry, partition may be dropped or be transferre:在 Transfer 执行时,分区不存在了,可能是由于分区被删除,或者分区不在源端日志流上了,系统会在下次生成负载均衡任务的时候排查分区是否存在或者需要重新生成 Transfer 任务。
  8. Transfer Partition 任务执行成功后,再次查看对应的分区信息。

    obclient [oceanbase]> SELECT TABLE_ID AS TABLE_ID, OBJECT_ID, TABLET_ID, LS_ID FROM oceanbase.CDB_OB_TABLE_LOCATIONS WHERE TENANT_ID = 1006 AND DATABASE_NAME = 'SYS' AND TABLE_NAME= 'TBL1_LOG_R' AND PARTITION_NAME = 'M202005' LIMIT 1;
    

    查询结果如下:

    +----------+-----------+-----------+-------+
    | TABLE_ID | OBJECT_ID | TABLET_ID | LS_ID |
    +----------+-----------+-----------+-------+
    |   500003 |    500009 |    200006 |  1003 |
    +----------+-----------+-----------+-------+
    1 row in set
    

    根据结果可知,SYS 库下的表 TBL1_LOG_R 的 M202005 分区,从之前的 1002 号日志流迁移到了 1003 号日志流上,Transfer Partition 操作成功。

用户租户 Transfer Partition

MySQL 模式

Oracle 模式

本文档以租户 mysql_tenant 为例,提供操作指导。

  1. MySQL 模式租户的租户管理员连接数据库。

    连接示例如下,连接数据库时请以实际环境为准。

    obclient -h10.xx.xx.xx -P2883 -uroot@mysql_tenant#obdemo -p***** -A
    
  2. 确认分区信息。

    查询视图 DBA_OB_TABLE_LOCATIONS,获取指定分区的 TABLET_IDOBJECT_ID 以及 LS_ID

    • 获取非分区表的 TABLET_IDOBJECT_ID 以及 LS_ID

      假设 mysql_tenant 租户下存在非分区表 t1,查询示例如下:

      obclient [test]> SELECT TABLE_ID AS TABLE_ID, OBJECT_ID, TABLET_ID, LS_ID FROM oceanbase.DBA_OB_TABLE_LOCATIONS WHERE DATABASE_NAME = 'test' AND TABLE_NAME= 't1' LIMIT 1;
      

      查询结果如下:

      +----------+-----------+-----------+-------+
      | TABLE_ID | OBJECT_ID | TABLET_ID | LS_ID |
      +----------+-----------+-----------+-------+
      |   500002 |    500002 |    200001 |  1001 |
      +----------+-----------+-----------+-------+
      1 row in set
      
    • 获取一级分区表的 TABLET_IDOBJECT_ID 以及 LS_ID

      对于一级分区表,查询时只需要指定具体的一级分区名即可。示例如下:

      假设 mysql_tenant 租户下存在一级分区表 tbl1_l

      obclient [test]> CREATE TABLE tbl1_l (col1 BIGINT PRIMARY KEY,col2 VARCHAR(50))
       PARTITION BY LIST(col1) 
        (PARTITION p0 VALUES IN (1, 2, 3),
         PARTITION p1 VALUES IN (5, 6),
         PARTITION p2 VALUES IN (DEFAULT)
        );
      

      查询该一级分区表中分区 p1 相关信息的示例如下:

      obclient [oceanbase]> SELECT TABLE_ID AS TABLE_ID, OBJECT_ID, TABLET_ID, LS_ID FROM oceanbase.DBA_OB_TABLE_LOCATIONS WHERE DATABASE_NAME = 'test' AND TABLE_NAME= 'tbl1_l' AND PARTITION_NAME = 'p1' LIMIT 1;
      

      查询结果如下:

      +----------+-----------+-----------+-------+
      | TABLE_ID | OBJECT_ID | TABLET_ID | LS_ID |
      +----------+-----------+-----------+-------+
      |   500012 |    500014 |    200009 |  1001 |
      +----------+-----------+-----------+-------+
      1 row in set
      
    • 获取二级分区表的 TABLET_IDOBJECT_ID 以及 LS_ID

      对于二级分区表,查询时需要同时指定一级分区名和二级分区名。

      假设 mysql_tenant 租户下存在二级分区表 t2_f_rclc

      obclient [test]> CREATE TABLE t2_f_rclc (col1 INT,col2 INT) 
       PARTITION BY RANGE COLUMNS(col1)
       SUBPARTITION BY LIST COLUMNS(col2)
       (PARTITION p0 VALUES LESS THAN(100)
         (SUBPARTITION sp0 VALUES IN(1,3),
          SUBPARTITION sp1 VALUES IN(4,6),
          SUBPARTITION sp2 VALUES IN(7,9)),
        PARTITION p1 VALUES LESS THAN(200)
         (SUBPARTITION sp3 VALUES IN(1,3),
          SUBPARTITION sp4 VALUES IN(4,6),
          SUBPARTITION sp5 VALUES IN(7,9))
       ); 
      

      查询该二级分区表中二级分区 sp3 相关信息的示例如下:

      obclient [oceanbase]> SELECT TABLE_ID AS TABLE_ID, OBJECT_ID, TABLET_ID, LS_ID FROM oceanbase.DBA_OB_TABLE_LOCATIONS WHERE DATABASE_NAME = 'test' AND TABLE_NAME= 't2_f_rclc' AND PARTITION_NAME = 'P1' AND SUBPARTITION_NAME = 'sp3' LIMIT 1;
      

      查询结果如下:

      +----------+-----------+-----------+-------+
      | TABLE_ID | OBJECT_ID | TABLET_ID | LS_ID |
      +----------+-----------+-----------+-------+
      |   500003 |    500009 |    200005 |  1002 |
      +----------+-----------+-----------+-------+
      1 row in set
      
  3. 选择 Transfer 目的端。

    1. 查询视图 DBA_OB_LS,获取租户的日志流状态和信息。

      obclient [oceanbase]> SELECT * FROM oceanbase.DBA_OB_LS;
      

      查询结果如下:

      +-------+--------+--------------+---------------+-------------+---------------------+----------+---------------------+---------------------+------+
      | LS_ID | STATUS | PRIMARY_ZONE | UNIT_GROUP_ID | LS_GROUP_ID | CREATE_SCN          | DROP_SCN | SYNC_SCN            | READABLE_SCN        | FLAG |
      +-------+--------+--------------+---------------+-------------+---------------------+----------+---------------------+---------------------+------+
      |     1 | NORMAL | zone1        |             0 |           0 |                NULL |     NULL | 1701247658419109377 | 1701247658419109377 |      |
      |  1001 | NORMAL | zone1        |          1003 |        1001 | 1701239750437064613 |     NULL | 1701247658419109376 | 1701247658419109376 |      |
      |  1002 | NORMAL | zone1        |          1004 |        1002 | 1701239750441114919 |     NULL | 1701247658223204599 | 1701247658223204599 |      |
      |  1003 | NORMAL | zone1        |          1005 |        1003 | 1701239750443869478 |     NULL | 1701247659122731843 | 1701247659030755559 |      |
      +-------+--------+--------------+---------------+-------------+---------------------+----------+---------------------+---------------------+------+
      4 rows in set
      
    2. 查询视图 DBA_OB_TABLET_TO_LS,获取日志流上 Tablet 的分布信息。

      obclient [oceanbase]> SELECT LS_ID, COUNT(*) AS C FROM oceanbase.DBA_OB_TABLET_TO_LS GROUP BY LS_ID;
      

      查询结果如下:

      +-------+------+
      | LS_ID | C    |
      +-------+------+
      |     1 |  590 |
      |  1001 |    4 |
      |  1002 |    3 |
      |  1003 |    3 |
      +-------+------+
      4 rows in set
      

      根据上述展示的信息,选择合适的日志流作为 Transfer 目的端。

  4. 执行以下命令,进行 Transfer Partition 操作。

    ALTER SYSTEM TRANSFER PARTITION TABLE_ID [=] table_id, OBJECT_ID [=] object_id TO LS ls_id;
    

    其中:

    • table_id:表 ID。

    • object_id:分区的唯一标识。

    • ls_id: Transfer 目的端的日志流 ID。

    • tenant_name:待 Transfer 的分区所属的租户。

    将租户 mysql_tenant 内 test 库下的表 tbl1_l 的 p1 分区,从当前 1001 号日志流迁移到 1003 号日志流上,示例如下。

    obclient [oceanbase]> ALTER SYSTEM TRANSFER PARTITION TABLE_ID = 500012, OBJECT_ID = 500014 TO LS 1003;
    
  5. Transfer Partition 的命令执行成功后,可以通过以下视图查看任务状态。

    1. 查询视图 DBA_OB_TRANSFER_PARTITION_TASKS,获取任务的 TASK_IDTRANSFER_TASK_ID 和 BALANCE_JOB_ID

      视图 DBA_OB_TRANSFER_PARTITION_TASKS 展示了本租户下当前正在处理的 Transfer Partition 任务。查询示例如下:

      obclient [oceanbase]> SELECT TASK_ID, BALANCE_JOB_ID, TRANSFER_TASK_ID, STATUS FROM oceanbase.DBA_OB_TRANSFER_PARTITION_TASKS WHERE TABLE_ID = 500012 AND OBJECT_ID = 500014;
      

      查询结果的示例如下:

      +---------+----------------+------------------+--------+
      | TASK_ID | BALANCE_JOB_ID | TRANSFER_TASK_ID | STATUS |
      +---------+----------------+------------------+--------+
      |       1 |          26506 |                1 | DOING  |
      +---------+----------------+------------------+--------+
      1 row in set
      

      可以根据查询结果中 STATUS 的值确认任务状态,进一步查看任务执行进度:

      • WAITING: 表示任务在等待中,还没有开始调度。
      • INIT:表示任务已经构建 BALANCE_JOB 了,您可以根据 BALANCE_JOB_ID 查看关联的 BALANCE_JOB 执行进度。
      • DOING: 表示任务已经开始执行 Transfer 了,您可以根据 TRANSFER_TASK_ID 查看关联的 Transfer Partition 任务,TRANSFER_TASK_ID 会变化很多次,一个 Transfer Partition 任务会涉及多次 Transfer。

      如果视图 DBA_OB_TRANSFER_PARTITION_TASKS 的查询结果为空,可以通过视图 DBA_OB_TRANSFER_PARTITION_TASK_HISTORY 来查看任务结果。

    2. 根据获取的 BALANCE_JOB_ID 查询视图 DBA_OB_BALANCE_JOBS 或 DBA_OB_BALANCE_JOB_HISTORY,确认关联的 BALANCE_JOB 的执行状态。

      视图 DBA_OB_BALANCE_JOBS 展示了本租户当前正在执行的负载均衡工作。每个租户同一时间只有一个负载均衡工作(BALANCE_JOB),每个工作会生成多个负载均衡任务(TRANSFER_TASK)。视图 DBA_OB_BALANCE_JOB_HISTORY 展示了本租户已执行的负载均衡工作历史。查询示例如下:

      obclient [oceanbase]> SELECT * FROM oceanbase.DBA_OB_BALANCE_JOBS WHERE JOB_ID = 26506;
      
      obclient [oceanbase]> SELECT * FROM oceanbase.DBA_OB_BALANCE_JOB_HISTORY WHERE JOB_ID = 26506;
      

      其中,26506 需要替换为上一步获取的 BALANCE_JOB_ID

      查询结果中的 STATUS 列展示了 BALANCE_JOB 的执行状态:

      • DOING:表示正在执行负载均衡工作。
      • COMPLETED:表示负载均衡工作执行成功。
      • CANCELING:表示负载均衡工作取消中。
      • CANCELED:表示负载均衡工作已取消。
    3. 根据获取的 TRANSFER_TASK_ID 查询视图 DBA_OB_TRANSFER_TASKS 或 DBA_OB_TRANSFER_TASK_HISTORY,确认任务关联的 TRANSFER_TASK 的执行状态。

      视图 DBA_OB_TRANSFER_TASKS 展示了本租户当前正在执行的负载均衡任务。同一时间可能有多个负载均衡任务正在执行,这些任务(TRANSFER_TASK)都属于同一个负载均衡工作(BALANCE_JOB)。视图 DBA_OB_TRANSFER_TASK_HISTORY 展示了本租户已执行的负载均衡任务历史。查询示例如下:

      obclient [oceanbase]> SELECT * FROM oceanbase.DBA_OB_TRANSFER_TASKS WHERE TASK_ID = 1;
      
      obclient [oceanbase]> SELECT * FROM oceanbase.DBA_OB_TRANSFER_TASK_HISTORY WHERE TASK_ID = 1;
      

      其中,1 需要替换为前面步骤中获取的 TRANSFER_TASK_ID

      查询结果中的 STATUS 列展示了 TRANSFER_TASK 的执行状态:

      • INIT:表示正在创建任务。
      • START:表示开始执行 Transfer。
      • DOING:表示正在执行 Transfer。
      • ABORTED:表示 Transfer 任务执行失败,任务终止。
      • COMPLETED:表示 Transfer 任务执行成功。
      • FAILED:表示 Transfer 任务执行失败。
      • CANCELED:表示 Transfer 任务已取消。
    4. 根据获取的 TASK_ID 查询视图 DBA_OB_TRANSFER_PARTITION_TASK_HISTORY,确认 Transfer Partition 任务的结果。

      视图 DBA_OB_TRANSFER_PARTITION_TASK_HISTORY 展示了本租户已执行的 Transfer Partition 任务历史。查询示例如下:

      obclient [oceanbase]> SELECT * FROM oceanbase.DBA_OB_TRANSFER_PARTITION_TASK_HISTORY WHERE TASK_ID = 1;
      

      其中,1 需要替换为前面步骤中获取的 TASK_ID

      查询结果中的 STATUS 列展示了 Transfer Partition 任务的结果:

      • COMPLETED:表示 Transfer Partition 任务执行成功。

      • FAILED :表示 Transfer Partition 任务执行失败,可以通过 COMMENT 列进一步确认任务失败的原因。COMMENT 列中常见的信息如下:

        • LS not exist or may be in DROPPING/WAIT_OFFLINE status:Transfer 目的端的日志流可能不存在,或者处于 DROPPINGWAITOFFLINE 的状态。
        • LS status is not NORMAL or is in BLOCK_TABLET_IN state:Transfer 目的端的日志流可能不是 NORMAL 的状态,例如,处于 CREATING 或 CREATED 等状态,或者日志流已经被 BLOCK_TABLET_IN,不可以迁入。
        • Table has beed dropped:待 Transfer 的分区所属的表被删除。
        • Partition has beed dropped:待 Transfer 的分区被删除。
        • Partition is already in dest LS:分区已经在 Transfer 目的端上。
        • Need retry, partition may be dropped:在 Transfer 执行时,分区已经被删除了,系统会在下次生成负载均衡任务的时候重新检查确定分区是否存在。
        • Need retry, partition may be dropped or be transferre:在 Transfer 执行时,分区不存在了,可能是由于分区被删除,或者分区不在源端日志流上了,系统会在下次生成负载均衡任务的时候排查分区是否存在或者需要重新生成 Transfer 任务。
  6. Transfer Partition 任务执行成功后,再次查看对应的分区信息。

    obclient [oceanbase]> SELECT TABLE_ID AS TABLE_ID, OBJECT_ID, TABLET_ID, LS_ID FROM oceanbase.DBA_OB_TABLE_LOCATIONS WHERE DATABASE_NAME = 'test' AND TABLE_NAME= 'tbl1_l' AND PARTITION_NAME = 'p1' LIMIT 1;
    

    查询结果如下:

    +----------+-----------+-----------+-------+
    | TABLE_ID | OBJECT_ID | TABLET_ID | LS_ID |
    +----------+-----------+-----------+-------+
    |   500012 |    500014 |    200009 |  1003 |
    +----------+-----------+-----------+-------+
    1 row in set
    

    根据结果可知,test 库下的表 tbl1_l 的 p1 分区,从之前的 1001 号日志流迁移到了 1003 号日志流上,Transfer Partition 操作成功。

手动发起 Transfer Partition 后,您可以根据业务需要,取消 Transfer Partition 任务。

使用限制

  • 不可取消处于 DOING 状态的 Transfer Partition 任务,但可以取消处于其他状态的 Transfer Partition 任务。

  • 取消 Transfer Partition 任务的操作只能在主库(即主租户)上执行。

系统租户取消 Transfer Partition

  1. 使用 root 用户登录到集群的 sys 租户。

    连接示例如下,连接数据库时请以实际环境为准。

    obclient -h10.xx.xx.xx -P2883 -uroot@sys#obdemo -p***** -A
    
  2. 查询视图 CDB_OB_TRANSFER_PARTITION_TASKS,获取当前任务的状态。

    obclient [oceanbase]> SELECT TENANT_ID, TABLE_ID, OBJECT_ID, STATUS FROM oceanbase.CDB_OB_TRANSFER_PARTITION_TASKS WHERE TENANT_ID = 1002;
    

    查询结果如下:

    +-----------+----------+-----------+---------+
    | TENANT_ID | TABLE_ID | OBJECT_ID | STATUS  |
    +-----------+----------+-----------+---------+
    |      1002 |   500006 |    500008 | WAITING |
    +-----------+----------+-----------+---------+
    1 row in set
    

    查询结果中,只有 STATUS 为非 DOING 状态(WAITINGINIT 等)的任务才能取消。

    如果希望取消的任务的 STATUS 为 DOING 状态,您可以通过将租户级配置项 enable_transfer 的值修改为 false 来关闭 Transfer 功能。关闭 Transfer 功能后,所有 DOING 状态的 Transfer Partition 任务都会回退到 WAITING 状态,就可以执行取消 Transfer Partition 的操作了。

    有关配置项 enable_transfer 的详细说明及设置,请参见 enable_transfer

  3. 根据查询结果,选择合适的操作:

    • 取消指定租户指定分区的 Transfer Partition 任务

      ALTER SYSTEM CANCEL TRANSFER PARTITION TABLE_ID [=] table_id OBJECT_ID [=] object_id TENANT = 'tenant_name';
      

      其中:

      • table_id:待取消分区的表 ID。

      • object_id:待取消的分区。

      • tenant_name:待取消分区的租户。

      示例如下:

      obclient [oceanbase]> ALTER SYSTEM CANCEL TRANSFER PARTITION TABLE_ID = 500006, OBJECT_ID = 500008 TENANT = 'mysql_tenant';
      
    • 取消指定租户下所有的 Transfer Partition 任务

      ALTER SYSTEM CANCEL TRANSFER PARTITION ALL TENANT = 'tenant_name';
      

      示例如下:

      obclient [oceanbase]> ALTER SYSTEM CANCEL TRANSFER PARTITION ALL TENANT = 'mysql_tenant';
      

用户租户取消 Transfer Partition

  1. MySQL 模式租户或 Oracle 模式租户的租户管理员连接数据库。

    连接示例如下,连接数据库时请以实际环境为准。

    obclient -h10.xx.xx.xx -P2883 -uroot@mysql_tenant#obdemo -p***** -A
    
  2. 查询视图 DBA_OB_TRANSFER_PARTITION_TASKS,获取当前任务的状态。

    MySQL 模式

    Oracle 模式

    MySQL 模式下的查询语句如下:

    obclient [oceanbase]> SELECT TABLE_ID, OBJECT_ID, STATUS FROM oceanbase.DBA_OB_TRANSFER_PARTITION_TASKS;
    

    查询结果的示例如下:

    +----------+-----------++--------+
    | TABLE_ID | OBJECT_ID | STATUS |
    +----------+-----------+--------+
    |   500046 |    500052 | INIT   |
    +----------+-----------+--------+
    1 row in set
    

    查询结果中,只有 STATUS 为非 DOING 状态(WAITINGINIT 等)的任务才能取消。

    如果希望取消的任务的 STATUS 为 DOING 状态,您可以通过将租户级配置项 enable_transfer 的值修改为 false 来关闭 Transfer 功能。关闭 Transfer 功能后,所有 DOING 状态的 Transfer Partition 任务都会回退到 WAITING 状态,就可以执行取消 Transfer Partition 的操作了。

    有关配置项 enable_transfer 的详细说明及设置,请参见 enable_transfer

  3. 根据查询结果,选择合适的操作:

    • 取消本租户指定分区的 Transfer Partition 任务

      ALTER SYSTEM CANCEL TRANSFER PARTITION TABLE_ID [=] table_id OBJECT_ID [=] object_id;
      

      其中:

      • table_id:待取消分区的表 ID。

      • object_id:待取消的分区。

      示例如下:

      obclient [oceanbase]> ALTER SYSTEM CANCEL TRANSFER PARTITION TABLE_ID = 500046, OBJECT_ID = 500052;
      
    • 取消本租户下所有的 Transfer Partition 任务

      obclient> ALTER SYSTEM CANCEL TRANSFER PARTITION ALL;

每个租户在同一时间只能有一个负载均衡工作(Balance Job)在运行,如果当前正在进行的 Balance Job 不符合业务需求,您可以取消本次任务。或者,如果您发起了 Transfer Partition,但是当前有正在执行的 Balance Job,导致 Transfer Partition 任务延迟调度,可以先取消当前的 Balance Job。

使用限制

取消 Balance Job 操作只能在主库(即主租户)上执行。

背景信息

每个租户在同一时间只能有一个负载均衡工作(JOB)在运行,这些负载均衡工作包括扩容、缩容、迁移均衡、分区均衡等。每个负载均衡工作会生成含多个负载均衡任务(Task),用于实现负载均衡的具体操作。取消 Balance Job 会取消该 Balance Job 下所有正在运行的 Task。

系统租户取消正在进行的 Balance Job

  1. 使用 root 用户登录到集群的 sys 租户。

    连接示例如下,连接数据库时请以实际环境为准。

    obclient -h10.xx.xx.xx -P2883 -uroot@sys#obdemo -p***** -A
    
  2. 查询视图 CDB_OB_BALANCE_JOBS,获取当前正在执行的任务。

    obclient [oceanbase]> SELECT * FROM oceanbase.CDB_OB_BALANCE_JOBS WHERE TENANT_ID = 1002;
    

    查询结果的示例如下:

    +-----------+--------+----------------------------+----------------------------+----------------------+------------+-----------------+-------------------------+--------+---------+
    | TENANT_ID | JOB_ID | CREATE_TIME                | MODIFY_TIME                | BALANCE_STRATEGY     | JOB_TYPE   | TARGET_UNIT_NUM | TARGET_PRIMARY_ZONE_NUM | STATUS | COMMENT |
    +-----------+--------+----------------------------+----------------------------+----------------------+------------+-----------------+-------------------------+--------+---------+
    |      1002 | 520683 | 2023-11-01 14:56:28.552867 | 2023-11-01 14:56:28.552867 | LS balance by shrink | LS_BALANCE |               1 |                       1 | DOING  | NULL    |
    +-----------+--------+----------------------------+----------------------------+----------------------+------------+-----------------+-------------------------+--------+---------+
    1 row in set
    
  3. 根据查询结果,如果确认该任务不符合预期,可以执行以下操作取消该 Balance Job。

    ALTER SYSTEM CANCEL BALANCE JOB TENANT = 'tenant_name';
    

    示例如下:

    obclient [oceanbase]> ALTER SYSTEM CANCEL BALANCE JOB TENANT ='mysql_tenant';
    

用户租户取消正在进行的 Balance Job

  1. MySQL 模式租户或 Oracle 模式租户的租户管理员连接数据库。

    连接示例如下,连接数据库时请以实际环境为准。

    obclient -h10.xx.xx.xx -P2883 -uroot@mysql_tenant#obdemo -p***** -A
    
  2. 查询视图 DBA_OB_BALANCE_JOBS,获取当前正在执行的任务。

    MySQL 模式

    Oracle 模式

    MySQL 模式下的查询语句如下:

    obclient [oceanbase]> SELECT * FROM oceanbase.DBA_OB_BALANCE_JOBS;
    
  3. 根据查询结果,如果确认不符合预期,可以执行以下操作取消该 Balance Job。

    obclient> ALTER SYSTEM CANCEL BALANCE JOB;

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值