---
title: KVCache 排查手册-OceanBase数据库使用指南
description: 了解OceanBase数据库在实际应用中关于 KVCache 排查手册相关的常见问题和使用技巧，帮助您快速解决 KVCache 排查手册的难题。
---
切换语言

- 中文站 - 简体中文
- International - English
- 日本站 - 日本語

划线反馈

# KVCache 排查手册

更新时间：2024-05-20 03:46

适用版本： V2.1.x、V2.2.x、V3.1.x、V3.2.x、V4.0.x、V4.1.x、V4.2.x 内容类型：TechNote  

本文介绍 KVcache 相关问题的排查方法。

## KVCache 相关概念

在进行排查前，需要了解几个概念。

- pin

  一个 cache 块 ( memblock ) 被 pin 住，表示它正在被引用。

  cache 的由多个定长的块组成，每个块称为一个 memblock 。每个 memblock 中存放了多个 KV ，使用者通过 KV 指针来读取 KV 。容易理解，在使用指针的过程中，需要保证指针的安全，也即需要 pin 住存放 KV 的 memblock ，保证它不被释放。cache 内部通过引用计数来实现，cache 每向外吐出一个 KV 指针，都要对 KV 所在的 memblock 的引用计数加 1 ( pin 住这个 memblock ) ，当不再使用 KV 指针时，引用计数减 1 。也就是说，当引用计数大于 0 时，说明有人正在读其中的内存，那这个 memblock 就不能被释放，反之如果等于 0 ，则释放是安全的。

  简而言之，如果一个 memblock 被 pin 住，那么它不能被释放。
 - sync wash

  sync wash 指 cache 腾出自身内存给租户使用的过程。 从 1 可知，如果一个 memblock 没有被 pin ，那么它是可以被释放的。sync wash 就是 cache 找到没有被 pin 的 memblock ，释放内存，腾给租户使用。
 - cache 大小无上限

  cache 的大小在一个租户内是无上限的。 为了最大化利用租户内存，在租户内不限制 cache 的大小，理论上 cache 最大可以占满一个租户的内存。但是 cache 的内存比较特殊，在租户需要内存时，会触发 cache 的 sync wash ，腾出内存给租户使用。

## 常见问题

### ret=-4273 can not find enough memory block to wash

首先 4273 不是 cache 的问题，4273 报错表示在 sync wash 过程中发现 cache 中所有的 memblock 都被 pin 了，没有内存可以释放。 导致 4273 的原因可能有：

- cache 本身已经被榨干了，没有内存可以 wash ，所以就找不到不被 pin 的 memblock。
 - cache 本身还有内存，但是都被 pin 住了，无法 wash 出来；这个原因又分为两种情况：

     - 确实有需要较多 cache 的 SQL 在执行。
     - cache handle 引用计数有泄漏。

排查的思路就是逐步确认是哪个原因导致的 4273 ，步骤如下：

1. 查看当时 cache 的大小。

   ```
   * 通过 MEMORY 日志，memory 日志可以看到租户的 cache_hold ，这个字段记录了 cache 的总大小。
   * 通过 CACHE 日志，可以看到当时各个 cache 的大小。
   * 通过虚拟表 `__all_virtual_kvcache_info`，各版本都有，但是是实时数据，需要在案发时查询。

   ```

   如果此时 cache 大小很少，说明此时 cache 本身已经被榨干，wash 不出内存符合预期，应该查看当时租户的内存分布，看看其他 mod 是否符合预期，否则进入第二步。
 2. 判断是否存在泄漏。

   这一步是通过查看 cache 的大小能否降下来，来区分是 cache handle 引用计数有泄漏还是确实有需要较多 cache 的 SQL 在执行。

   对于 OceanBase 数据库 V4.0 及以后版本，可以停止所有查询后尝试手动 flush cache ，如果 cache 能降下来，则说明没有泄漏，是确实有需要较多 cache 的 SQL 在执行导致。

   手动 flush 需要直连到 OBServer 节点上执行，如果可以 flush 干净 ( 可能需要手动 flush 多次 ) ，则表示没有泄漏，对比 flush 前后 `__all_virtual_kvcache_info` 表中 size 的变化来判断。也可以通过查询 `__all_virtual_kvcache_store_memblock` 虚拟表来直接查看所有 memblock 引用计数，看是否有异常。

   ```sql
   select * from __all_virtual_kvcache_store_memblock where ... order by ref_count desc limit 10;

   ```

   对于 OceanBase 数据库 V4.0 之前版本，只能通过日志查看在 4273 报错后，cache 的大小是否降下去过，如果能降下去，则说明一定没有泄漏，如果没降下去过，则无法判断。( 因为之前版本手动 flush 并不会立即释放 cache 的内存，而是通过降低其访问热度，通过后台 wash 线程慢慢刷出去。 )

   如果是较多 cache 的 SQL 在执行导致，规避方案是，将 cache 用量较大的操作分散在租户内存压力较小的时候执行，如果仍有报错，尝试对租户内存进行扩容，增大内存。 如果是 cache handle 引用计数有泄漏导致，则需要复现问题，排查泄漏的路径。

