GreatSQL内存消耗异常排查攻略:从系统到应用层面的深入分析

GreatSQL内存消耗异常排查攻略:从系统到应用层面的深入分析

当 GreatSQL 数据库处于高并发高负载时,可能会发现 mysqld 进程的内存消耗远远超出设置的 innodb_buffer_pool_size 时,有时候甚至会高达甚至超过系统内存的90%,遇到这种问题时,心里经常会发慌,担心下一秒内存就会爆了发生 OOM,或者数据库hang死不响应。

本文和大家试着使用 GreatSQL 中的 sys schemaperformance_schema 进行深入分析,找出内存消耗大户的源头,并尽可能解决问题。

下面是详细的排查方法和步骤。

1. 确认实际内存消耗

1.1 操作系统层面分析

先检查确认 mysqld 进程的内存具体消耗占用情况,做到心里有数,避免真的下一秒就发生 OOM 的问题:

$ free -ht
free -ht
              total        used        free      shared  buff/cache   available
Mem:           30Gi        28Gi       240Mi        33Mi       2.0Gi       1.7Gi
Swap:            0B          0B          0B
Total:         30Gi        28Gi       240Mi

$ ps aux | grep mysqld
mysql      51931 23.0 89.8 32100800 29008060 ?   Ssl  Nov22 949:41 /data/apps/GreatSQL-8.0.32-26-Linux-glibc2.28-x86_64/bin/mysqld

$ top -p $(pidof mysqld) -n 1
top - 05:36:37 up 4 days,  4:06,  1 user,  load average: 5.56, 8.70, 10.87
Tasks:   1 total,   0 running,   1 sleeping,   0 stopped,   0 zombie
%Cpu(s):  8.4 us,  1.7 sy,  0.0 ni, 86.6 id,  3.4 wa,  0.0 hi,  0.0 si,  0.0 st
MiB Mem :  31553.3 total,    265.6 free,  29148.6 used,   2139.2 buff/cache
MiB Swap:      0.0 total,      0.0 free,      0.0 used.   1903.3 avail Mem

    PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
  51931 mysql     20   0   30.6g  27.7g   4656 S  80.0  89.8 949:51.99 mysqld

在上述结果中重点关注几个指标:

  • RES(Resident Memory):物理内存占用,约 27.7G。
  • VIRT(Virtual Memory):虚拟地址空间大小,约 30.6G。

看到 mysqld 进程当前内存消耗占比约 90%,还算可控,没到火烧眉毛的境地。

继续使用 pmap 查看 mysqld 进程中的内存分布情况:

$ pmap -x $(pidof mysqld) | sort -k3 -rn | head -n 20

total kB         32100804 29029796 29016940
00007f8a484d8000 5368992 5361664 5361664 rw---   [ anon ]
00007f892533d000 4698892 4691952 4691952 rw---   [ anon ]
00007f87e961e000 4564872 4556784 4556784 rw---   [ anon ]
00007f86adbe0000 4296832 4290544 4290544 rw---   [ anon ]
00007f86298bb000 1023252 1015792 1015792 rw---   [ anon ]
00007f866c350000  979648  979632  979632 rw---   [ anon ]
00007f87b8112000  719800  712688  712688 rw---   [ anon ]
00007f89046d4000  451760  444400  444400 rw---   [ anon ]
0000000005afc000  286800  282640  282640 rw---   [ anon ]
00007f8ba7715000  200300  200276  200276 rw---   [ anon ]
00007f8578000000  131072  131072  131072 rw---   [ anon ]
00007f8570000000  131072  131072  131072 rw---   [ anon ]
00007f8568000000  131072  131072  131072 rw---   [ anon ]
00007f8560000000  131072  131072  131072 rw---   [ anon ]
00007f8558000000  131072  131072  131072 rw---   [ anon ]
00007f8550000000  131048  131048  131048 rw---   [ anon ]
00007f8438000000  130668  130668  130668 rw---   [ anon ]
00007f8b98000000   65536   65536   65536 rw---   [ anon ]
00007f8b94000000   65536   65536   65536 rw---   [ anon ]

