mariadb 內存佔用優化

本文由雲+社區發表

作者:工程師小熊

摘要:我們在使用mariadb的時候發現有時候不能啓動起來,在使用過程中mariadb佔用的內存很大,在這裏學習下mariadb與內存相關的配置項,對mariadb進行調優。

查詢最高內存佔用

使用以下命令可以知道mysql的配置使用多少 RAM

SELECT ( @@key_buffer_size
+ @@query_cache_size
+ @@innodb_buffer_pool_size
+ @@innodb_additional_mem_pool_size
+ @@innodb_log_buffer_size
+ @@max_connections * ( @@read_buffer_size
+ @@read_rnd_buffer_size
+ @@sort_buffer_size
+ @@join_buffer_size
+ @@binlog_cache_size
+ @@thread_stack
+ @@tmp_table_size
)
) / (1024 * 1024 * 1024) AS MAX_MEMORY_GB;

可以使用mysql計算器來計算內存使用

下面是理論,可以直接到推薦配置

如何調整配置

key_buffer_size(MyISAM索引用)

指定索引緩衝區的大小,它決定索引處理的速度,尤其是索引讀的速度。爲了最小化磁盤的 I/O , MyISAM 存儲引擎的表使用鍵高速緩存來緩存索引,這個鍵高速緩存的大小則通過 key-buffer-size 參數來設置。如果應用系統中使用的表以 MyISAM 存儲引擎爲主,則應該適當增加該參數的值,以便儘可能的緩存索引,提高訪問的速度。

怎麼設

show global status like 'key_read%';

+------------------------+-------------+
| Variable_name | Value |
+------------------------+-------------+
| Key_read_requests | 27813678764 |
| Key_reads | 6798830 |
--------------------- 
  • key_buffer_size通過檢查狀態值Key_read_requests和Key_reads,可以知道key_buffer_size設置是否合理。
  • 比例key_reads / key_read_requests應該儘可能的低,至少是1:100,1:1000更好。
show global status like '%created_tmp_disk_tables%';
  • key_buffer_size只對MyISAM表起作用。即使你不使用MyISAM表,但是內部的臨時磁盤表是MyISAM表,也要使用該值。可以使用檢查狀態值created_tmp_disk_tables得知詳情。
  • 對於1G內存的機器,如果不使用MyISAM表,推薦值是16M(8-64M)

另一個參考如下

show global status like 'key_blocks_u%';
+------------------------+-------------+
| Variable_name | Value |
+------------------------+-------------+
| Key_blocks_unused | 0 |
| Key_blocks_used | 413543 |
+------------------------+-------------+

Key_blocks_unused表示未使用的緩存簇(blocks)數,Key_blocks_used表示曾經用到的最大的blocks數,比如這臺服務器,所有的緩存都用到了,要麼增加key_buffer_size,要麼就是過渡索引了,把緩存佔滿了。比較理想的設置:

  • 可以根據此工式來動態的調整Key_blocks_used / (Key_blocks_unused + Key_blocks_used) * 100% ≈ 80%
show engines;
  • 查詢存儲引擎

innodb_buffer_pool_size (innodb索引用)

這個參數和MyISAM的key_buffer_size有相似之處,但也是有差別的。這個參數主要緩存innodb表的索引,數據,插入數據時的緩衝。爲Innodb加速優化首要參數。

該參數分配內存的原則:這個參數默認分配只有8M,可以說是非常小的一個值。

  • 如果是專用的DB服務器,且以InnoDB引擎爲主的場景,通常可設置物理內存的50%,這個參數不能動態更改,所以分配需多考慮。分配過大,會使Swap佔用過多,致使Mysql的查詢特慢。
  • 如果是非專用DB服務器,可以先嚐試設置成內存的1/4,如果有問題再調整

query_cache_size(查詢緩存)

