OceanBase转储/合并

OceanBase转储/合并

之前文章介绍了LSM-Tree对比B-Tree的优势,和LSM-Tree的几种算法,并且我们知道了LSM-Tree大部分数据是在内存中。通过多种Compaction算法去优化平衡写放大、读放大、空间放大。
OceanBase存储结构就是用了LSM-Tree,本文通过上一章对LSM-Tree的基本概念,继续延伸到OceanBase中的内存及转储/合并。

OceanBase内存结构

  如果大家安装配置过OB,相信会看到memory_limit、memory_limit_percentage这两个参数,OB提供了两种方式控制了一个OBServer的总内存上线:

  • 按照物理机或虚拟机总内存的百分比计算observer内存上限:由memory_limit_percentage参数配置
  • 直接设置observer内存上限:由memory_limit参数配置
    memory_limit=0时,memory_limit_percentage决定observer内存大小;否则由memory_limit决定observer内存大小
memory_limit_percentage memory_limit OBServer内存上限
场景1 80 0 80
场景2 80 90 90
  • 场景1:memory_limit=0,因此由memory_limit_percentage确定observer内存大小,即100GB*80% = 80GB。
  • 场景2:memory_limit=‘90GB’,因此observer内存上限就是90GB,memory_limit_percentage参数失效。

OBServer内部内存结构又可以做多个划分,例如100G的物理总内存,OBServer分配了80G,剩余20G为操作系统剩余的,OBServer中的80G内存分别由租户内存和system内存组成,system内存由参数system_memory设置,例如OBserver总内存上限为80G,system_memory设置为20G,则租户内存为memory_limit-system_memory=60G。

OB支持多租户,即可以在一个OBServer实例中,创建多个物理上隔离的租户,租户之间的内存、CPU、数据库、表相互隔离,可以供多个不同业务使用。

这里经常遇到的问题是创建租户时资源不足,默认OB中会有一个sys租户,用户租户+sys租户的内存使用量不能超过整个租户的内存限制,可以用下面方法查看租户内存使用情况。

obclient [oceanbase]> SELECT t1.name resource_pool_name,        t2.`name` unit_config_name,        t2.max_cpu,        t2.min_cpu,        t2.memory_size/1024/1024/1024 memory_size,                                      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.`resource_pool_id`,          t2.`unit_config_id`,          t3.unit_id;
+--------------------+------------------+---------+---------+----------------+---------+-------+--------------------+-----------+-------------+
| resource_pool_name | unit_config_name | max_cpu | min_cpu | memory_size    | unit_id | zone  | observer           | tenant_id | tenant_name |
+--------------------+------------------+---------+---------+----------------+---------+-------+--------------------+-----------+-------------+
| sys_pool           | sys_unit_config  |       1 |       1 | 2.750000000000 |       1 | zone1 | 10.140.114.12:2882 |         1 | sys         |
| sys_pool           | sys_unit_config  |       1 |       1 | 2.750000000000 |       2 | zone2 | 10.140.60.14:2882  |         1 | sys         |
| sys_pool           | sys_unit_config  |       1 |       1 | 2.750000000000 |       3 | zone3 | 10.140.118.7:2882  |         1 | sys         |
| pool_2             | S2               |       7 |       2 | 8.000000000000 |    1001 | zone1 | 10.140.114.12:2882 |      1002 | tenant_2    |
| pool_2             | S2               |       7 |       2 | 8.000000000000 |    1002 | zone2 | 10.140.60.14:2882  |      1002 | tenant_2    |
| pool_2             | S2               |       7 |       2 | 8.000000000000 |    1003 | zone3 | 10.140.118.7:2882  |      1002 | tenant_2    |
+--------------------+------------------+---------+---------+----------------+---------+-------+--------------------+-----------+-------------+
6 rows in set (0.001 sec)

  租户内的内存结构,分为两种:

  • 不可动态伸缩:MemStore,之前提到过LSM-Tree新增和修改的数据都是先存储到MemTable中,对应的就是这部分内存。
  • 可动态伸缩内存:主要是执行计划缓存(PLANCHE)、SQL执行期间用到的内存主要是parser和优化器使用(SQL AREA)、工作线程所占用的内存(WORKER AREA)

  刚开始使用OB时,使用sysbench造数,总是会遇到"returned error 4030 (Over tenant memory limits) for query"关于内存的错误,这个问题最主要是MmeTable的内存超过了MemStore的限制,OB中默认租户的MemLimit是租户总内存的50%,是由参数memstore_limit_percentage控制,默认值是50。

  当MemStore内存使用超过freeze_trigger_percentage定义的百分比时,触发冻结及后续的转储/合并等行为,下面需要在介绍下OB中的转储/合并操作。

OceanBase中转储/合并

  下面一张是OB中分层存储的结构图,内存中的是MemTbale,磁盘中分为三层
