关于oceanbase:故障分析-租户-memstore-内存满问题排查

38次阅读

共计 10586 个字符,预计需要花费 27 分钟才能阅读完成。

作者:操盛春

技术专家,任职于爱可生,专一钻研 MySQL、OceanBase 源码。

本文起源:原创投稿

* 爱可生开源社区出品,原创内容未经受权不得随便应用,转载请分割小编并注明起源。


本文是对官网同名文档局部内容的解释,官网文档链接:
https://www.oceanbase.com/docs/enterprise-oceanbase-database-…

一、查看解冻状况

1.1 解冻性能是否失常?

以 tenant_id = 1001 租户为例,查问 __all_virtual_tenant_memstore_info 表:

-- 留神:where 条件中 tenant_id 须要批改成理论场景对应的值
select * from __all_virtual_tenant_memstore_info where tenant_id = 1001\G

*************************** 1. row ***************************
           tenant_id: 1001
              svr_ip: 10.186.17.106
            svr_port: 22882
active_memstore_used: 1295451600
 total_memstore_used: 1298137088
major_freeze_trigger: 8589934550
      memstore_limit: 17179869150
          freeze_cnt: 0

active_memstore_used 示意沉闷状态的 MemTable 占用的内存,major_freeze_trigger 示意 memstore 占用内存达到 major_freeze_trigger 之后会触发转储(或合并)。

如果解冻性能失常,租户 memstore 占用内存达到 major_freeze_trigger 之后,就会先解冻、而后转储该租户下的 MemTable,转储实现的 MemTable 占用的内存会从 active_memstore_used 中减去。

解冻是转储或合并的前置操作,所以,先依据 active_memstore_used 和 major_freeze_trigger 的大小关系,判断解冻性能是否失常:

  • active_memstore_used <= major_freeze_trigger,阐明解冻性能失常,须要查看转储状况,参照 2. 查看转储状况 大节。
  • active_memstore_used > major_freeze_trigger,阐明解冻性能不失常,须要查看解冻线程的状况,参照 1.2 解冻线程是否失常工作?大节。

1.2 解冻线程是否失常工作?

能够执行以下命令判断负责解冻性能的线程是否在失常运行:

[admin@hostname log]$ grep "tenant manager timer task" observer.log

## 如果线程失常运行,会每 2s 输入一次:====== tenant manager timer task ======
[2023-02-16 08:13:47.952516] INFO  [COMMON] ob_tenant_mgr.cpp:207 [21537][2][Y0-0000000000000000] [lt=10] [dc=0] ====== tenant manager timer task ======
[2023-02-16 08:13:49.953761] INFO  [COMMON] ob_tenant_mgr.cpp:207 [21537][2][Y0-0000000000000000] [lt=9] [dc=0] ====== tenant manager timer task ======
[2023-02-16 08:13:51.955373] INFO  [COMMON] ob_tenant_mgr.cpp:207 [21537][2][Y0-0000000000000000] [lt=52] [dc=0] ====== tenant manager timer task ======
[2023-02-16 08:13:54.022403] INFO  [COMMON] ob_tenant_mgr.cpp:207 [21537][2][Y0-0000000000000000] [lt=21] [dc=0] ====== tenant manager timer task ======
[2023-02-16 08:13:56.023441] INFO  [COMMON] ob_tenant_mgr.cpp:207 [21537][2][Y0-0000000000000000] [lt=8] [dc=0] ====== tenant manager timer task ======
[2023-02-16 08:13:58.024985] INFO  [COMMON] ob_tenant_mgr.cpp:207 [21537][2][Y0-0000000000000000] [lt=9] [dc=0] ====== tenant manager timer task ======
...

如果日志中可能每 2s 失常输入信息:====== tenant manager timer task ======,阐明负责解冻性能的线程失常运行,那就意味着是某些 MemTable 无奈解冻,导致 memstore 占用内存超过 major_freeze_trigger。

这种状况下,须要查看有哪些没有解冻的 MemTable:

-- 留神:where 条件中 svr_ip、tenant_id 须要批改成理论场景对应的值
select a.table_name, b.table_id, b.partition_id, b.mt_base_version, b.mt_is_frozen, b.mt_protection_clock, b.mt_snapshot_version
from gv$table as a inner join __all_virtual_tenant_memstore_allocator_info as b
on a.table_id = b.table_id
where b.mt_is_frozen = 0 and b.svr_ip='10.186.17.106' and b.tenant_id = 1001
order by mt_protection_clock;

