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 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
redolog 一共2个 每个50M (太小了,性能测试一下压满导致强制刷页)
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((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
表示对缓冲池执行的写入次数。
ssd 的话不建议,影响本次响应回复时间,机械硬盘可以开
#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算法