### cache 占用内存较高

如前文所述，cache 在租户内是无上限的，所以理论上无论 cache 占多大内存，只要能被 sync wash ，就是符合预期的。 判断能否 sync wash 出来需要参考问题 1 中判断 cache 大小能否降下来的方法，能降下来就是能 sync wash 出来。

### cache 预热

OceanBase 数据库 V4.0 及以后的版本支持 cache 预热功能，之前版本没有此功能。 为缓解 compaction 后的性能抖动，在 compaction 时会将新生成的微块放入 cache 中，进行预热。 预热并不会将所有新生成的微块都预热进 cache ，而是根据租户的内存情况进行预热。现有策略下，data block cache 使用租户空闲内存的 5% ，index block 使用租户空闲内存的 2% 。 data block 和 index block 按照不同的优先级被预热进 cache ，按照中间层索引树的等级分配不同的优先级，越接近根节点的 block 优先级越高。

### wash

cache 腾出自身内存的过程称为 wash ，cache 的 wash 行为分为同步 wash 和异步 wash 两种。 同步 wash 就是上文提到的 sync wash，同步腾出内存，这里不再赘述。 异步 wash 由一个后台 wash 线程完成，wash 线程会定期地根据每个租户的内存压力 ( 包括租户大小、当前 cache 大小、租户当前空闲内存等 ) 计算出租户应该 wash 出的 cache size ，然后再根据每个 memblock 的访问热度从低到高 wash 。如果压力不大，计算结果可能是 0，不做 wash ，可以根据如下日志来判断，如果有则表示 wash 线程异步 wash 了 memblock。

```sql
COMMON_LOG(INFO, "Wash memory, ",
            "tenant_id", wash_iter->first,
            "cache_size", tenant_wash_info->cache_size_,
            "lower_mem_limit", tenant_wash_info->lower_limit_,
            "upper_mem_limit", tenant_wash_info->upper_limit_,
            "min_wash_size", tenant_wash_info->min_wash_size_,
            "max_wash_size", tenant_wash_info->max_wash_size_,
            "mem_usage", lib::get_tenant_memory_hold(wash_iter->first),
            "reserve_mem", static_cast<int64_t>((static_cast<double>(tenant_wash_info->upper_limit_)) * tenant_reserve_mem_ratio_),
            "wash_size", tenant_wash_info->wash_size_);

```

wash 线程的异步 wash 实际上是减去 memblock 原始的引用计数，等待引用计数减为 0 时执行释放，因此：

- 异步 wash 并不能立即释放 memblock ，需要等待不被 pin 。
 - 如果有引用计数泄漏泄漏，wash 线程一样不能 wash memblock。

### 手动 flush

手动 flush cache 表示手动清理指定 cache ， 命令如下，目前只能在 sys 租户下执行，需要直连要 flush 的 OBServer 节点。

```sql
alter system flush kvcache [tenant tenant_name [cache 'cache_name']];

```

cache_name 可以在 `__all_virtual_kvcache_info` 中查到，常用的 cache_name 有 ：user_block_cache、index_block_cache、user_row_cache、fuse_row_cache、bf_cache。

OceanBase 数据库 V4.0 及以后版本，flush 包含了立即清空的功能，预期情况下，flush 之后 cache 应该是立即被清空（若内存占用太多，flush 只清理一部分内存）。如果发生 4274 的报错，是 cache 较多导致的超时问题，再次 flush 即可。

OceanBase 数据库 V4.0 之前版本，手动 flush 只会清除指定 cache 中 KV 的索引，索引删除后，对应的 KV 就无法再被访问到，其所在的 memblock 的热度就会持续降低，等待 wash 线程将其 wash 出去。

#### 判断手动 flush 是否生效