緩存機制簡單的說就是緩存sql文本及查詢結果,如果運行相同的sql,服務器直接從緩存中取到結果,而不需要再去解析和執行sql。如果表更改了,那麼使用這個表的所有緩衝查詢將不再有效,查詢緩存值的相關條目被清空。更改指的是表中任何數據或是結構的改變,包括INSERT、UPDATE、DELETE、TRUNCATE、ALTER TABLE、DROP TABLE或DROP DATABASE等,也包括那些映射到改變了的表的使用MERGE表的查詢。顯然,這對於頻繁更新的表,查詢緩存是不適合的,而對於一些不常改變數據且有大量相同sql查詢的表,查詢緩存會節約很大的性能。

  • 注意:如果你查詢的表更新比較頻繁,而且很少有相同的查詢,最好不要使用查詢緩存。因爲這樣會消耗很大的系統性能還沒有任何的效果

要不要打開?

先設置成這樣跑一段時間

query_cache_size=128M 
query_cache_type=1 

看看命中結果來進行進一步的判斷

mysql> show status like '%Qcache%';
+-------------------------+-----------+
| Variable_name           | Value     |
+-------------------------+-----------+
| Qcache_free_blocks      | 669       |
| Qcache_free_memory      | 132519160 |
| Qcache_hits             | 1158      |
| Qcache_inserts          | 284824    |
| Qcache_lowmem_prunes    | 2741      |
| Qcache_not_cached       | 1755767   |
| Qcache_queries_in_cache | 579       |
| Qcache_total_blocks     | 1853      |
+-------------------------+-----------+
8 rows in set (0.00 sec)

Qcache_free_blocks:表示查詢緩存中目前還有多少剩餘的blocks,如果該值顯示較大,則說明查詢緩存中的內存碎片過多了,可能在一定的時間進行整理。

Qcache_free_memory:查詢緩存的內存大小,通過這個參數可以很清晰的知道當前系統的查詢內存是否夠用,是多了,還是不夠用,DBA可以根據實際情況做出調整。

Qcache_hits:表示有多少次命中緩存。我們主要可以通過該值來驗證我們的查詢緩存的效果。數字越大,緩存效果越理想。

Qcache_inserts: 表示多少次未命中然後插入,意思是新來的SQL請求在緩存中未找到,不得不執行查詢處理,執行查詢處理後把結果insert到查詢緩存中。這樣的情況的次數,次數越多,表示查詢緩存應用到的比較少,效果也就不理想。當然系統剛啓動後,查詢緩存是空的,這很正常。

Qcache_lowmem_prunes:該參數記錄有多少條查詢因爲內存不足而被移除出查詢緩存。通過這個值,用戶可以適當的調整緩存大小。

Qcache_not_cached: 表示因爲query_cache_type的設置而沒有被緩存的查詢數量。

Qcache_queries_in_cache:當前緩存中緩存的查詢數量。

Qcache_total_blocks:當前緩存的block數量。

  • 我們可以看到現網命中1158,未緩存的有1755767次,說明我們這個系統命中的太少了,表變動比較多,不什麼開啓這個功能涉及參數
  • query_cache_limit:允許 Cache 的單條 Query 結果集的最大容量,默認是1MB,超過此參數設置的 Query 結果集將不會被 Cache
  • query_cache_min_res_unit:設置 Query Cache 中每次分配內存的最小空間大小,也就是每個 Query 的 Cache 最小佔用的內存空間大小
  • query_cache_size:設置 Query Cache 所使用的內存大小,默認值爲0,大小必須是1024的整數倍,如果不是整數倍,MySQL 會自動調整降低最小量以達到1024的倍數
  • query_cache_type:控制 Query Cache 功能的開關,可以設置爲0(OFF),1(ON)和2(DEMAND)三種,意義分別如下: 0(OFF):關閉 Query Cache 功能,任何情況下都不會使用 Query Cache 1(ON):開啓 Query Cache 功能,但是當 SELECT 語句中使用的 SQL_NO_CACHE 提示後,將不使用Query Cache 2(DEMAND):開啓 Query Cache 功能,但是隻有當 SELECT 語句中使用了 SQL_CACHE 提示後,才使用 Query Cache
  • query_cache_wlock_invalidate:控制當有寫鎖定發生在表上的時刻是否先失效該表相關的 Query Cache,如果設置爲 1(TRUE),則在寫鎖定的同時將失效該表相關的所有 Query Cache,如果設置爲0(FALSE)則在鎖定時刻仍然允許讀取該表相關的 Query Cache。