+------------+------------------+--------------+------------------+--------------+---------------------+---------------------+
| table_name | table_id         | partition_id | mt_base_version  | mt_is_frozen | mt_protection_clock | mt_snapshot_version |
+------------+------------------+--------------+------------------+--------------+---------------------+---------------------+
| t1         | 1100611139453777 |            0 | 1678500259180548 |            0 |                   0 | 9223372036854775807 |
| t4         | 1100611139453785 |            0 | 1678500010823930 |            0 |                   0 | 9223372036854775807 |
| t3         | 1100611139453781 |            0 | 1678945615601759 |            0 |           165599800 | 9223372036854775807 |
| t5         | 1100611139453787 |            0 | 1678500010823930 |            0 |           276698400 | 9223372036854775807 |
| t2         | 1100611139453778 |            0 | 1678500258966417 |            0 |           404566600 | 9223372036854775807 |
| t6         | 1100611139453789 |            0 | 1678945429338788 |            0 | 9223372036854775807 | 9223372036854775807 |
+------------+------------------+--------------+------------------+--------------+---------------------+---------------------+

mt_protection_clock 示意某个 MemTable 创立、转储、合并之后,进行增、删、改等操作第一次分配内存时,该 MemTable 所属租户 memstore 已占用内存。

按 mt_protection_clock 排序仿佛没有什么非凡意义,可能只是为了不便查看而已。

mt_protection_clock = 9223372036854775807,是个非凡值。

某个 MemTable 转储或合并之后,它的 mt_protection_clock 会批改为 9223372036854775807,而后始终放弃不变,直到转储或合并之后第一次分配内存,mt_protection_clock 才会发生变化。

如果转储或合并之后,MemTable 没有再调配过内存,mt_protection_clock 会始终放弃为 9223372036854775807,重启 OB 之后也还是 9223372036854775807,直到接下来第一次分配内存,mt_protection_clock 的值才会发生变化。

因为租户 memstore 占用内存达到 freeze_trigger_percentage 对应的内存下限之后,会触发租户级别的转储,也就是该租户下的所有 MemTable 都会进行转储。基于这个前提,下面 SQL 语句查问进去的 mt_is_frozen 等于 0,并且 mt_protection_clock 不等于 9223372036854775807 的 MemTable 就有可能是解冻异样的表,须要一一排查确认是否解冻异样。

为什么是有可能解冻异样的表?

因为有可能转储或合并之后,某些 MemTable 表又产生了 DML 操作,插入了新的数据,这种状况下,mt_is_frozen = 0、mt_protection_clock != 9223372036854775807 就是失常的了。

排除这种状况之后,剩下的 MemTable 就是解冻异样的表。

通过下面的一系列操作之后,如果找到了解冻异样的表,能够通过 table_id 查找对应的谬误日志,以 table_id = 1100611139453778 为例:

## 进入 observer.log.wf 日志文件所在的目录
## 如果 OB 的 enable_syslog_wf = false,须要把 observer.log.wf 替换为 observer.log
grep 1100611139453778 observer.log.wf | grep -E "WARN|ERROR"

## 如果下面的命令没有找到谬误日志,也可试试以下命令
grep "fail to do minor freeze" observer.log.wf | grep 1100611139453778

二、查看转储状况

2.1 转储性能是否失常?

以 tenant_id = 1001 租户为例,查问 __all_virtual_tenant_memstore_info 表:

查看转储状况作为查看解冻状况的下一个步骤,只有当解冻状况失常时,才要查看转储状况。这里所举例子和 1. 查看解冻状况中的例子是同一个,为了不便查看,就复制过去了。

-- 留神:where 条件中 tenant_id 须要批改成理论场景对应的值
select * from __all_virtual_tenant_memstore_info where tenant_id = 1001\G

*************************** 1. row ***************************
           tenant_id: 1001
              svr_ip: 10.186.17.106
            svr_port: 22882
active_memstore_used: 1295451600
 total_memstore_used: 1298137088
major_freeze_trigger: 8589934550
      memstore_limit: 17179869150
          freeze_cnt: 0

1. 查看解冻状况大节介绍过,active_memstore_used <= major_freeze_trigger,阐明解冻性能失常。

在此基础上,再判断 total_memstore_used 和 major_freeze_trigger 的关系:

  • 如果 total_memstore_used <= major_freeze_trigger,阐明转储性能失常,那就阐明一切正常,不须要排查了。
  • 如果 total_memstore_used > major_freeze_trigger,阐明转储性能不失常,参照 2.2 及当前大节的内容。

