Google Cloud의 MySQL용 Cloud SQL은 Google Cloud Platform에서 MySQL, PostgreSQL 또는 SQL Server 데이터베이스를 설정, 유지, 관리할 수 있는 완전 관리형 데이터베이스 서비스입니다. Cloud SQL은 Fleet에서 다수의 MySQL 데이터베이스를 실행하므로 일부 MySQL 인스턴스에서 메모리를 많이 소비하여 메모리 부족(OOM) 오류로 인해 비정상적으로 종료되는 경우가 많습니다. 이 문서는 MySQL 메모리 문제를 해결하는 데 도움이 됩니다.
메모리 부족(OOM) 조건은 프로세스가 메모리를 할당하려고 하지만 서버에 사용 가능한 메모리가 없어서 실패하는 경우에 발생합니다. 그 결과 시스템의 메모리가 매우 부족할 때 Linux 커널에서 사용하는 프로세스인 Linux OOM Killer가 발생하여 데이터베이스 프로세스가 종료됩니다.
데이터베이스 프로세스가 SIGKILL 지시문으로 종료되어 애플리케이션에서 정상적으로 MySQL을 종료할 수 없으므로 OOM 조건으로 인한 데이터베이스 비정상 종료는 바람직하지 않습니다. 이로 인해 다음과 같은 문제가 발생합니다.
MySQL은 MySQL 메모리 할당을 이해하는 데 도움이 되도록 5.7 버전부터 performance_schema에 메모리 계측을 추가했습니다. 이 문서는 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%
RAM이 10GB 이상인 인스턴스의 경우 mysqld 프로세스는 인스턴스 RAM 92%만 사용하도록 제한되며 InnoDB 버퍼 풀은 mysqld 프로세스에 제공되는 메모리의 75~80% 범위로 구성됩니다. InnoDB 버퍼 풀 외에도 MySQL은 여러 버퍼와 캐시를 할당하여 성능을 향상시킵니다. MySQL에서의 메모리 할당을 이해하려면 이 문서를 참조하세요.
mysqld 메모리 사용량이 구성된 mysqld 프로세스 한도를 초과하는 경우 mysqld 프로세스가 인스턴스 메모리를 가장 많이 사용하므로 Linux OOM Killer에서 이 프로세스를 종료합니다.
performance_schema는 하위 수준에서 서버 실행을 모니터링하는 MySQL 기능입니다. MySQL용 Cloud SQL을 사용하면 RAM 크기가 3GB 이상인 인스턴스에서 performance_schema를 사용 설정할 수 있습니다. MySQL 8.0 버전부터 RAM이 15GB 이상인 인스턴스에서는 기본적으로 사용 설정됩니다.
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 테이블의 ENABLED 열을 업데이트하여 사용 설정할 수 있습니다.
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 rows in set (0.00 sec)
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)
시스템 스키마에는 performance_schema 데이터를 쉽게 이해할 수 있는 형식으로 요약하는 다양한 뷰가 포함되어 있습니다. 이러한 뷰 대부분은 쌍으로 제공됩니다. 이름은 같으며 둘 중 하나에 x$ 프리픽스가 있습니다. MySQL 5.7 및 8.0에서는 다음과 같은 뷰가 제공되므로 메모리 할당을 서로 다른 수준에서 볼 수 있습니다.
mysql> use sys;
Database changed
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 rows in set (0.02 sec)
sys.memory_global_total 뷰에는 총 메모리 사용량이 읽을 수 있는 형식으로 요약되어 있습니다.
mysql> select * from sys.memory_global_total;
+-----------------+
| total_allocated |
+-----------------+
| 12.43 GiB |
+-----------------+
1 row in set (0.44 sec)
sys.x$memory_global_total에는 같은 정보가 바이트 단위로 표시됩니다.
mysql> select * from sys.x$memory_global_total;
+-----------------+
| total_allocated |
+-----------------+
| 13351482992 |
+-----------------+
1 row in set (1.39 sec)
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 rows in set (1.02 sec)
이 출력에서는 innodb가 가장 많은 메모리를 소비하고 있으며 그 다음으로 performance_schema 및 temporary 테이블 순입니다.
이제 sys.memory_global_by_current_bytes를 쿼리하면 event_name 열에 설명된 대로 MySQL 내부 구조의 메모리 사용량이 바로 표시됩니다. 메모리의 비정상적인 증가 문제를 해결하는 경우 이 뷰의 출력을 확인하여 소스를 식별할 수 있습니다.
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 rows in set (0.45 sec)
다음은 각 할당 유형에 대한 세부정보입니다.
할당 유형 | 세부정보 |
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 rows in set (1.16 sec)
애플리케이션/작업마다 다른 사용자를 사용하는 경우(권장) 이 뷰를 사용하면 특정 애플리케이션이나 작업에서 많은 메모리를 사용하고 있는지 확인할 수 있습니다.
이 뷰에는 호스트 이름별로 그룹화된 메모리 사용량이 요약되어 있습니다.
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 rows in set (1.58 sec)
이 뷰는 클라이언트 호스트별 메모리 사용량을 식별하는 데 유용합니다. 요즘에는 여러 클라이언트/애플리케이션 서버가 매우 일반적이며 이 뷰는 특정 클라이언트나 워크로드로 인해 메모리 사용량이 증가하는지 확인하는 데 도움이 됩니다. 또한 먼저 클라이언트/애플리케이션 서버 하나에 변경사항을 배포하고 메모리 사용량을 다른 클라이언트 호스트와 비교하여 메모리에 미치는 영향을 모니터링한 후에 정보에 입각한 결정을 내릴 수 있습니다.
이 뷰에는 스레드별로 그룹화된 메모리 사용량이 요약되어 있으며 이 뷰를 사용하면 메모리를 가장 많이 사용하는 스레드를 찾을 수 있습니다.
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 rows in set (2.29 sec)
특정 스레드에서 많은 메모리를 소비하는 것이 발견되면 thread_id를 사용하여 MySQL 느린 쿼리 로그나 일반 로그를 필터링하고 메모리를 증가시킨 세션/쿼리를 식별할 수 있습니다.
높은 메모리 사용량 문제를 해결하는 것은 까다로운 태스크입니다. 다행히 사용자가 performance_schema 메모리 계측을 사용하면 내부 MySQL 구조에서 메모리를 사용하는 방식을 이해할 수 있습니다. 시스템 스키마 뷰를 사용하면 정보를 더 쉽게 해석할 수 있습니다. 메모리 관련 문제가 발생하는 경우 이 기능을 사용하면 문제의 원인을 찾고 구성/쿼리를 변경하여 문제를 해결할 수 있습니다.
예를 들면 다음과 같습니다.
MySQL 인스턴스에서 필요한 양보다 많은 메모리를 사용하고 서버에서 메모리가 부족해질 때까지 메모리 사용량이 지속적으로 증가하는 상황이 발생하고 performance_schema에서 총 메모리와 계측 메모리 간의 차이를 보여주거나 메모리가 사용되는 위치를 알려주지 않는 경우 이는 메모리 누수 증상일 수 있습니다. 메모리 누수가 의심되면 다음 단계를 수행합니다.