无论新老版本，手动 flush 一定会清理掉全部的 kv_cnt ，可以观察 flush 前后 kv_cnt 是否清零过来判断手动 flush 是否生效。

```sql
select * from __all_virtual_kvcache_info where cache_name = '<cache_name>';

```

### 监控 cache handle ，排查引用计数泄漏

OceanBase 数据库 V4.0 及之后版本，如果怀疑或确认 cache 引用计数有泄漏，可以通过如下方法诊断。

1. binary 需要启用 ENABLE_DEBUG_LOG 编译选项。
 2. 打开监控，指定监控的 cache name 。

   ```sql
   alter system set leak_mod_to_check = 'cache_name';

   ```
 3. 查看泄漏 backtrace 。

   ```sql
   select * from __all_virtual_kvcache_handle_leak_info where tenant_id = tenant_id order by hold_count desc limit 10;

   ```

   在 OceanBase 数据库 V4.3 及以后版本虚拟表更名为 `__all_virtual_storage_leak_info`，并且新增配置项 `_storage_leak_check_mod` 用于指定泄漏监控的内容。

   这里简单介绍一下监控的实现方法。cache 对外吐出的引用计数都包含在 cache_handle 中，一个 cache_handle hold 住 1 个引用计数，所以在 cache 对外吐出 cache_handle 时，记录一条 backtrace ，在 cache_handle reset 时，消除记录。所以最后遗留下来未释放的 backtrace 很大可能就是泄漏的 backtrace 。

   根据记录方式可以知道，只要当前 cache 外部有 cache_handle ，那就会被记录 backtrace ，所以任何路径在持有 cache_handle 期间都会被记录，需要抓到持有时间过长或不符合预期的堆栈才是泄漏的堆栈。

   如果事先没有开启 cache handle 监控，可以通过 `__all_virtual_kvcache_store_memblock` 虚拟表简要确认一下问题，这张表会输出当前 server 上所有 memblock 的信息。 (OceanBase 数据库 V 4.3 及以后版本使用新的虚拟表)

   ```

   关注 `ref_count` 列，这一列表示 memblock 的当前的引用计数，目前当 memblock 没有被引用时，这里拿到的引用计数为 2 ( 初始引用计数为 1 ，查 memblock 信息时需要先加引用计数做保护，因此为 2 ) 。在确保某 memblock 当前不会被 pin 的前提下，ref_count 大于 2 的 memblock 都可以认为有引用计数泄漏。

### 泄漏检测扩展 (OceanBase 数据库 V4.3 及以后版本)

OceanBase 数据库 V4.3 及以后版本，新增存储层的泄漏检测功能，目前支持的检测内容有 cache handle、io handle、storage iter ，同时，新增配置项 `_storage_leak_check_mod` 用于配置泄漏检测的内容，有效值分别为 `cache_name / all_cache / io_handle / storage_iter` ，默认为空串，设定成空串或其他值时，关闭监控。

同时，`__all_virtual_kvcache_handle_leak_info` 虚拟表更名为 `__all_virtual_storage_leak_info`。

检测步骤使用新的配置和虚拟表即可：

1. binary 需要启用 ENABLE_DEBUG_LOG 编译选项。
 2. 打开监控，指定监控的内容。

   ```sql
   alter system set _storage_leak_check_mod = 'cache_name' | 'all_cache' | 'io_handle' | 'storage_iter' ;

   ```
 3. 查看泄漏 backtrace。

   ```sql
   select * from __all_virtual_storage_leak_info where ... order by hold_count desc limit 10;

   ```

上一篇

[因 IO 问题导致 SQL 执行报错 -4012 的原因及解决方法](https://www.oceanbase.com/knowledge-base/oceanbase-database-1000000002393210)

下一篇

[SSTable 数量太多的问题排查](https://www.oceanbase.com/knowledge-base/oceanbase-database-1000000000994204) ![有帮助](https://gw.alipayobjects.com/mdn/ob_asset/afts/img/A*y6ocSqN8cqsAAAAAAAAAAAAAARQnAQ)![无帮助](https://gw.alipayobjects.com/mdn/ob_asset/afts/img/A*BG9IQJyLHF8AAAAAAAAAAAAAARQnAQ)![反馈](https://gw.alipayobjects.com/mdn/ob_asset/afts/img/A*eTWdQKCRKHwAAAAAAAAAAAAAARQnAQ)[AI](https://www.oceanbase.com/obi) 咨询热线
