几乎原生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算法



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