看到大量的匿名内存(anon)消耗较多内存,这可能是由以下几个原因引起的:

  1. 动态分配的内存

    • GreatSQL 在运行过程中会频繁地进行内存分配和释放,这些内存通常是以匿名映射的形式存在于进程的虚拟地址空间中。
    • 例如,GreatSQL 的线程池、缓存、临时表等都会动态分配内存。
  2. 缓冲区和缓存

    • GreatSQL 使用大量的缓冲区和缓存来提高性能,例如InnoDB Buffer Pool(以下简称 IBP)、InnoDB Log Buffer等。
    • 这些缓冲区和缓存通常会占用大量的匿名内存。
  3. 线程堆栈

    • 每个线程都有自己的堆栈空间,这些堆栈空间也是匿名内存的一部分。
    • 为了响应用户请求而创建了大量线程,那么这些线程的堆栈空间会占用不少内存。
  4. 临时文件

    • GreatSQL 在处理大查询或排序操作时,可能会使用临时表、临时文件,其内存映射也会占用匿名内存。
  5. 内存泄漏

    • 如果 GreatSQL 存在内存泄漏问题,也会导致匿名内存不断增加。

可以针对上述各个模块/维度做进一步排查分析。

1.2 检查 IBP 内存相关配置

  • 使用以下 SQL 命令查询 IBP 等内存消耗较多的相关模块内存配置
greatsql> SHOW GLOBAL VARIABLES LIKE 'innodb_buffer_pool_size';
+-------------------------+-------------+
| Variable_name           | Value       |
+-------------------------+-------------+
| innodb_buffer_pool_size | 21474836480 |
+-------------------------+-------------+

greatsql> SHOW GLOBAL VARIABLES LIKE 'innodb_log_buffer_size';
+------------------------+----------+
| Variable_name          | Value    |
+------------------------+----------+
| innodb_log_buffer_size | 33554432 |
+------------------------+----------+

从上面可见 IBP 设置为 20G,但是 mysqld 进程的内存占用为 27.7G,超过 IBP 较多,这可能是由于用户的 SQL 请求(比如效率较低的慢查询 SQL)其他模块或线程引起。还需要继续排查。

2. 利用 Performance Schema 排查内存消耗来源

从 5.6.6 版本开始,Performance Schema 默认启用,是一个内置的性能诊断工具,用于实时监控和分析 GreatSQL 服务器的运行状态。它提供了详细的性能数据,包括 内存分配的全局视图、SQL 语句的执行时间、线程活动、锁等待等详细信息,帮助开发者和 DBA 识别和解决性能瓶颈。

2.1 按内存模块查看占用

使用 memory_summary_global_by_event_name 按模块查看内存分配情况:

greatsql> USE performance_schema;
greatsql> SELECT
  EVENT_NAME,
  CURRENT_NUMBER_OF_BYTES_USED AS memory_bytes,
  CURRENT_NUMBER_OF_BYTES_USED / 1024 / 1024 AS memory_mb
FROM
  performance_schema.memory_summary_global_by_event_name
WHERE
  CURRENT_NUMBER_OF_BYTES_USED > 0
ORDER BY
  CURRENT_NUMBER_OF_BYTES_USED DESC