2.2 是否存在沉闷事务?

如果是 OB 2.2.x 版本,能够通过以下 SQL 查问已解冻但未开释内存的 MemTable,是否因为存在沉闷事务,导致转储调度异样,内存无奈开释。

-- 留神:where 条件中 svr_ip、tenant_id 须要批改成理论场景对应的值
-- table_type = 0 示意 MEMTABLE
-- is_active = 0 示意 MEMTABLE 处于解冻状态,还未转储完结
-- 对于 is_active,参照官网文档:https://www.oceanbase.com/docs/enterprise-oceanbase-database-cn-10000000000364739
select * from __all_virtual_table_mgr as a
where a.table_type = 0 and a.is_active = 0 and a.trx_count > 0 and (a.table_id, a.partition_id) in (
  select table_id, partition_id from __all_virtual_tenant_memstore_allocator_info
  where svr_ip='10.186.17.106' and tenant_id=1001 and mt_is_frozen=1
)

如果下面 SQL 查问到了 MemTable,阐明这些查出来的表上因为存在沉闷事务,导致转储调度异样。

能够通过 __all_virtual_trans_stat 表,查看 MemTable 表的事务信息,以确定事务长时间处于沉闷状态的起因。比方:大事务。

如果从 __all_virtual_trans_stat 表中没有失去无效信息,能够再从日志文件中查看下面 MemTable 相干的事务日志,须要依据哪些关键词过滤事务日志,官网文档没有写,后续再补充吧(已列入遗留问题列表)。

2.3 从正本 clog 回放进度慢?

查看已解冻的 MemTable,是否因为 MemTable 的弱一致性读工夫戳小于快照点(snapshot_version),导致 MemTable 转储调度异样,内存无奈开释。

