某些特殊场景下MEMORY_QUOTA参数不生效的问题

【 TiDB 使用环境】
v5.1.2

【概述】 场景 + 问题概述

我正在测试一条SQL,在测试过程中,参数为:
/*+ STREAM_AGG(), MERGE_JOIN(xxx, xxx), MEMORY_QUOTA(2000 MB) */
其中STREAM_AGG(), MERGE_JOIN(xxx, xxx)均已生效,然而MEMORY_QUOTA未生效

伪SQL如下

select studentId, sum(x), sum(y), sum(z)
from
    student_tab
    right join
    (select studentid from mmm where scoref between a and b)
on studentid = studentid
where score between a and b
group by studentid

student_tab表有userid这个索引,表的数据量在TB级别,student_tab开启了TiFlash,mmm表未开启。

EXPLAIN执行计划如下(脱敏只截取了大致计划)
从执行计划可以看到,由于studentid具备索引,所以IndexFullScan_76就选择了该索引来进行build。

+------------------------------------------+--------------+
| id                                       | task         |
+------------------------------------------+--------------+
| Projection_69                            | root         |
| └─StreamAgg_73                           | root         |
|   └─Projection_86                        | root         |
|     └─MergeJoin_74                       | root         |
|       ├─Sort_84(Build)                   | root         |
|       │ └─Selection_82                   | root         |
|       │   └─CTEFullScan_83               | root         |
|       └─Projection_80(Probe)             | root         |
|         └─IndexLookUp_79                 | root         |
|           ├─IndexFullScan_76(Build)      | cop[tikv]    |
|           └─Selection_78(Probe)          | cop[tikv]    |
|             └─TableRowIDScan_77          | cop[tikv]    |
+------------------------------------------+--------------+

在运行过程中,发现TIDB内存消耗并未被限制在2000MB,而是持续增长,已经增长到接近100GB,然后发现情况不对手动kill了该条查询。

后续指定其走tiflash发现,sort和join,agg阶段几乎不消耗资源,因为经过筛选后的数据只有几百条。

又分析了上图下执行计划,发现是IndexFullScan,怀疑是索引导致的问题。查了下该索引大小为上百GB

目前怀疑在查询时,tidb/sql节点尝试将整个索引加载到内存。而MEMORY_QUOTA参数,并不能限制索引的资源消耗,所以导致tidb/sql节点消耗的内存资源持续上涨,不受MEMORY_QUOTA限制

1赞

IndexLookUp_79的执行过程是:IndexFullScan 根据条件过滤符合条件的记录,然后返回给tidb server,tidb server根据返回的索引信息去tikv上回表查数据─TableRowIDScan。join走tikv时需要tikv返回数据给tidb后才行,tiflash支持mpp功能,可以在tiflash侧完成join后返回所需数据。

1赞

也就是说,索引过滤其实是走在tikv节点上?并不会尝试把索引加载到sql节点内存里?
那这样的话,说不通sql节点内存占用异常增长的问题。

mmm表未开启tiflash,所以join必然是构建在sql节点上

1赞

先去tikv获取符合条件的索引记录, 索引记录返回tidb, 数据量大需要内存就多, tidb根据索引记录构建回表task再次访问tikv

1赞

这个索引记录占用的内存,似乎并不能被MEMORY_QUOTA参数限制?

1赞

一般来说,都是写左连接的SQL,student_tab上的studentid字段上有索引,但最好studentid字段是主键的一部分。我不知道您的表结构如何设计的,但设计主键的时候,还是以业务需求来设计,不要弄一个没有意义的自增字段或uuid作为主键。

目前的进展

根据pprof分析heap,根据inuse_space信息判断,发现很多未释放的内存积压在resp.detail上(MEMORY_QUOTA限制的3000MB)

结合tidb代码发现,detail这部分的内存占用并未统计,而是只统计了个空结构体的常量,里面其实很多map都没统计进去,所以这部分内存占用加起来即使超出了MEMORY_QUOTA的限制,也是无感知的,导致MEMORY_QUOTA参数失效

%E5%9B%BE%E7%89%87

%E5%9B%BE%E7%89%87

至于为何这种基本的统计信息会占这么多内存,还在继续分析中

  1. 麻烦反馈下表结构和完整的查询 SQL
  2. 麻烦导出 SQL中需要用到的表的统计信息,json 文件。
    https://docs.pingcap.com/zh/tidb/stable/statistics#导出统计信息