innodb_additional_mem_pool_size(InnoDB內部目錄大小)

InnoDB 字典信息緩存主要用來存放 InnoDB 存儲引擎的字典信息以及一些 internal 的共享數據結構信息,也就是存放Innodb的內部目錄,所以其大小也與系統中所使用的 InnoDB 存儲引擎表的數量有較大關係。

這個值不用分配太大,通常設置16M夠用了,默認8M,如果設置的內存大小不夠,InnoDB 會自動申請更多的內存,並在 MySQL 的 Error Log 中記錄警告信息。

innodb_log_buffer_size (日誌緩衝)

表示InnoDB寫入到磁盤上的日誌文件時使用的緩衝區的字節數,默認值爲16M。一個大的日誌緩衝區允許大量的事務在提交之前不用寫日誌到磁盤,所以如果有更新,插入或刪除許多行的事務,則使日誌緩衝區更大一些可以節省磁盤IO

通常最大設爲64M足夠

max_connections (最大併發連接)

MySQL的max_connections參數用來設置最大連接(用戶)數。每個連接MySQL的用戶均算作一個連接,max_connections的默認值爲100。

  • 這個參數實際起作用的最大值(實際最大可連接數)爲16384,即該參數最大值不能超過16384,即使超過也以16384爲準;
  • 增加max_connections參數的值,不會佔用太多系統資源。系統資源(CPU、內存)的佔用主要取決於查詢的密度、效率等;
  • 該參數設置過小的最明顯特徵是出現”Too many connections”錯誤
mysql> show variables like '%max_connect%';
+-----------------------+-------+
| Variable_name         | Value |
+-----------------------+-------+
| extra_max_connections | 1     |
| max_connect_errors    | 100   |
| max_connections       | 2048  |
+-----------------------+-------+
3 rows in set (0.00 sec)

mysql> show status like 'Threads%';
+-------------------+---------+
| Variable_name     | Value   |
+-------------------+---------+
| Threads_cached    | 0       |
| Threads_connected | 1       |
| Threads_created   | 9626717 |
| Threads_running   | 1       |
+-------------------+---------+
4 rows in set (0.00 sec)

可以看到此時的併發數也就是Threads_connected=1,還遠遠達不到2048

mysql> show variables like 'open_files_limit';
+------------------+-------+
| Variable_name    | Value |
+------------------+-------+
| open_files_limit | 65535 |
+------------------+-------+
1 row in set (0.00 sec)

max_connections 還取決於操作系統對單進程允許打開最大文件數的限制

也就是說如果操作系統限制單個進程最大可以打開100個文件

那麼 max_connections 設置爲200也沒什麼用

MySQL 的 open_files_limit 參數值是在MySQL啓動時記錄的操作系統對單進程打開最大文件數限制的值

可以使用 show variables like ‘open_files_limit’; 查看 open_files_limit 值

ulimit -n
65535

或者直接在 Linux 下通過ulimit -n命令查看操作系統對單進程打開最大文件數限制 ( 默認爲1024 )

connection級內存參數(線程獨享)

connection級參數,是在每個connection第一次需要使用這個buffer的時候,一次性分配設置的內存。

排序性能

mysql對於排序,使用了兩個變量來控制sort_buffer_size和 max_length_for_sort_data, 不象oracle使用SGA控制. 這種方式的缺點是要單獨控制,容易出現排序性能問題.

