# [Critical bug] Analyze may consume too much memory

💡  [https://github.com/pingcap/tidb/pull/28729](https://github.com/pingcap/tidb/pull/28729)

## Key Words

OOM, analyze version

## Affected Version

v5.0 ~ v5.3

## Issue

* Issue 现象

![heap.png](/images/TiDB/heap.png)

我们会在监控上看到 TiDB 的内存有一个很高的飙升。同时如果我们使用 go tool pprof 抓取内存 profile 的话，我们会看到 subMergeWorker 和 FromProto 占据了大量的内存。

* 相关 issue

    * ONCALL-3815

## Root Cause

* 问题原因

为了解决由于 Count-Min Sketch 的哈希冲突造成的各种估算误差(ONCALL-1582，TIDB-4347，ONCALL-1154，ONCALL-1674，ONCALL-1018，TIDB-2220，ONCALL-843，ONCALL-1143，TIDB-3575，ONCALL-642，ONCALL-979，TIDB-3345，ONCALL-921，TIDB-3151)，我们对统计信息的结构和收集方式做了实验性更改，增加了从 TiKV 读取的采样集的大小。当并发参数改动时，ANALYZE 命令会消耗比原来更多的内存，从而可能发生 OOM 风险。 统计信息的行为目前是由变量 tidb_analyze_version 控制。当其值为 1 时，TiDB 使用和以前的版本如 v4.0.\* 一样的统计信息结构和收集方式。当其值为 2 时，使用新的统计信息结构和收集方式。

一个针对为什么 auto analyze 需要首先 DROP STATS 的说明：

  auto analyze 的行为应该和上一次手动 analyze 保持一致。因此当一个表已经收集到了 version=2 的统计信息时，不管当前变量 tidb_analyze_version 的值是多少，它的 auto analyze 都会收集 version=2 的统计信息。

## Diagnostic Steps

N/A

## Resolution

## Fixed Version

* v5.3.0 及以上已经解决。
* V5.3 优化采样算法，在采样集大小不变的情况下，减小采样算法本身的开销。

## Workaround

* Workaround: 针对两种情况我们可以有两种解决办法

1. 如果只有 auto-analyze 存在
    1. 设置全局变量 tidb_analyze_version 为 1
       
        ```sql
          set @@global.tidb_analyze_version = 1;
        ```

    2. 使用如下的 SQL 生成 DROP STATS 的语句并执行。
     
        ```sql
          select distinct(concat('DROP STATS ',table_schema, '.', table_name,';')) from information_schema.tables, mysql.stats_histograms where stats_ver = 2 and table_id = tidb_table_id ;
        ```

     3. 等待 auto-analyze 重新收集
    
2. 如果 analyze 是手动收集的
    1. 设置全局变量 tidb_analyze_version 为 1

        ```sql
        set @@global.tidb_analyze_version = 1;
        ```

    2. 手动 analyze 每张需要的表。

        ```sql
        select distinct(concat('ANALYZE ',table_schema, '.', table_name,';')) from information_schema.tables, mysql.stats_histograms where stats_ver = 2 and table_id = tidb_table_id ;
        ```