幾乎原生Mysql配置 執行Update語句卡住一直執行很長時間才返回(問題排查)

環境:
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配置基本原始,需要很多參數的優化。

記錄一些目前狀態可能有問題的指標。

  1. 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
  1. innodb redolog
redolog 一共2個 每個50M (太小了,性能測試一下壓滿導致強制刷頁)
  1. 慢查詢日誌未開啓
  2. buffer池
innodb_buffer_pool_size 134 217 728    ( 16 c  32g虛擬機這分配。。)
innodb_buffer_pool_chunk_size  134 217 728
  1. 髒頁刷新策略
	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);
}
  1. 緩存利用率
    innodb_buffer_pool_reads:表示InnoDB緩衝池無法滿足的請求數。需要從磁盤中讀取。
    innodb_buffer_pool_read_requests:表示從內存中讀取邏輯的請求數。
    在這裏插入圖片描述
    大概有3%的數據需要讀磁盤。應該降低到1%以下。

  2. 緩存池中數據表明緩存池太小,這個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
    表示對緩衝池執行的寫入次數。

  1. innodb_flush_neighbors=1 臨近數據髒頁刷新策略 默認是
ssd 的話不建議,影響本次響應回覆時間,機械硬盤可以開
  1. 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算法



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