对于弱一致性读工夫戳,参照官网文档:弱一致性读。(https://www.oceanbase.com/docs/enterprise-oceanbase-database-…)

为什么 MemTable 的弱一致性读工夫戳小于快照点(snapshot_version)会导致该 MemTable 转储调度异样,我还没有弄清楚,征询了官网还没有回答,前面搞清楚了再补充(已列入遗留问题列表)。

-- 留神:where 条件中 svr_ip、tenant_id 须要批改成理论场景对应的值
-- table_type = 0 示意 MEMTABLE
-- is_active = 0 示意 MEMTABLE 处于解冻状态,还未转储完结
-- 对于 is_active,参照官网文档:https://www.oceanbase.com/docs/enterprise-oceanbase-database-cn-10000000000364739
select 
  a.svr_ip, a.table_id, a.partition_id, a.is_active, a.table_type, a.snapshot_version,
  b.min_trans_service_ts, b.min_replay_engine_ts, b.min_log_service_ts
from __all_virtual_table_mgr as a inner join __all_virtual_partition_info as b
on a.table_id = b.table_id and a.partition_id = b.partition_idx and a.svr_ip = b.svr_ip
where a.table_type = 0 
and a.is_active = 0 
and a.snapshot_version > least(least(b.min_trans_service_ts, b.min_replay_engine_ts), b.min_log_service_ts)
and (a.table_id, a.partition_id) in (
  select table_id, partition_id from __all_virtual_tenant_memstore_allocator_info
  where svr_ip='10.186.17.106' and tenant_id=1001 and mt_is_frozen=1
);

least(least(min_trans_service_ts, min_replay_engine_ts), min_log_service_ts) 示意取 min_trans_service_ts, min_replay_engine_ts, min_log_service_ts 3 个字段中的最小值。

如果下面 SQL 查问到了 MemTable,阐明这些表的弱一致性读工夫戳小于快照点(snapshot_version),接下来查看是否因为是否因为 clog 日志回放速度慢导致弱一致性读工夫戳落后比拟多。

-- 用下面的 SQL(select 子句中只保留了 table_id、partition_id 字段)作为 in 条件的子查问
select * from __all_virtual_partition_replay_status
where (table_id, partition_idx) in (
  select a.table_id, a.partition_id
  from __all_virtual_table_mgr as a inner join __all_virtual_partition_info as b
  on a.table_id = b.table_id and a.partition_id = b.partition_idx and a.svr_ip = b.svr_ip
  where a.table_type = 0 
  and a.is_active = 0 
  and a.snapshot_version > least(least(b.min_trans_service_ts, b.min_replay_engine_ts), b.min_log_service_ts)
  and (a.table_id, a.partition_id) in (
    select table_id, partition_id from __all_virtual_tenant_memstore_allocator_info
    where svr_ip='10.186.17.106' and tenant_id=1001 and mt_is_frozen=1
  )
);

2.4 确认转储是否胜利?

如果 2.2、2.3 大节都没有查问到转储调度异样的 MemTable,接下来依据已解冻但未开释内存的 MemTable 的 pkey(table_id + partition_id) 到 observer.log 日志文件中查看转储过程的日志,以确认转储是否胜利。

  • 先查进去已解冻但未开释内存的 MemTable 的 pkey:
select table_id, partition_id from __all_virtual_tenant_memstore_allocator_info
where svr_ip='10.186.17.106' and tenant_id=1001 and mt_is_frozen=1
  • 依据 pkey(这里实际上只用了 table_id)到 observer.log 日志文件中查看转储过程的日志:
## 1100611139453778 是 table_id,须要替换成理论应用是的 table_id
grep "add dag success.*1100611139453778" observer.log
grep "task start process.*1100611139453778" observer.log
grep "task finish process.*1100611139453778" observer.log
grep "dag finish.*1100611139453778" observer.log

阐明:官网文档中这个步骤是依据 pkey 到 observer.log 中查找是否有相应的转储日志,然而实际上基本是查到不的,因为租户 memstore 占用内存达到 freeze_trigger_percentage 对应的内存下限时,是以租户为维度进行转储的,对于转储过程的日志,记录的是租户 ID,如下:

[403269][938][Y0-0000000000000000] [lt=28] [dc=0] add dag success(dag=0x7fffd6d0b5d0, start_time=1679040139626760, id=Y0-0000000000000000, *dag={this:0x7fffd6d0b5d0, type:7, name:"DAG_MAJOR_FINISH", id:Y0-0000000000000000, dag_ret:0, dag_status:1, start_time:1679040139626760, tenant_id:1}, dag->hash()=0, dag_cnt=1, dag_type_cnts=1)
[242340][416][Y59620ABA116A-0005F70FC566F959] [lt=40] [dc=0] task finish process(ret=0, start_time=1679023923679973, end_time=1679023923680479, runtime=506, *this={type:30, status:2, dag_:{this:0x7fffd6e7e800, type:7, name:"DAG_MAJOR_FINISH", id:Y59620ABA116A-0005F70FC566F959, dag_ret:0, dag_status:2, start_time:1679023923678832, tenant_id:1}})
[403017][456][Y59620ABA116A-0005F7134FC908DD] [lt=21] [dc=0] dag finished(*dag={this:0x7fffd6d0c020, type:7, name:"DAG_MAJOR_FINISH", id:Y59620ABA116A-0005F7134FC908DD, dag_ret:0, dag_status:3, start_time:1679040379627887, tenant_id:1}, runtime=1995, dag_cnt=0, dag_cnts_[dag->get_type()]=0)

所以,这一步实际上能够先跳过,暂且认为转储调度没有异样,就说明会转储胜利。

2.5 MemTable 援用计数是否失常?

如果确认了转储调度失常,转储过程也失常,然而已解冻的 MemTable 内存却没有开释,那再确认下是否因为 MemTable 的援用计数异样,导致内存无奈开释。

-- 留神:where 条件中 svr_ip、tenant_id 须要批改成理论场景对应的值
-- table_type = 0 示意 MEMTABLE
-- is_active = 0 示意 MEMTABLE 处于解冻状态,还未转储完结
-- 对于 is_active,参照官网文档:https://www.oceanbase.com/docs/enterprise-oceanbase-database-cn-10000000000364739
select * from __all_virtual_table_mgr as a
where a.table_type = 0 and a.is_active = 0 and a.write_ref > 0 and (a.table_id, a.partition_id) in (
  select table_id, partition_id from __all_virtual_tenant_memstore_allocator_info
  where svr_ip='10.186.17.106' and tenant_id=1001 and mt_is_frozen=1
);

失常状况下,转储过程实现之后,MemTable 的援用计数(write_ref)应该变为 0。

如果下面 SQL 查问到了 MemTable,阐明已实现解冻、转储过程的 MemTable 中,还存在援用计数大于 0 的 MemTable,那就阐明这些 MemTable 的援用计数异样,导致内存无奈开释。

三、遗留问题

  • 通过哪些关键字到 observer.log 文件中查看某个表的事务日志?
  • 为什么 MemTable 的弱一致性读工夫戳小于快照点(snapshot_version)会导致该 MemTable 转储调度异样?
  • OB 主动触发转储是按租户维度进行的,observer.log 中怎么查问单个表的转储过程日志?

正文完
 0