mysql> SHOW GLOBAL STATUS like '%sort%';
+---------------------------+--------+
| Variable_name             | Value  |
+---------------------------+--------+
| Sort_merge_passes         | 0      |
| Sort_priority_queue_sorts | 1409   |
| Sort_range                | 0      |
| Sort_rows                 | 843479 |
| Sort_scan                 | 13053  |
+---------------------------+--------+
5 rows in set (0.00 sec)
  • 如果發現Sort_merge_passes的值比較大,你可以考慮增加sort_buffer_size 來加速ORDER BY 或者GROUP BY 操作,不能通過查詢或者索引優化的。我們這爲0,那就沒必要設置那麼大。

讀取緩存

read_buffer_size = 128K(默認128K)爲需要全表掃描的MYISAM數據表線程指定緩存

read_rnd_buffer_size = 4M:(默認256K)首先,該變量可以被任何存儲引擎使用,當從一個已經排序的鍵值表中讀取行時,會先從該緩衝區中獲取而不再從磁盤上獲取。

大事務binlog

mysql> show global status like 'binlog_cache%';
+-----------------------+----------+
| Variable_name         | Value    |
+-----------------------+----------+
| Binlog_cache_disk_use | 220840   |
| Binlog_cache_use      | 67604667 |
+-----------------------+----------+
2 rows in set (0.00 sec)
  • Binlog_cache_disk_use表示因爲我們binlog_cache_size設計的內存不足導致緩存二進制日誌用到了臨時文件的次數
  • Binlog_cache_use 表示 用binlog_cache_size緩存的次數
  • 當對應的Binlog_cache_disk_use 值比較大的時候 我們可以考慮適當的調高 binlog_cache_size 對應的值
  • 如上圖,現網是32K,我們加到64K

join語句內存影響

如果應用中,很少出現join語句,則可以不用太在乎join_buffer_size參數的設置大小。

如果join語句不是很少的話,個人建議可以適當增大join_buffer_size到1MB左右,如果內存充足可以設置爲2MB。

線程內存影響

Thread_stack:每個連接線程被創建時,MySQL給它分配的內存大小。當MySQL創建一個新的連接線程時,需要給它分配一定大小的內存堆棧空間,以便存放客戶端的請求的Query及自身的各種狀態和處理信息。

mysql> show status like '%threads%';
+-------------------------+---------+
| Variable_name           | Value   |
+-------------------------+---------+
| Delayed_insert_threads  | 0       |
| Slow_launch_threads     | 0       |
| Threadpool_idle_threads | 0       |
| Threadpool_threads      | 0       |
| Threads_cached          | 0       |
| Threads_connected       | 1       |
| Threads_created         | 9649301 |
| Threads_running         | 1       |
+-------------------------+---------+
8 rows in set (0.00 sec)

mysql> show status like 'connections';
+---------------+---------+
| Variable_name | Value   |
+---------------+---------+
| Connections   | 9649311 |
+---------------+---------+
1 row in set (0.00 sec)

如上:系統啓動到現在共接受到客戶端的連接9649311次,共創建了9649301個連接線程,當前有1個連接線程處於和客戶端連接的狀態。而在Thread Cache池中共緩存了0個連接線程(Threads_cached)。

Thread Cache 命中率:

Thread_Cache_Hit = (Connections - Threads_created) / Connections * 100%;

一般在系統穩定運行一段時間後,Thread Cache命中率應該保持在90%左右纔算正常。

內存臨時表

tmp_table_size 控制內存臨時表的最大值,超過限值後就往硬盤寫,寫的位置由變量 tmpdir 決定

max_heap_table_size 用戶可以創建的內存表(memory table)的大小.這個值用來計算內存表的最大行數值。

Order By 或者Group By操作多的話,加大這兩個值,默認16M

mysql> show status like 'Created_tmp_%';
+-------------------------+-------+
| Variable_name           | Value |
+-------------------------+-------+
| Created_tmp_disk_tables | 0     |
| Created_tmp_files       | 626   |
| Created_tmp_tables      | 3     |
+-------------------------+-------+
3 rows in set (0.00 sec)
  • 如上圖,寫入硬盤的爲0,3次中間表,說明我們的默認值足夠用了

mariadb 推薦配置

  • 注意這裏只推薦innodb引擎
  • 內存配置只關注有註釋的行
[mysqld]
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
default-storage-engine=INNODB

character-set-server=utf8
collation-server=utf8_general_ci

user=mysql
symbolic-links=0

# global settings
table_cache=65535
table_definition_cache=65535

max_allowed_packet=4M
net_buffer_length=1M
bulk_insert_buffer_size=16M

query_cache_type=0				#是否使用查詢緩衝,0關閉
query_cache_size=0				#0關閉,因爲改表操作多,命中低,開啓消耗cpu

# shared
key_buffer_size=8M				#保持8M MyISAM索引用
innodb_buffer_pool_size=4G		        #DB專用mem*50%,非DB專用mem*15%25%
myisam_sort_buffer_size=32M
max_heap_table_size=16M				#最大中間表大小
tmp_table_size=16M				#中間表大小

# per-thread
sort_buffer_size=256K				#加速排序緩存大小
read_buffer_size=128k				#爲需要全表掃描的MYISAM數據表線程指定緩存
read_rnd_buffer_size=4M				#已排序的表讀取時緩存,如果比較大內存就到6M
join_buffer_size=1M				#join語句多時加大,1-2M
thread_stack=256k				#線程空間,256K or 512K
binlog_cache_size=64K				#大事務binlog


# big-tables
innodb_file_per_table = 1
skip-external-locking
max_connections=2048				#最大連接數
skip-name-resolve

# slow_query_log
slow_query_log_file = /var/log/mysql-slow.log
long_query_time = 30
group_concat_max_len=65536

# according to tuning-primer.sh
thread_cache_size = 8
thread_concurrency = 16

# set variables
concurrent_insert=2

運行時修改

使用以下命令來修改變量

set global {要改的key} = {}; (立即生效重啓後失效)
set @@{要改的key} = {}; (立即生效重啓後失效)
set @@global.{要改的key} = {}; (立即生效重啓後失效)

試驗

mysql> set @@global.innodb_buffer_pool_size=4294967296;
ERROR 1238 (HY000): Variable 'innodb_buffer_pool_size' is a read only variable
mysql> set @@global.thread_stack=262144;
ERROR 1238 (HY000): Variable 'thread_stack' is a read only variable
mysql> set @@global.binlog_cache_size=65536;
Query OK, 0 rows affected (0.00 sec)
mysql> set @@join_buffer_size=1048576;
Query OK, 0 rows affected (0.00 sec)
mysql> set @@read_rnd_buffer_size=4194304;
Query OK, 0 rows affected (0.00 sec)
mysql> set @@sort_buffer_size=262144;
Query OK, 0 rows affected (0.00 sec)
mysql> set @@read_buffer_size=131072;
Query OK, 0 rows affected (0.00 sec)
mysql> set global key_buffer_size=8388608;
Query OK, 0 rows affected (0.39 sec)
  • 我們可以看到innodb_buffer_pool_sizethread_stack報錯了,他們只能改配置文件,在運行時是隻讀的。 以下直接複製使用
set @@global.binlog_cache_size=65536;
set @@join_buffer_size=1048576;
set @@read_rnd_buffer_size=4194304;
set @@sort_buffer_size=262144;
set @@read_buffer_size=131072;
set global key_buffer_size=8388608;

引用

記一次Mysql佔用內存過高的優化過程

mysql 優化技巧心得一(key_buffer_size設置)

mysql內存計算

mysql計算器

mariadb官網

此文已由騰訊雲+社區在各渠道發佈

獲取更多新鮮技術乾貨,可以關注我們騰訊雲技術社區-雲加社區官方號及知乎機構號

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章