tidb_enable_paging=ON的情况下tikv-client积压的RPC消息还是较大的疑惑

【 TiDB 使用环境】Poc
【 TiDB 版本】5.7.25-TiDB-v6.5.2
修改tidb_max_paging_size=256,tidb_distsql_scan_concurrency=1其余参数默认
这种情况下执行:select a.* from a;观察内存使用比预想的要大。
预想的内存占用:

为了模拟上层算子执行缓慢让distsql请求返回的RPC请求积压,这里直接修改了tidb-server的源代码,将explain analyze部分进行了延迟处理,让tikv-client的消息更容易积。
tidb/executor/explain.go at 29116c0256c52b224da2b34d712c1063d171c0ad · pingcap/tidb · GitHub
在上面这里的每一次循环获取结果集时候做time.Sleep(time.Second) 延迟处理。

让tikv返回的RPC消息进行堆积,在内存占用最大情况下(基本只有tikv-client堆积消息占用内存)为:tidb_distsql_scan_concurrency * tidb_max_paging_size * 表所有字段定义大小之和
我想内存占用不应该超过上面这个预想值,但是实际上内存占用却比这个要大挺多(感觉copTask请求执行完返回tikv-client缓存的数目远比tidb_distsql_scan_concurrency 大)。
具体场景:
表结构定义:

mysql> desc lineitem_bak;
+-----------------+---------------+------+------+---------+-------+
| Field           | Type          | Null | Key  | Default | Extra |
+-----------------+---------------+------+------+---------+-------+
| L_ORDERKEY      | bigint(20)    | NO   | PRI  | NULL    |       |
| L_PARTKEY       | bigint(20)    | NO   |      | NULL    |       |
| L_SUPPKEY       | bigint(20)    | NO   |      | NULL    |       |
| L_LINENUMBER    | bigint(20)    | NO   | PRI  | NULL    |       |
| L_QUANTITY      | decimal(15,2) | NO   |      | NULL    |       |
| L_EXTENDEDPRICE | decimal(15,2) | NO   |      | NULL    |       |
| L_DISCOUNT      | decimal(15,2) | NO   |      | NULL    |       |
| L_TAX           | decimal(15,2) | NO   |      | NULL    |       |
| L_RETURNFLAG    | char(1)       | NO   |      | NULL    |       |
| L_LINESTATUS    | char(1)       | NO   |      | NULL    |       |
| L_SHIPDATE      | date          | NO   | MUL  | NULL    |       |
| L_COMMITDATE    | date          | NO   |      | NULL    |       |
| L_RECEIPTDATE   | date          | NO   |      | NULL    |       |
| L_SHIPINSTRUCT  | char(25)      | NO   |      | NULL    |       |
| L_SHIPMODE      | char(10)      | NO   |      | NULL    |       |
| L_COMMENT       | varchar(44)   | NO   |      | NULL    |       |
| col1            | decimal(15,2) | YES  |      | NULL    |       |
| col2            | decimal(15,2) | YES  |      | NULL    |       |
| col3            | decimal(15,2) | YES  |      | NULL    |       |
| col4            | decimal(15,2) | YES  |      | NULL    |       |
| col5            | decimal(15,2) | YES  |      | NULL    |       |
| col6            | decimal(15,2) | YES  |      | NULL    |       |
| col7            | decimal(15,2) | YES  |      | NULL    |       |
| col8            | decimal(15,2) | YES  |      | NULL    |       |
| col9            | decimal(15,2) | YES  |      | NULL    |       |
| col10           | decimal(15,2) | YES  |      | NULL    |       |
| col11           | decimal(15,2) | YES  |      | NULL    |       |
| col12           | decimal(15,2) | YES  |      | NULL    |       |
| col13           | decimal(15,2) | YES  |      | NULL    |       |
| col14           | decimal(15,2) | YES  |      | NULL    |       |
| col15           | decimal(15,2) | YES  |      | NULL    |       |
| col16           | decimal(15,2) | YES  |      | NULL    |       |
| col17           | decimal(15,2) | YES  |      | NULL    |       |
| col18           | decimal(15,2) | YES  |      | NULL    |       |
| col19           | decimal(15,2) | YES  |      | NULL    |       |
| col20           | decimal(15,2) | YES  |      | NULL    |       |
| col21           | decimal(15,2) | YES  |      | NULL    |       |
| col22           | decimal(15,2) | YES  |      | NULL    |       |
| col23           | decimal(15,2) | YES  |      | NULL    |       |
| col24           | decimal(15,2) | YES  |      | NULL    |       |
| col25           | decimal(15,2) | YES  |      | NULL    |       |
| col26           | decimal(15,2) | YES  |      | NULL    |       |
| col27           | decimal(15,2) | YES  |      | NULL    |       |
| col28           | decimal(15,2) | YES  |      | NULL    |       |
| col29           | decimal(15,2) | YES  |      | NULL    |       |
| col30           | decimal(15,2) | YES  |      | NULL    |       |
| col31           | decimal(15,2) | YES  |      | NULL    |       |
| col32           | decimal(15,2) | YES  |      | NULL    |       |
| col33           | decimal(15,2) | YES  |      | NULL    |       |
| col34           | decimal(15,2) | YES  |      | NULL    |       |
| col35           | decimal(15,2) | YES  |      | NULL    |       |
| col36           | decimal(15,2) | YES  |      | NULL    |       |
| col37           | decimal(15,2) | YES  |      | NULL    |       |
| col38           | decimal(15,2) | YES  |      | NULL    |       |
| col39           | decimal(15,2) | YES  |      | NULL    |       |
| col40           | decimal(15,2) | YES  |      | NULL    |       |
| col41           | decimal(15,2) | YES  |      | NULL    |       |
| col42           | decimal(15,2) | YES  |      | NULL    |       |
| col43           | decimal(15,2) | YES  |      | NULL    |       |
| col44           | decimal(15,2) | YES  |      | NULL    |       |
| col45           | decimal(15,2) | YES  |      | NULL    |       |
| col46           | decimal(15,2) | YES  |      | NULL    |       |
| col47           | decimal(15,2) | YES  |      | NULL    |       |
| col48           | decimal(15,2) | YES  |      | NULL    |       |
| col49           | decimal(15,2) | YES  |      | NULL    |       |
| col50           | decimal(15,2) | YES  |      | NULL    |       |
+-----------------+---------------+------+------+---------+-------+
66 rows in set (0.00 sec)