L0层称为 Mini SSTable,L1层称为 Minor SSTable,转储主要发生在这两层,L0内还会进行细分分为L0-LN。
L2层是Major SSTable是做合并时产生的一个全新的版本。

转储:

转储有三类:MINI_MERGE/MINI_MINOR_MERGE/MINOR_MERGE,MINI_MERGE是发生在从MemTable刷新到Mini SSTable,而MINI_MINOR_MERGE是多个Mini SSTable合并成一个新的Mini SSTable,MINOR_MERGE是将L0与L1层合并,从GV$OB_TABLET_COMPACTION_HISTORY可以看到历史发生转储/合并的信息及次数

OB中转储分为自动触发和手动触发

  • 自动触发:

    1. 当MemTable中数据量达到阈值时,就会做Fronze,原MemTable中数据会写入到Mini SSTable中,同时产生一个新的MemTable接受新的增量数据。当租户MemTable使用量达到memstore_limit_percentage * freeze_trigger_percentage时就会触发转储动作,freeze_trigger_percentage默认为50。

    2. L0层中的Ln层SSTable达到一定数量后(由参数minor_compact_trigger控制,默认是2),会触发转储,但这里需要注意并不是每次都是先触发MINI_MINOR_MERGE类型的转储,内部会有会根据Mini SSTable数量、写放大系数、Minor SSTable数量、Row Count,综合判断转储的触发类型。

  • 手动触发:

    1. 手动转储OB中提供了更细粒度的管理,可做到集群/Server/租户/Replica 级别的转储,具体可以参考ALTER SYSTEM MINOR FREEZE命令的使用

合并

  合并就是将所有的Mini SSTable和Minor SSTable与Major SSTable做合并,这样减少SSTable的数量,减少了读放大。这是一个比较耗时和占用CPU的操作,所以在OB中提供了不同方式的合并,去减少合并对线上的影响。
对于合并又三种触发方式:

  1. 定时触发:默认情况下会在每天凌晨2点做合并,可以用过ALTER SYSTEM SET major_freeze_duty_time = ‘02:00’;做修改
  2. 手工触发: ALTER SYSTEM MAJOR FREEZE;
  3. 自动触发:当MemTable使用量达到memstore_limit_percentage * freeze_trigger_percentage,且转储次数达到minor_freeze_times(4.0中改为major_compact_trigger)设置的上限时,就会直接进行合并

合并方式可以从:https://www.oceanbase.com/docs/community-observer-cn-10000000000449401 查看到详细的解释,这里提下轮状合并的方式。

因为做合并操作对系统影响比较大,所以建议在业务低峰期做,但并不是所有业务有低峰期,可以利用OB集群架构特点,进行轮状做合并,减少对业务的影响。
假设集群有6个Zone,每次只对3个Zone进行合并操作,可以如下设置:

alter system set enable_manual_merge = false;  -- 关闭手动合并
alter system set enable_merge_by_turn = true;  -- 开启轮转合并
alter system set zone_merge_order = 'z1,z2,z3,z4,z5,z6', zone_merge_concurrency = 3;  -- 设置合并顺序

通过上面设置后,会按设置的顺序进行z1,z2,z3合并,当有Zone完成合并后,会按继续启z4,保证每次有三个Znoe在进行合并。
但通过轮转合并会加长整个轮状的时间,并且某一个ZONE的合并开始之前,会将这个ZONE上的Leader服务切换到其它ZONE,对长事务是有影响的。

监控转储与合并

  我们可以通过GV$OB_SSTABLES视图,查看SSTable转储与合并的情况,假设当前参数设置如下,通过sysbench 做prepare操作,产生一些数据,观察转储与合并情况:

参数
memstore_limit_percentage 50
freeze_trigger_percentage 10
major_compact_trigger 3
minor_compact_trigger 5
租户内存 7G

需要注意,上面这几个参数是租户级别设置。

  • 内存情况如下,当MEMSTORE_USED>FREEZE_TRIGGER时就会触发转储,并且当转储次数达到3次后就会触发合并:
obclient [OCEANBASE]> select round(ACTIVE_SPAN/1024/1024/1024,2) as ACTIVE_SPAN_GB , round(FREEZE_TRIGGER/1024/1024/1024,2) as FREEZE_TRIGGER_GB, round(MEMSTORE_USED/1024/1024/1024,2) as MEMSTORE_USED_GB , round(MEMSTORE_LIMIT/1024/1024/1024, 2) as MEMSTORE_LIMIT_GB from GV$OB_MEMSTORE where tenant_id =  1002;
+----------------+-------------------+------------------+-------------------+
| ACTIVE_SPAN_GB | FREEZE_TRIGGER_GB | MEMSTORE_USED_GB | MEMSTORE_LIMIT_GB |
+----------------+-------------------+------------------+-------------------+
|           0.04 |              0.30 |             0.04 |              3.00 |
|           0.03 |              0.30 |             0.03 |              3.00 |
|           0.03 |              0.30 |             0.03 |              3.00 |
+----------------+-------------------+------------------+-------------------+
3 rows in set (0.003 sec)
  • 可以单独针对一张表,查看SSTable的情况,先通过oceanbase.CDB_OB_TABLE_LOCATIONS查看表的TABLET_ID:
obclient [oceanbase]> select * from oceanbase.CDB_OB_TABLE_LOCATIONS where tenant_id = 1002 and database_name = 'sysbenchdb';
+-----------+---------------+------------+----------+------------+----------------+-------------------+------------+---------------+-----------+-------+-------+---------------+----------+----------+--------------+
| TENANT_ID | DATABASE_NAME | TABLE_NAME | TABLE_ID | TABLE_TYPE | PARTITION_NAME | SUBPARTITION_NAME | INDEX_NAME | DATA_TABLE_ID | TABLET_ID | LS_ID | ZONE  | SVR_IP        | SVR_PORT | ROLE     | REPLICA_TYPE |
+-----------+---------------+------------+----------+------------+----------------+-------------------+------------+---------------+-----------+-------+-------+---------------+----------+----------+--------------+
|      1002 | sysbenchdb    | sbtest1    |   500015 | USER TABLE | NULL           | NULL              | NULL       |          NULL |    200008 |  1002 | zone1 | 10.140.114.12 |     2882 | FOLLOWER | FULL         |
|      1002 | sysbenchdb    | sbtest1    |   500015 | USER TABLE | NULL           | NULL              | NULL       |          NULL |    200008 |  1002 | zone2 | 10.140.60.14  |     2882 | LEADER   | FULL         |
|      1002 | sysbenchdb    | sbtest1    |   500015 | USER TABLE | NULL           | NULL              | NULL       |          NULL |    200008 |  1002 | zone3 | 10.140.118.7  |     2882 | FOLLOWER | FULL         |
+-----------+---------------+------------+----------+------------+----------------+-------------------+------------+---------------+-----------+-------+-------+---------------+----------+----------+--------------+
3 rows in set (0.023 sec)
  • 当达到minor_compact_trigger设置个数后,会触发MINOR_MERGE或MINOR_MERGE类型的转储
obclient [oceanbase]> SELECT count(*) , type FROM oceanbase.GV$OB_TABLET_COMPACTION_HISTORY where tenant_id = 1002 AND TABLET_ID = 2000068 and svr_ip = '10.140.114.12' group  by  type;
+----------+------------+
| count(*) | type       |
+----------+------------+
|        6 | MINI_MERGE |
+----------+------------+
1 row in set (0.005 sec)

obclient [oceanbase]> SELECT count(*) , type FROM oceanbase.GV$OB_TABLET_COMPACTION_HISTORY where tenant_id = 1002 AND TABLET_ID = 200008 and svr_ip = '10.140.114.12' group  by  type;
+----------+-------------+
| count(*) | type        |
+----------+-------------+
|        7 | MINI_MERGE  |
|        1 | MINOR_MERGE |
+----------+-------------+
2 rows in set (0.004 sec)
  • 当转储次数达到major_compact_trigger时就会触发合并(MAJOR_MERGE)
obclient [oceanbase]> SELECT count(*) , type FROM oceanbase.GV$OB_TABLET_COMPACTION_HISTORY where tenant_id = 1002 AND TABLET_ID = 200008 and svr_ip = '10.140.114.12' group
 by  type;
+----------+-------------+
| count(*) | type        |
+----------+-------------+
|        1 | MAJOR_MERGE |
|        2 | MINI_MERGE  |
+----------+-------------+
2 rows in set (0.005 sec)

使用上的建议

  回到之前我们遇到内存不足的错误:“returned error 4030 (Over tenant memory limits) for query”,对于使用过MySQL的用户来说,会觉得很奇怪,通过我们之前的分析现在也能知道个大概原因了。

如果我们内存很小,想尝试OB做一些简单的测试Prapare数据报错,那么可以调大memstore_limit_percentage并减小freeze_trigger_percentage,这样设置后会加快转储的次数,释放内存。

如果是性能测试,那么可以调大一些freeze_trigger_percentage并将major_compact_trigger设置为0或者很大(需要内存足厚),这样大量的数据就在内存中。

2 个赞

昨天微信公众号:懒人的记录,见到了本篇文章

:grinning: 学习记录,如有写的不对地方, 欢迎指正

请教下,大事务占用哪部分内存?是sql_work_arena还是另外内存区域挤占kvcache呢?如果占用内存过大是否会进行临时落盘?