优化实例中的高内存消耗

一个常见问题是实例占用大量内存或内存不足 (OOM) 问题。运行内存利用率高的数据库实例通常会导致性能问题、停滞,甚至数据库停机。

某些 MySQL 内存块在全球范围内使用。这意味着所有查询工作负载共享内存位置,始终被占用,并且仅在 MySQL 进程停止时释放。某些内存块基于会话,这意味着一旦会话关闭,该会话使用的内存就会释放回系统。

每当 Cloud SQL for MySQL 实例的内存用量较高时,Cloud SQL 会建议您找出使用大量内存的查询或进程并释放内存。MySQL 内存消耗分为三个主要部分:

  • 线程和进程内存消耗
  • 缓冲区内存消耗
  • 缓存消耗

线程和进程内存消耗

每个用户会话都会消耗内存,具体取决于正在运行的查询、缓冲区或该会话使用的缓存,并且由 MySQL 的会话参数控制。主要参数包括:

  • thread_stack
  • net_buffer_length
  • read_buffer_size
  • read_rnd_buffer_size
  • sort_buffer_size
  • join_buffer_size
  • max_heap_table_size
  • tmp_table_size

如果在特定时间运行 N 个查询,则每个查询会在会话期间按照这些参数消耗内存。

缓冲区内存消耗

这部分内存在所有查询中都是通用的,由 Innodb_buffer_pool_sizeInnodb_log_buffer_sizekey_buffer_size 等参数控制。

缓存消耗

缓存包括查询缓存,用于保存查询及其结果,以便更快地检索相同后续查询的数据。它还包含 binlog 缓存,用于在事务运行时保存对二进制日志所做的更改,并由 binlog_cache_size 控制。

其他内存消耗

联接和排序操作也会使用内存。如果您的查询使用联接或排序操作,则这些查询将基于 join_buffer_sizesort_buffer_size 使用内存。

除此之外,如果启用了性能架构,它也会占用内存。如需按性能架构检查内存用量,请使用以下查询:

SELECT *
FROM
  performance_schema.memory_summary_global_by_event_name
WHERE EVENT_NAME LIKE 'memory/performance_schema/%';

MySQL 中有许多工具供您设置,以用于监控性能架构的内存用量。如需了解详情,请参阅 MySQL 文档

用于批量插入数据的 MyISAM 相关参数为 bulk_insert_buffer_size

如需了解 MySQL 如何使用内存,请参阅 MySQL 文档

建议

使用 Metrics Explorer 确定内存用量

您可以使用 Metrics Explorer 中的 database/memory/components.usage 指标查看实例的内存用量。

如果 database/memory/components.cachedatabase/memory/components.free 的内存总计不足 5%,则发生 OOM 事件的风险较高。如需监控内存用量并防止 OOM 事件,我们建议您设置提醒政策,并在 database/memory/components.usage 中将指标阈值条件设置为 95% 或更高。

下表显示了实例内存与建议的提醒阈值之间的关系:

实例内存 建议的提醒阈值
100 GB 及以下 95%
100 GB 到 200 GB 96%
200 GB 到 300 GB 97%
超过 300 GB 98%

计算内存消耗

计算 MySQL 数据库的内存使用量上限,以便为 MySQL 数据库选择适当的实例类型。使用以下公式:

MySQL 内存使用量上限 = innodb_buffer_pool_size + innodb_additional_mem_pool_size + innodb_log_buffer_size + tmp_table_size + key_buffer_size + ((read_buffer_size + read_rnd_buffer_size + sort_buffer_size + join_buffer_size) x max_connections)

以下是公式中使用的参数:

  • innodb_buffer_pool_size:缓冲池的大小(以字节为单位),即 InnoDB 缓存表和索引数据的内存区域。
  • innodb_additional_mem_pool_size:InnoDB 用于存储数据字典信息和其他内部数据结构的内存池的大小(以字节为单位)。
  • innodb_log_buffer_size:InnoDB 用于写入磁盘上的日志文件的缓冲区大小(以字节为单位)。
  • tmp_table_size:MEMORY 存储引擎和 TempTable 存储引擎(从 MySQL 8.0.28 开始)创建的内部内存临时表的大小上限。
  • Key_buffer_size:用于索引块的缓冲区大小。MyISAM 表的索引块已缓冲,并由所有线程共享。
  • Read_buffer_size:对 MyISAM 表执行依序扫描的每个线程都会为其扫描的每个表分配一个此大小(以字节为单位)的缓冲区。
  • Read_rnd_buffer_size:此变量用于 MyISAM 表读取、任何存储引擎以及多范围读取优化。
  • Sort_buffer_size:必须执行排序的每个会话分配一个该大小的缓冲区。sort_buffer_size 不特定于任何存储引擎,并且以一般方式应用于优化。
  • Join_buffer_size:用于普通索引扫描、范围索引扫描,以及不使用索引并因此执行全表扫描的联接的缓冲区的最小大小。
  • Max_connections:允许的最大并行客户端连接数。

排查高内存消耗问题

  • 运行 SHOW PROCESSLIST 可查看当前正在消耗内存的查询。它会显示所有连接的线程及其当前运行的 SQL 语句,并尝试对其进行优化。注意状态列和时长列。

    mysql> SHOW [FULL] PROCESSLIST;
    
    
  • 检查 BUFFER POOL AND MEMORY 部分中的 SHOW ENGINE INNODB STATUS 以查看当前缓冲区池和内存用量,这有助于您设置缓冲区池大小。

    mysql> SHOW ENGINE INNODB STATUS \G
    ----------------------
    BUFFER POOL AND MEMORY
    ----------------------
    Total memory allocated 398063986; in additional pool allocated 0
    Dictionary memory allocated 12056
    Buffer pool size 89129
    Free buffers 45671
    Database pages 1367
    Old database pages 0
    Modified db pages 0
    
  • 使用 MySQL 的 SHOW variables 命令检查计数器值,这会提供临时表数量、线程数、表缓存数、脏页数、打开表的数量和缓冲池用量等信息。

    mysql> SHOW variables like 'VARIABLE_NAME'
    

应用更改

分析不同组件的内存用量后,在 MySQL 数据库中设置适当的标志。如需更改 Cloud SQL for MySQL 实例中的标志,您可以使用 Google Cloud 控制台或 gcloud CLI。如需使用 Google Cloud 控制台更改标志值,请修改标志部分,选择标志,然后输入新值。

最后,如果内存使用率仍然很高,并且您认为正在运行的查询和标志值已经过优化,请考虑增加实例大小以避免 OOM。