表数据量和语句执行计划:

mysql> select count(*) from lineitem_bak;
+-----------+
| count(*)  |
+-----------+
| 179998372 |
+-----------+
1 row in set (16.14 sec)

mysql> explain select * from lineitem_bak;
+-----------------------+--------------+-----------+--------------------+----------------------+
| id                    | estRows      | task      | access object      | operator info        |
+-----------------------+--------------+-----------+--------------------+----------------------+
| TableReader_5         | 179998372.00 | root      |                    | data:TableFullScan_4 |
| └─TableFullScan_4     | 179998372.00 | cop[tikv] | table:lineitem_bak | keep order:false     |
+-----------------------+--------------+-----------+--------------------+----------------------+
2 rows in set (0.00 sec)
--设置参数:
mysql> set tidb_distsql_scan_concurrency=1;
Query OK, 0 rows affected (0.00 sec)

mysql> set tidb_max_paging_size=256;
Query OK, 0 rows affected (0.00 sec)


--发起语句执行:
mysql> explain analyze select * from lineitem_bak;

等待至少20秒,让tikv数据充分返回给tidb-server进行积压,观察processlist情况:

mysql> select * from information_schema.processlist\G
*************************** 1. row ***************************
      ID: 4050963675563950485
    USER: root
    HOST: 192.168.31.200:42902
      DB: tpch
 COMMAND: Query
    TIME: 120
   STATE: autocommit
    INFO: explain analyze select * from lineitem_bak
  DIGEST: 474e5e529400b26f8147ce06f488416d9e5f948c01deb9f0244de806fd5438bf
     MEM: 231428128
    DISK: 0
TxnStart: 06-11 10:03:22.584(442092487333380097)
*************************** 2. row ***************************
      ID: 4050963675563950487
    USER: root
    HOST: 192.168.31.200:49006
      DB: tpch
 COMMAND: Query
    TIME: 0
   STATE: autocommit
    INFO: select * from information_schema.processlist
  DIGEST: 4b5e7cdd5d3ed84d6c1a6d56403a3d512554b534313caf296268abdec1c9ea99
     MEM: 0
    DISK: 0
TxnStart: 
2 rows in set (0.00 sec)

因为lineitem_bak表一共66个字段,每一个字段占用的内存最大不超过L_COMMENT varchar(44),所以每一行记录最大不会超过66 * 44 = 2904字节,加上内存操作其它占位符等平均每行内存使用不会超过3000字节。
预计内存整体占用为:
tidb_distsql_scan_concurrency * tidb_max_paging_size * 表所有字段定义大小之和 = 1 * 256 * 3000 = 768000字节 约为(750KB)。
但是实际上内存占用了231428128字节约为(230MB),远远大于预估的内存量
heap信息:

所以tidb_enable_paging=ON的情况下内存占用为何还是较大呢?
是否除tidb_distsql_scan_concurrency之外还有其它并发条件?

1 个赞

内存放大的许多因素:

  1. chunk 编码会导致数据放大,不同类型放大倍数不一样,其实 decimal 是放大比例很高的
  2. RPC 消息读取之后,是一整块的内存,包含了不少 chunk,然后释放机制是,只有里面所有 chunk 消费完之后,这整块内存才释放.(比如说有 100 个 chunk,哪怕其中 99 个消费掉了,还有一个 chunk 被引用着,则整块内存无法释放)
  3. tidb_distsql_scan_concurrency这个参数,并发是一块,实际上还带了 channel 的,数据并发读上来并不是立刻消费,而是往 channel 写,由消费者去读 channel 消费. 由于 channel 的存在,实际的内存占用就不仅受并发控制,还有 channel size 的影响
  4. 除了读数据这一层的并发,有部分算子的处理是带内部并发的,这些算子内部并发"正在消费"中的 chunk 也会成为内存放大的因素

然后是关于放大倍数的

https://github.com/pingcap/tidb/issues/35633 这里是 coprocessor paging 的开发记录
其中文档 https://pingcap.feishu.cn/docs/doccnPKxuP3LiOBI5HaSPO4Idtc 有部分的数据

chunk vs 非chunk 大小对比
发现 decimal 放大最严重:2603116 vs 10410316
bigint not null 的时候,chunk 要优于非 chunk,占用更小的包大小: 2342962 vs 2084880
varchar(150) 的时候,chunk 占用的比非 chunk 略高:5205162 vs 6770230
varchar(1) 的时候,chunk 是非 chunk 的近 3 倍:782036 vs 2347106

此话题已在最后回复的 60 天后被自动关闭。不再允许新回复。