Google Cloud 的 MySQL 適用的 Cloud SQL 是全代管資料庫服務,可協助您設定、維護及管理 Google Cloud Platform 中的 MySQL、PostgreSQL 或 SQL Server 資料庫。Cloud SQL 會在機群中執行大量 MySQL 資料庫,我們發現部分 MySQL 執行個體常出現因記憶體不足 (OOM) 錯誤而耗用大量記憶體及當機的情形。本文將協助您解決 MySQL 記憶體問題。
如果程序嘗試分配記憶體,但因伺服器沒有可用的記憶體而失敗,就會發生記憶體不足 (OOM) 的狀況。這通常會導致 Linux OOM 終止程序 (在系統的記憶體容量嚴重不足時,Linux 核心所採用的處理程序) 終止資料庫程序。
不適合因為 OOM 狀況而造成資料庫當機的情況,因為資料庫程序是使用 SIGKILL 指令終止,會讓應用程式沒有機會正常關閉 MySQL。這會導致以下問題:
MySQL 從 5.7 版開始新增 performance_schema 的記憶體檢測作業,有助於瞭解 MySQL 的記憶體配置。本文將協助您透過 performance_schema 來解決 MySQL 記憶體問題。
每個 Cloud SQL 執行個體都是由在代管 Google Cloud 伺服器上運作的虛擬機器 (VM) 提供。每個 VM 都會執行資料庫程式,例如 MySQL 伺服器和提供支援服務 (如記錄與監控) 的服務代理程式。Cloud SQL 會為作業系統 (OS) 和服務代理程式保留部分記憶體,並為資料庫程式 (例如 mysqld) 分配剩餘的記憶體。
下表顯示 mysqld 程序的記憶體配置,以及每個機型的預設 InnoDB 緩衝區集區設定。
機器類型 | 執行個體 RAM GIB (位元組) | mysqld 記憶體 (GB) | InnoDB 緩衝區集區 GiB (位元組) | 緩衝區集區 (執行個體 RAM 的百分比) | 緩衝區集區 (mysqld 記憶體的百分比) |
db-g1-small | 1.7(1825361101) | 1.11 | 0.625(671088640) | 37% | 56% |
db-custom-1-3840 | 3.75(4026531840) | 2.97 | 1.375(1476395008) | 37% | 46% |
db-custom-2-7680 | 7.5(8053063680) | 6.72 | 4(4294967296) | 53% | 60% |
db-custom-2-13312 | 13(13958643712) | 12 | 9(9663676416) | 69% | 75% |
db-custom-4-15360 | 15(16106127360) | 14 | 10.5(11274289152) | 70% | 76% |
db-custom-4-26624 | 26(27917287424) | 24 | 19(20401094656) | 73% | 79% |
db-custom-8-30720 | 30(32212254720) | 28 | 22(23622320128) | 73% | 80% |
db-custom-8-53248 | 52(55834574848) | 48 | 38(40802189312) | 73% | 79% |
db-custom-16-61440 | 60(64424509440) | 55 | 44(47244640256) | 73% | 80% |
db-custom-16-106496 | 104(111669149696) | 96 | 75(80530636800) | 72% | 78% |
db-custom-32-122880 | 120(128849018880) | 110 | 87(93415538688) | 73% | 79% |
db-custom-32-212992 | 208(223338299392) | 191 | 150(161061273600) | 72% | 78% |
db-custom-64-245760 | 240(257698037760) | 221 | 173(185757335552) | 72% | 78% |
db-custom-96-368640 | 360(386547056640) | 331 | 260(279172874240) | 72% | 79% |
db-custom-64-425984 | 416(446676598784) | 383 | 300(322122547200) | 72% | 78% |
db-custom-96-638976 | 624(670014898176) | 574 | 450(483183820800) | 72% | 78% |
機器類型
執行個體 RAM GIB (位元組)
mysqld 記憶體 (GB)
InnoDB 緩衝區集區 GiB (位元組)
緩衝區集區 (執行個體 RAM 的百分比)
緩衝區集區 (mysqld 記憶體的百分比)
db-g1-small
1.7(1825361101)
1.11
0.625(671088640)
37%
56%
db-custom-1-3840
3.75(4026531840)
2.97
1.375(1476395008)
37%
46%
db-custom-2-7680
7.5(8053063680)
6.72
4(4294967296)
53%
60%
db-custom-2-13312
13(13958643712)
12
9(9663676416)
69%
75%
db-custom-4-15360
15(16106127360)
14
10.5(11274289152)
70%
76%
db-custom-4-26624
26(27917287424)
24
19(20401094656)
73%
79%
db-custom-8-30720
30(32212254720)
28
22(23622320128)
73%
80%
db-custom-8-53248
52(55834574848)
48
38(40802189312)
73%
79%
db-custom-16-61440
60(64424509440)
55
44(47244640256)
73%
80%
db-custom-16-106496
104(111669149696)
96
75(80530636800)
72%
78%
db-custom-32-122880
120(128849018880)
110
87(93415538688)
73%
79%
db-custom-32-212992
208(223338299392)
191
150(161061273600)
72%
78%
db-custom-64-245760
240(257698037760)
221
173(185757335552)
72%
78%
db-custom-96-368640
360(386547056640)
331
260(279172874240)
72%
79%
db-custom-64-425984
416(446676598784)
383
300(322122547200)
72%
78%
db-custom-96-638976
624(670014898176)
574
450(483183820800)
72%
78%
如果執行個體擁有 10 GB 以上的 RAM,則 mysqld 程序僅限於使用 92% 的執行個體 RAM,而 InnoDB 緩衝區集區則是設定在 mysqld 程序可用記憶體範圍的 75-80% 範圍內。除了 InnoDB 緩衝區集區,MySQL 也會分配幾個緩衝區和快取,藉此提高效能。如要瞭解 MySQL 中的記憶體配置,請參閱這篇文章。
當 mysqld 記憶體用量成長超過 mysqld 程序的設定限制時,Linux OOM 終止程序會終止 mysqld 程序,因為該程序是執行個體中最大的記憶體消費者。
performance_schema 是一項 MySQL 功能,能夠以低階監控伺服器執行作業。MySQL 適用的 Cloud SQL 允許在 RAM 大小為 3 GB 以上的執行個體中,啟用 performance_schema。從 MySQL 8.0 版開始,至少有 15 GB RAM 的執行個體預設會啟用這項功能。
MySQL 5.6 和 5.7 版預設會停用 performance_schema,如要啟用,請使用資料庫旗標。
啟用 performance_schema 會導致效能負擔,且會影響記憶體。如要瞭解其記憶體配置,請參閱效能結構定義記憶體配置模型。
performance_schema 記憶體檢測功能可協助監控 MySQL 的記憶體用量。記憶體檢測的名稱格式為 memory/code_area/instrument_name,其中 code_area 是如 sql 或 Innodb 等值,而 instrument_name 則是檢測的詳細資料。
MySQL 5.7 預設會啟用部分的記憶體檢測,但並非全部。如要啟用這些檢測,請更新 performance_schema.setup_instruments 資料表的「已啟用」欄。
mysql> select version();
+-------------------+
| version() |
+-------------------+
| 5.7.39-google-log |
+-------------------+
1 row in set (0.00 sec)
mysql> SELECT enabled, count(*) FROM performance_schema.setup_instruments WHERE NAME LIKE '%memory%' group by enabled;
+---------+----------+
| enabled | count(*) |
+---------+----------+
| YES | 70 |
| NO | 306 |
+---------+----------+
資料集中的 2 列 (0.00 秒)
mysql> UPDATE performance_schema.setup_instruments SET ENABLED = 'YES' WHERE NAME LIKE 'memory/%';
Query OK, 306 rows affected (0.00 sec)
Rows matched: 376 Changed: 306 Warnings: 0
mysql> SELECT enabled, count(*) FROM performance_schema.setup_instruments WHERE NAME LIKE '%memory%' group by enabled;
+---------+----------+
| enabled | count(*) |
+---------+----------+
| YES | 376 |
+---------+----------+
1 row in set (0.00 sec)
在執行階段啟用記憶體檢測,只會計算啟用檢測後建立的記憶體物件。因此,建議您等待伺服器收集足夠的資料來進行疑難排解。
MySQL 8.0 預設會啟用所有的記憶體檢測功能。
mysql> select version();
+---------------+
| version() |
+---------------+
| 8.0.28-google |
+---------------+
1 row in set (0.00 sec)
mysql> SELECT enabled, count(*) FROM performance_schema.setup_instruments WHERE NAME LIKE '%memory%' group by enabled;
+---------+----------+
| enabled | count(*) |
+---------+----------+
| YES | 490 |
+---------+----------+
1 row in set (0.00 sec)
Sys 結構定義包含許多檢視畫面,會將 performance_schema 資料彙整成易於理解的形式。這些檢視畫面大多是成對的。兩者的名稱相同,其中一個的前置字串為 x$。MySQL 5.7 和 8.0 提供下列檢視畫面,方便您查看不同層級的記憶體配置。
mysql> use sys;
資料庫已變更
mysql> show tables like '%memory%';
+-------------------------------------+
| Tables_in_sys (%memory%) |
+-------------------------------------+
| memory_by_host_by_current_bytes |
| memory_by_thread_by_current_bytes |
| memory_by_user_by_current_bytes |
| memory_global_by_current_bytes |
| memory_global_total |
| x$memory_by_host_by_current_bytes |
| x$memory_by_thread_by_current_bytes |
| x$memory_by_user_by_current_bytes |
| x$memory_global_by_current_bytes |
| x$memory_global_total |
+-------------------------------------+
資料集中的 10 列 (0.02 秒)
sys.memory_global_total 檢視畫面匯總了記憶體總用量,並採用可讀的格式。
mysql> select * from sys.memory_global_total;
+-----------------+
| total_allocated |
+-----------------+
| 12.43 GiB |
+-----------------+
資料集中的 1 列 (0.44 秒)
sys.x$memory_global_total 顯示相同的資訊,但以位元組為單位。
mysql> select * from sys.x$memory_global_total;
+-----------------+
| total_allocated |
+-----------------+
| 13351482992 |
+-----------------+
資料集中的 1 列 (1.39 秒)
不含 x$ 前置字元的檢視畫面旨在提供更容易瞭解和閱讀的輸出內容。帶有 x$ 前置字串的檢視畫面是以原始格式顯示相同的值,旨在與其他自行處理資料的工具搭配使用。
sys.memory_global_by_current_bytes 檢視畫面會顯示目前的記憶體用量,並按照分配類型 (event_name) 分組,例如:memory/innodb/buf_buf_pool (InnoDB 緩衝區集區)。根據預設,資料列會按照記憶體的使用量遞減排序。
在深入探討各種分配類型前,建議您先概略瞭解 Innodb、sql、performance_schema 等各代碼區的記憶體用量。
下列查詢會根據代碼區匯總目前已分配的記憶體。
SELECT SUBSTRING_INDEX(event_name,'/',2) AS code_area,
FORMAT_BYTES(SUM(current_alloc)) AS current_alloc
FROM sys.x$memory_global_by_current_bytes
GROUP BY SUBSTRING_INDEX(event_name,'/',2)
ORDER BY SUM(current_alloc) DESC;
+---------------------------+---------------+
| code_area | current_alloc |
+---------------------------+---------------+
| memory/innodb | 11.28 GiB |
| memory/performance_schema | 520.02 MiB |
| memory/temptable | 514.00 MiB |
| memory/sql | 106.12 MiB |
| memory/mysys | 43.25 MiB |
| memory/vio | 372.15 KiB |
| memory/myisam | 696 bytes |
| memory/csv | 88 bytes |
| memory/blackhole | 88 bytes |
+---------------------------+---------------+
資料集中的 9 列 (1.02 秒)
這個輸出內容顯示 innodb 是最大規模的記憶體取用端,其次是 performance_schema 和臨時資料表。
現在,查詢 sys.memory_global_by_current_bytes 後,會直接顯示 MySQL 內部結構的記憶體用量 (如 event_name 欄所述)。如果您要解決記憶體的異常增加問題,請查看此檢視畫面的輸出結果,找出來源。
mysql> select event_name,current_alloc,high_alloc from sys.memory_global_by_current_bytes limit 5;
+-------------------------------+---------------+------------+
| event_name | current_alloc | high_alloc |
+-------------------------------+---------------+------------+
| memory/innodb/buf_buf_pool | 10.72 GiB | 10.72 GiB |
| memory/temptable/physical_ram | 514.00 MiB | 515.00 MiB |
| memory/innodb/hash0hash | 292.69 MiB | 292.69 MiB |
| memory/innodb/memory | 191.77 MiB | 197.94 MiB |
| memory/sql/TABLE | 60.39 MiB | 62.35 MiB |
+-------------------------------+---------------+------------+
資料集中的 5 列 (0.45 秒)
以下是各種分配類型的詳細資料。
分配類型 | 詳細資料 |
memory/innodb/buf_buf_pool | InnoDB 緩衝區集區使用的記憶體 |
memory/temptable/physical_ram | 內部臨時資料表使用的記憶體 |
memory/innodb/hash0hash | InnoDB 雜湊資料表使用的記憶體 |
memory/innodb/memory | 各種 InnoDB 緩衝區使用的記憶體 |
memory/sql/TABLE | TABLE 物件及其 mem 根層級使用的記憶體 |
分配類型
詳細資料
memory/innodb/buf_buf_pool
InnoDB 緩衝區集區使用的記憶體
memory/temptable/physical_ram
內部臨時資料表使用的記憶體
memory/innodb/hash0hash
InnoDB 雜湊資料表使用的記憶體
memory/innodb/memory
各種 InnoDB 緩衝區使用的記憶體
memory/sql/TABLE
TABLE 物件及其 mem 根層級使用的記憶體
這個檢視畫面匯總了依使用者分組的記憶體用量,若某位使用者有多個連線,則會匯總該使用者所有執行個體的記憶體用量。
mysql> select user,current_allocated from memory_by_user_by_current_bytes;
+-----------------+-------------------+
| user | current_allocated |
+-----------------+-------------------+
| sbtest | 60.30 MiB |
| background | 2.68 MiB |
| root | 1.52 MiB |
| event_scheduler | 16.38 KiB |
+-----------------+-------------------+
資料集中的 4 列 (1.16 秒)
如果您為各項應用程式/工作使用不同的使用者 (建議做法),這個檢視畫面可協助您判斷特定應用程式或工作是否耗用大量記憶體。
這個檢視畫面會顯示依主機名稱分組的記憶體用量摘要。
mysql> select host,current_allocated from memory_by_host_by_current_bytes;
+------------+-------------------+
| host | current_allocated |
+------------+-------------------+
| 10.128.0.8 | 62.10 MiB |
| background | 2.70 MiB |
| 127.0.0.1 | 311.01 KiB |
| localhost | 16.38 KiB |
+------------+-------------------+
資料集中的 4 列 (1.58 秒)
這個檢視畫面有助於識別每個用戶端主機的記憶體用量。現今擁有多個用戶端/應用程式伺服器的情況非常常見,這種檢視方式可協助您確認任何特定用戶端或工作負載是否造成高記憶體用量。您也可以先在某個用戶端/應用程式伺服器上部署變更,監控其對記憶體的影響 (比較其他用戶端主機的記憶體用量),然後做出明智的決策。
這個檢視畫面匯總了依執行緒分組的記憶體用量,有助於找出佔用大部分記憶體的執行緒。
mysql> select thread_id,user,current_allocated from sys.memory_by_thread_by_current_bytes limit 5;
+-----------+-------------------+-------------------+
| thread_id | user | current_allocated |
+-----------+-------------------+-------------------+
| 12999 | sbtest@10.128.0.8 | 3.80 MiB |
| 12888 | sbtest@10.128.0.8 | 3.75 MiB |
| 12760 | sbtest@10.128.0.8 | 3.68 MiB |
| 13128 | sbtest@10.128.0.8 | 3.67 MiB |
| 13221 | sbtest@10.128.0.8 | 3.47 MiB |
+-----------+-------------------+-------------------+
資料集中的 5 列 (2.29 秒)
如果您發現特定執行緒耗用了大量記憶體,則可使用 thread_id 篩選 MySQL 慢速查詢記錄或一般記錄,並找出導致記憶體用量增加的工作階段/查詢。
解決大量記憶體用量問題並不容易。幸好,performance_schema 記憶體檢測功能可協助使用者瞭解內部 MySQL 結構使用記憶體的方式。sys 結構定義檢視畫面可讓您更輕鬆地解讀資訊。如果您遇到記憶體相關問題,這項功能可協助您找出問題來源,並透過變更設定/查詢來修正該問題。
例如:
如果您遇到 MySQL 執行個體使用的記憶體量高於正常值的情況,且記憶體用量持續增加直到伺服器記憶體用盡,而 performance_schema 顯示出總量與檢測記憶體之間有差距,或是無法判斷記憶體去了哪裡,那就有可能是記憶體流失的問題。如果您懷疑記憶體流失,可採取下列步驟: