環境:
Mysql版本5.7.18
2個Mysql實例 每個實例16C 32G內存,使用的幾乎原生的Mysql配置
庫數量1 庫中表數量50
1次業務流對實例依次1次插入1次查詢1次更新
插入一個服務,4個docker副本,每個副本jdbc連接數8
查詢和更新在一個服務,4個docker副本,每個副本jdbc連接數8
Mysql實例上總連接數102個。
所有表使用的Innodb引擎
現象
從應用服務上看,在同一時間,4個查詢與更新的服務副本,更新操作的線程一直卡在IO讀上,也就是沒有Mysql應答,導致其中部分服務的2-6個jdbc連接被卡死,直到540xxx毫秒(9分鐘)後統一給了應答,服務才解除卡死。
線程棧大致如下:
java.net.SocketInputStream.socketRead0
com.mysql.jdbc.util.ReadAheadInputStream.fill
com.mysql.jdbc.MysqlIO.readFully
jdbc.pool.ProxyConnection.invoke
xxxxx.業務代碼模塊的updateDao
分析
目前懷疑跟數據庫刷髒頁導致一瞬間Mysql暫停服務了,且這部分查詢處於一直執行的卡死狀態,後來發現Mysql配置基本原始,需要很多參數的優化。
記錄一些目前狀態可能有問題的指標。
- innodb_io_capacity
使用的默認值
innodb_io_capacity 200
innodb_io_capacity_max 2000
200 這個默認值太低了,目前用sysbench 測出來磁盤隨機讀寫IOPS遠高於該值
目前看到一篇有關的信息,記錄下:
http://blog.itpub.net/26506993/viewspace-2214703/
獲取該值的磁盤IO測試命令:
fio -filename=$filename -direct=1 -iodepth 1 -thread -rw=randrw -ioengine=psync -bs=16k -size=500M -numjobs=10 -runtime=10 -group_reporting -name=mytest
sysbench /opt/sysbench-1.0.13/src/lua/oltp_read_write.lua --db-driver=mysql --mysql-db=sbtest
--mysql-user=root --mysql-password --mysql-socket=/data/mysql/mysql.sock --tables=20
--table-size=10000000 --threads=64 --time=600 --report-interval=10 run
- innodb redolog
redolog 一共2個 每個50M (太小了,性能測試一下壓滿導致強制刷頁)
- 慢查詢日誌未開啓
- buffer池
innodb_buffer_pool_size 134 217 728 ( 16 c 32g虛擬機這分配。。)
innodb_buffer_pool_chunk_size 134 217 728
- 髒頁刷新策略
innodb_max_dirty_pages_pct 75
innodb_max_dirty-pages_pct_lwm 0
innodb_max_dirty-pages_pct_lwm 參數說明:
默認爲0 結合 io_capacity 與 max_io_capacity參數,計算出要刷新的頁面數
當srv_max_dirty_pages_pct_lwm 0:,是否刷髒頁,只與srv_max_buf_pool_modified_pct參數有關,當髒頁比例超過這個閾值時,開始按io_capacity能力的100%進行刷髒頁。
當srv_max_dirty_pages_pct_lwm不爲0:髒頁比例超過srv_max_dirty_pages_pct_lwm這個低水位線時,纔開始逐步地刷髒頁,髒頁比例越高,刷髒頁的強度越高。
static ulint af_get_pct_for_dirty()
{
double dirty_pct = buf_get_modified_ratio_pct();
if (dirty_pct == 0.0) {
return(0);
}
if (srv_max_dirty_pages_pct_lwm == 0) {
if (dirty_pct >= srv_max_buf_pool_modified_pct) {
return(100);
}
} else if (dirty_pct >= srv_max_dirty_pages_pct_lwm) {
return(static_cast<ulint>((dirty_pct * 100) / (srv_max_buf_pool_modified_pct + 1)));
}
return(0);
}
-
緩存利用率
innodb_buffer_pool_reads:表示InnoDB緩衝池無法滿足的請求數。需要從磁盤中讀取。
innodb_buffer_pool_read_requests:表示從內存中讀取邏輯的請求數。
大概有3%的數據需要讀磁盤。應該降低到1%以下。 -
緩存池中數據表明緩存池太小,這個pool_wait_free嚴重,如何調節:
參考的這篇博客:https://www.cnblogs.com/wanbin/p/9530833.html
Innodb_buffer_pool_pages_data
InnoDB緩衝池中包含數據的頁數。 該數字包括髒頁面和乾淨頁面。 使用壓縮表時,報告的Innodb_buffer_pool_pages_data值可能大於Innodb_buffer_pool_pages_total(Bug#59550)。
Innodb_buffer_pool_pages_dirty
顯示在內存中修改但尚未寫入數據文件的InnoDB緩衝池數據頁的數量(髒頁刷新)。
Innodb_buffer_pool_pages_flushed
表示從InnoDB緩衝池中刷新髒頁的請求數。
Innodb_buffer_pool_pages_free
顯示InnoDB緩衝池中的空閒頁面
Innodb_buffer_pool_pages_misc
InnoDB緩衝池中的頁面數量很多,因爲它們已被分配用於管理開銷,例如行鎖或自適應哈希索引。此值也可以計算爲Innodb_buffer_pool_pages_total - Innodb_buffer_pool_pages_free - Innodb_buffer_pool_pages_data。
Innodb_buffer_pool_pages_total
InnoDB緩衝池的總大小,以page爲單位。
innodb_buffer_pool_reads
表示InnoDB緩衝池無法滿足的請求數。需要從磁盤中讀取。
innodb_buffer_pool_read_requests
它表示從內存中邏輯讀取的請求數。
innodb_buffer_pool_wait_free
通常,對InnoDB緩衝池的寫入發生在後臺。 當InnoDB需要讀取或創建頁面並且沒有可用的乾淨頁面時,InnoDB首先刷新一些髒頁並等待該操作完成。 此計數器計算這些等待的實例。 如果已正確設置innodb_buffer_pool_size,則此值應該很小。如果大於0,則表示InnoDb緩衝池太小。
innodb_buffer_pool_write_request
表示對緩衝池執行的寫入次數。
- innodb_flush_neighbors=1 臨近數據髒頁刷新策略 默認是
ssd 的話不建議,影響本次響應回覆時間,機械硬盤可以開
- skip-external-locking #如果是單服務器環境,則將其禁用即可
調整後參數
#Version 0.0.2 recreate hesiyuan
[client]
port=31306
socket=/data/dmain/mysqld.sock
[mysqld]
##########basic setting##########
server-id=11 # cluster not duplicate
port=31306
user=mysql
datadir=/data/dmain
basedir=/app/mysql
tmpdir=/data/tmp
socket=/data/dmain/mysqld.sock
skip-external-locking #V0.0.2 new
skip-name-resolve #V0.0.2 changge <- skip_name_resolve=1
default-storage-engine=INNODB
character_set_server=utf8mb4 #V0.0.2 changge <- character_set_server=utf8
#event_scheduler=0 #default off mysql sheduler task
#autocommit=1 #default on
log_timestamps=SYSTEM
#default_password_lifetime=0
query_cache_type=0 #V0.0.2 new
lock_wait_timeout=20 #V0.0.2 new #ddl lock wait time
max_execution_time=30000 #V0.0.2 new #ms execute time limit
performance-schema-instrument='wait/lock/metadata/sql/mdl=ON' #V0.0.2 new
explicit_defaults_for_timestamp=1 #V0.0.2 new
open-files-limit=28192 #V0.0.2 new
#########log setting ###########
log_bin=/data/binlog/bin.log
log_bin_index=/data/binlog/binlog.index
log-error=/data/dmain/mysql.log
binlog_format=row
binlog_cache_size=2M # 4-> 2
max_binlog_cache_size=1G # 512M -> 1G
max_binlog_cache_size=512M
binlog_checksum=NONE
sync_binlog=1
#binlog_rows_query_log_events=1 #default 0 record sql
binlog_row_image='minimal' #not record blob&test default full update before value all record
#gtid_mode=on
#enforce_gtid_consistency=on
#binlog_gtid_simple_recovery=1
long_query_time=0.5
slow_query_log=1
slow_query_log_file=/data/slowlog/mysql.slow
expire_logs_days=7
relay-log=/data/relaylog/relay.log
relay_log_recovery=0
relay_log_purge=0
relay_log_info_repository=table #default file
master_info_repository=table #default file
##########connect setting#########
back_log=350 #default 50 when connection is full wait queue
max_connections=1500
max_user_connections=1000
max_connect_errors=1000
interactive-timeout=43200 # default 8 hours
wait_timeout=43200 # default 8 hours no-interactive-timeout diff by clinet setting
connect_timeout=43200 # 12 hours default 8hours
thread_cache_size=200
###########thread_buffers##########
key_buffer_size=64M
max_allowed_packet=128M
table_open_cache=6144
table_definition_cache=4096
sort_buffer_size=2M
read_buffer_size=8M
read_rnd_buffer_size=8M
join_buffer_size=50M
tmp_table_size=64M
max_heap_table_size=64M
bulk_insert_buffer_size=32M
thread_stack=256K
###########innodb setting##########
innodb_log_files_in_group=4
innodb_log_file_size=1G
innodb_log_buffer_size=64M
innodb_rollback_segments=128
#innodb_undo_tablespaces=5
innodb_max_undo_log_size=10G
#innodb_undo_log_truncate=1
thread_cache_size=200 #
innodb_thread_concurrency=10 #V0.0.2 new
innodb_adaptive_flushing=1
innodb_buffer_pool_size=20G
innodb_buffer_pool_instances=16
innodb_lock_wait_timeout=20
innodb_spin_wait_delay=6
innodb_sync_spin_loops=40
#innodb_max_dirty_pages_pct=75 #默認值
innodb_support_xa=1
innodb_flush_log_at_trx_commit=1 #V0.0.2 2->1
innodb_concurrency_tickets=100
innodb_thread_concurrency=10
log_bin_trust_function_creators=1
innodb_flush_method=O_DIRECT
innodb_file_per_table=1
innodb_read_io_threads=8
innodb_write_io_threads=8
innodb_purge_threads=2
innodb_purge_batch_size=300
innodb_change_buffering=all
innodb_stats_on_metadata=0
innodb_strict_mode=1
innodb_use_native_aio=1
innodb_print_all_deadlocks=1
innodb_autoinc_lock_mode=2
max_length_for_sort_data=4096
innodb_print_all_deadlocks=1
innodb_page_size=16K
innodb_flush_neighbors=0
innodb_rollback_on_timeout=1
innodb_open_files=15000
#innodb_numa_interleave=1
innodb_buffer_pool_dump_at_shutdown=1
innodb_buffer_pool_load_at_startup=1
innodb_io_capacity=500 #根據磁盤IO測試獲取
# 不同工具測試命令
# ./sysbench --test=fileio --file-num=1 --file-test-mode=rndrw --file-extra-flags=direct --file-io-mode=async(sync) --file-fsync-freq=0 --num-threads=1 --file-rw-ratio=1 --max-requests=10000 run
# fio -filename=$filename -direct=1 -iodepth 1 -thread -rw=randrw -ioengine=psync -bs=16k -size=500M -numjobs=10 -runtime=10 -group_reporting -name=mytest
innodb_io_capacity_max=2000 #
######master配置#########
rpl_semi_sync_master_enabled=1
######slave配置##########
rpl_semi_sync_slave_enabled=1
#dev sql setting----------------------------------------------------
sql_mode='ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION,PIPES_AS_CONCAT' #V0.0.2 new
optimizer_switch='index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,engine_condition_pushdown=on,index_condition_pushdown=on,mrr=on,mrr_cost_based=off,block_nested_loop=on,batched_key_access=on,materialization=on,semijoin=on,loosescan=on,firstmatch=on,duplicateweedout=on,subquery_materialization_cost_based=on,use_index_extensions=on,condition_fanout_filter=on,derived_merge=on' #V0.0.2 new
group_concat_max_len=50000 #V0.0.2 new group sql max string length
過程學習參數
#interactive_timeout #參數含義:服務器關閉交互式連接前等待活動的秒數。交互式客戶端定義爲在mysql_real_connect()中使用CLIENT_INTERACTIVE選項的客戶端。參數默認值:28800秒(8小時)
#wait_timeout: #參數含義:服務器關閉非交互連接之前等待活動的秒數。在線程啓動時,根據全局wait_timeout值或全局interactive_timeout值初始化會話wait_timeout值,取決於客戶端類型(由mysql_real_connect()的連接選項CLIENT_INTERACTIVE定義)。參數默認值:28800秒(8小時)
skip-external-locking #如果是單服務器環境,則將其禁用即可 當外部鎖定(external-locking)起作用時,每個進程若要訪問數據表,則必須等待之前的進程完成操作並解除鎖定。由於服務器訪問數據表時經常需要等待解鎖,因此在單服務器環境下external locking會讓MySQL性能下降。所以在很多Linux發行版的源中,MySQL配置文件中默認使用了skip-external-locking來避免external locking。
back_log=350 #默認的50修改爲350.(每個連接256kb, 佔用:87.5M)back_log值指出在MySQL暫時停止回答新請求之前的短時間內多少個請求可以被存在堆棧中。也就是說,如果MySql的連接數達到max_connections時,新來的請求將會被存在堆棧中,以等待某一連接釋放資源,該堆棧的數量即back_log,如果等待連接的數量超過back_log,將不被授予連接資源。將會報:unauthenticated user | xxx.xxx.xxx.xxx | NULL | Connect | NULL | login | NULL 的待連接進程時.
event_scheduler=0 #默認開啓 mysql定時任務 可以關了用java 定時任務
explicit_defaults_for_timestam=1 #系統變量決定MySQL服務端對timestamp列中的默認值和NULL值的不同處理方法。此變量自MySQL 5.6.6 版本引入,分爲全局級別和會話級別,可動態更新,默認值爲OFF。#個人建議爲1 可以及時在測試環境發現參數未賦值問題 參考https://blog.51cto.com/10814168/2376234
group_concat_max_len #MySQL提供的group_concat函數可以拼接某個字段值成字符串,如 select group_concat(user_name) from sys_user,默認的分隔符是 逗號,即"," ,如果需要自定義分隔符可以使用 SEPARATOR如:select group_concat(user_name SEPARATOR '_') from sys_user但是如果 user_name 拼接的字符串的長度字節超過1024 則會被截斷。
default_password_lifetime #create user xxx@xxx identified by 'xxx' 創建用戶密碼過期時間 使用版本5.7.18 已經爲0 無限期
query_cache_type=0時表示關閉,1時表示打開,Mysql 8 已經默認關閉,查詢緩存的最終結果是事與願違:之所以查詢緩存並沒有能起到提升性能的做用,客觀上有如下兩點原因
1、把SQL語句的hash值作爲鍵,SQL語句的結果集作爲值;這樣就引起了一個問題如 select user from mysql.user 和 SELECT user FROM mysql.user 這兩個將會被當成不同的SQL語句,這個時候就算結果集已經有了,但是一然用不到。
2、當查詢所基於的低層表有改動時與這個表有關的查詢緩存都會作廢、如果對於併發度比較大的系統這個開銷是可觀的;對於作廢結果集這個操作也是要用併發,訪問控制的,就是說也會有鎖。併發大的時候就會有Waiting for query cache lock 產生。
個人:Mysql.8既然已經默認關閉,如果業務沒有大量相同sql查詢不變的東西,沒必要使用。真要緩存不如用redis
innodb_lock_wait_timeout與lock_wait_timeout #前者是innodb的dml操作的行級鎖的等待時間 後面是數據結構ddl操作的鎖的等待時間
max_execution_time=30000 可見https://blog.csdn.net/sun_ashe/article/details/87988770 最長執行時間
binlog_rows_query_log_events #在row模式下..開啓該參數,將把sql語句打印到binlog日誌裏面.默認是0(off);
雖然將語句放入了binlog,但不會執行這個sql,就相當於註釋一樣.但對於dba來說,在查看binlog的時候,很有用處.
binlog_row_image='minimal' #默認爲full,在binlog爲row格式下,full將記錄update前後所有字段的值,minimal時,只記錄更改字段的值和where字段的值,noblob時,記錄除了blob和text的所有字段的值,如果update的blob或text字段,也只記錄該字段更改後的值,更改前的不記錄;
gtid_mode # 暫時沒有看懂 待後續研究
server-id # 主從同步需要的集羣內id
sync_binlog #目前設置爲1 https://www.cnblogs.com/xuxubaobao/p/10839979.html
innodb_support_xa與innodb_flush_log_at_trx_commit # 都設置爲1 https://blog.csdn.net/zbszhangbosen/article/details/9132833
innodb_write_io_threads 與innodb_read_io_threads # 根據cpu核數考慮,2顆8核心 那麼都設置爲8吧
-----------------join及多條件範圍查詢優化-------------------------
read_rnd_buffer_size=8M # 多非主鍵索引查詢 回表優化,Multi-Range Read優化。這個優化的主要目的儘量使用順序度盤這,就是 MRR 優化的設計思路。此時,語句的執行流程變成了這樣:根據索引 a,定位到滿足條件的記錄,將 id 值放入 read_rnd_buffer 中 ;將 read_rnd_buffer 中的 id 進行遞增排序;排序後的 id 數組,依次到主鍵 id 索引中查記錄,並作爲結果返回。另外需要說明的是,如果你想要穩定地使用 MRR 優化的話,需要設置set optimizer_switch="mrr_cost_based=off,mrr=on"。(官方文檔的說法,是現在的優化器策略,判斷消耗的時候,會更傾向於不使用 MRR,把 mrr_cost_based 設置爲 off,就是固定使用 MRR 了。)
![在這裏插入圖片描述](https://img-blog.csdnimg.cn/20200618145804177.png)
MRR 能夠提升性能的核心在於,這條查詢語句在索引 a 上做的是一個範圍查詢(也就是說,這是一個多值查詢),可以得到足夠多的主鍵 id。這樣通過排序以後,再去主鍵索引查數據,才能體現出“順序性”的優勢。
optimizer_switch='mrr=on,mrr_cost_based=off,batched_key_access=on'; 開啓Batched Key Access算法,前期依賴於mrr算法