LIMIT 10;
+--------------------------------------------------------------------+--------------+----------------+
| EVENT_NAME                                                         | memory_bytes | memory_mb      |
+--------------------------------------------------------------------+--------------+----------------+
| memory/innodb/buf_buf_pool                                         |  21957836800 | 20940.62500000 |
| memory/group_rpl/GCS_XCom::xcom_cache                              |   1070853221 |  1021.24521351 |
| memory/mysys/IO_CACHE                                              |     84149952 |    80.25164795 |
| memory/performance_schema/events_statements_summary_by_digest      |     42240000 |    40.28320313 |
| memory/innodb/log_buffer_memory                                    |     33555440 |    32.00096130 |
| memory/innodb/ut0link_buf                                          |     25165888 |    24.00006104 |
| memory/innodb/lock0lock                                            |     22440096 |    21.40054321 |
| memory/sql/TABLE                                                   |     15646883 |    14.92203045 |
| memory/performance_schema/events_statements_history_long           |     15040000 |    14.34326172 |
| memory/performance_schema/events_errors_summary_by_thread_by_error |     14561280 |    13.88671875 |
+--------------------------------------------------------------------+--------------+----------------+
10 rows in set (0.00 sec)  
  • EVENT_NAME:具体内存分配的模块名称,如 memory/innodb/buffer_poolmemory/sql/temporary_table 等。
  • CURRENT_NUMBER_OF_BYTES_USED:当前分配的内存总量。

例如:

  • 如果 memory/innodb/buf_buf_pool 值较高,说明 InnoDB buffer pool 占用较高。其他几个包含 memory/innodb 关键字的,也都是和 InnoDB 存储引擎相关的内存模块。

  • 其中 memory/mysys/MY_BITMAP::bitmap 主要用于管理位图数据结构 (bitmap) 的内存使用。它的设计初衷是为了实现高效的位存储与处理,主要用于存储和操作需要标志位(bit flag)来跟踪或控制的数据。其具体存储的数据包括但不限于:

    • 索引或表分区的状态:位图被用来记录索引或分区的使用状态。例如,在分区表扫描时,通过位图可以高效管理哪些分区需要扫描或已经扫描。
    • 事务或锁状态:记录事务的特定标志位或锁的使用状态,比如资源锁(resource locks)的分配状态。
    • InnoDB 的内部操作:位图被用于跟踪一些内部存储引擎的优化过程,例如自适应哈希索引、页的脏位(dirty bit)标记等。
    • 线程管理:管理线程池中线程的分配和使用状态。
    • 性能统计:在某些性能分析的场景下,位图用于记录启用或禁用的统计模块。
  • 其中 memory/group_rpl/GCS_XCom::xcom_cache 是 MGR Xcom cache,在 GreatSQL 8.0.32-26 中初始默认值即为 1GB,详情参考 Xcom cache分配静态化。其他几个包含 group_rpl 关键字特征的,也是和 MGR 相关的模块。

  • 其中 memory/mysys/IO_CACHE 是一个重要的内存管理模块,主要用于管理和优化文件I/O操作。IO_CACHE 提供了一个高效的缓存机制,可以显著提高文件读写操作的性能。主要存储的数据有:

    • 临时文件数据:排序、分组、联接等操作过程中生成的中间结果。
    • 二进制日志:binlog 的写入和读取操作中使用缓存。
    • 文件块:GreatSQL 访问文件时,将数据块加载到缓存中,避免重复读取。
    • 表数据:表扫描或索引扫描时,用于缓存表或索引的数据。
  • 如果 memory/sql/temporary_table 值较高,说明内存被临时表消耗。

  • 如果 memory/innodb/hash_index 值较高,可能是 InnoDB 的自适应哈希索引占用内存。

上面的查询结果表明,memory/innodb/buf_buf_pool(IBP) 占用内存约 20G,memory/group_rpl/GCS_XCom::xcom_cache(MGR XCom Cache) 占用内存约 1G,都是符合预期的。但是 memory/mysys/IO_CACHE 占用的内存较高,需要重点排查。

2.2 跟踪各模块内存使用变化

可以每间隔一段时间重复执行下面的 SQL 请求,观察各个模块的内存消耗变化,找出内存消耗增长较快的模块,它们可能就是导致 mysqld 进程消耗较大内存的“元凶”。

greatsql> USE performance_schema;
greatsql> SELECT
  EVENT_NAME,
  SUM(SUM_NUMBER_OF_BYTES_ALLOC) / 1024 /
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值