雲數據庫 MySQL 的內存是重要的性能參數,常出現由異常 SQL 請求以及待優化的數據庫導致的內存利用率升高的情況,嚴重時還會出現由於 OOM 導致實例發生 HA 切換,影響業務的穩定及可用性。
MySQL 的內存大體可以分為 global 級的共享內存和 session 級的私有內存兩部分,共享內存是實例創建時即分配的內存空間,並且是所有連接共享的。私有內存用於每個連接到 MySQL 服務器時才分配各自的緩存,一些特殊的 SQL 或字段類型會導致單個線程可能分配多次緩存,因此當出現 OOM 異常,都是由各個連接私有內存造成的。下面將詳細介紹各部分的構成。
mysql> show global variables like '%innodb_buffer%'; +-------------------------------------+----------------+ | Variable_name | Value | +-------------------------------------+----------------+ | innodb_buffer_pool_dump_at_shutdown | OFF | | innodb_buffer_pool_dump_now | OFF | | innodb_buffer_pool_filename | ib_buffer_pool | | innodb_buffer_pool_instances | 8 | | innodb_buffer_pool_load_abort | OFF | | innodb_buffer_pool_load_at_startup | OFF | | innodb_buffer_pool_load_now | OFF | | innodb_buffer_pool_size | 16777216000 | #單位:字節 +-------------------------------------+----------------+
共享內存
執行以下命令,查詢示例的共享內存分配情況:
show variables where variable_name in ('innodb_buffer_pool_size','innodb_log_buffer_size','innodb_additional_mem_pool_size','key_buffer_size','query_cache_size');
說明:
5.7版本不支持 innodb_additional_mem_pool_size。
以下參數是內存規格為1000MB實例的共享內存分配情況的查詢結果(以下為測試實例配置):
1. +---------------------------------+-----------+ 2. | Variable_name | Value | 3. +---------------------------------+-----------+ 4. | innodb_additional_mem_pool_size | 8388608 | 5. | innodb_buffer_pool_size | 524288000 | 6. | innodb_log_buffer_size | 67108864 | 7. | key_buffer_size | 16777216 | 9. | query_cache_size | 0 | 10. +---------------------------------+-----------+ 11. 5 rows in set (0.01 sec)
參數說明:
-
innodb_buffer_pool_size
該部分緩存是 Innodb 引擎最重要的緩存區域,是通過內存來彌補物理數據文件的重要手段,在雲數據庫 MySQL 上會采用實例規格配置的50% - 80%作為該部分大小(上圖為1000MB * 0.5 = 500MB)。其中主要包含數據頁、索引頁、undo 頁、insert buffer、自適應哈希索引、鎖信息以及數據字典等信息。在進行 SQL 讀和寫的操作時,首先並不是對物理數據文件操作,而是先對 buffer_pool 進行操作,再通過 checkpoint 等機制寫回數據文件。該空間的優點是可以提升數據庫的性能、加快 SQL 運行速度,缺點是故障恢復速度較慢。 -
innodb_log_buffer_size
該部分主要存放 redo log 的信息,在雲數據庫 MySQL 上會設置64MB的大小。InnoDB 會首先將 redo log 寫在這里,然后按照一定頻率將其刷新回重做日志文件中。該空間不需要太大,因為一般情況下該部分緩存會以較快頻率刷新至 redo log(Master Thread 會每秒刷新、事務提交時會刷新、其空間少於1/2時同樣會刷新)。 -
innodb_additional_mem_pool_size
該部分主要存放 InnoDB 內的一些數據結構,在雲數據庫 MySQL 中統一設置為8MB。通常是在 buffer_pool 中申請內存的時候還需要在額外內存中申請空間存儲該對象的結構信息。該大小主要與表數量有關,表數量越大需要更大的空間。 -
key_buffer_size
該部分是 MyISAM 表的重要緩存區域,所有實例統一為16M。該部分主要存放 MyISAM 表的鍵。MyISAM 表不同於 InnoDB 表,其緩存的索引緩存是放在 key_buffer 中的,而數據緩存則存儲於操作系統的內存中。雲數據庫 MySQL 的系統是 MyISAM 引擎的,因此需給予該部分一定量的空間的。 -
query_cache_size
該部分是對查詢結果做緩存,以減少解析 SQL 和執行 SQL 的開銷,在雲數據庫 MySQL 上關閉了該部分的緩存。主要適合於讀多寫少的應用場景,因為它是按照 SQL 語句的 hash 值進行緩存的,當表數據發生變化后即失效。
私有內存
執行以下命令,查詢示例的 session 私有內存分配情況:
show variables where variable_name in ('read_buffer_size','read_rnd_buffer_size','sort_buffer_size','join_buffer_size','binlog_cache_size','tmp_table_size');
查詢結果如下(以下為測試實例配置):
1. +----------------------+-----------+ 2. | Variable_name | Value | 3. +----------------------+-----------+ 4. | binlog_cache_size | 32768 | 5. | join_buffer_size | 262144 | 6. | read_buffer_size | 262144 | 7. | read_rnd_buffer_size | 524288 | 8. | sort_buffer_size | 524288 | 9. | tmp_table_size | 209715200 | 10. +----------------------+-----------+ 11. 6 rows in set (0.00 sec)
參數說明:
-
read_buffer_size
分別存放了對順序掃描的緩存,當 thread 進行順序掃描數據時會首先掃描該 buffer 空間以避免更多的物理讀。 -
read_rnd_buffer_size
分別存放了對隨機掃描的緩存,當 thread 進行隨機掃描數據時會首先掃描該 buffer 空間以避免更多的物理讀。 -
sort_buffer_size
需要執行 order by 和 group by 的 SQL 都會分配 sort_buffer,用於存儲排序的中間結果。在排序過程中,若存儲量大於 sort_buffer_size,則會在磁盤生成臨時表以完成操作。 -
join_buffer_size
MySQL 僅支持 nest loop 的 join 算法,處理邏輯是驅動表的一行和非驅動表聯合查找,這時就可以將非驅動表放入 join_buffer,不需要訪問擁有並發保護機制的 buffer_pool。 -
binlog_cache_size
該區域用來緩存該 thread 的 binlog 日志,在一個事務還沒有 commit 之前會先將其日志存儲於 binlog_cache 中,等到事務 commit 后會將其 binlog 刷回磁盤上的 binlog 文件以持久化。 -
tmp_table_size
不同於上面各個 session 級的 buffer,這個參數可以在控制台上修改。該參數是指用戶內存臨時表的大小,如果該 thread 創建的臨時表超過它設置的大小會把臨時表轉換為磁盤上的一張 MyISAM 臨時表。