优化服务器设置
- 1、MySQL的配置是如何工作的
-
- [1.1 MySQL配置文件](#1.1 MySQL配置文件)
- [1.2 语法、作用于和动态性](#1.2 语法、作用于和动态性)
- [1.3 设置变量的副作用](#1.3 设置变量的副作用)
- 2、不可取的调整参数方法
- 3、配置内存使用
-
- [3.1 每个连接的内存需求](#3.1 每个连接的内存需求)
- [3.2 为操作系统保留内存](#3.2 为操作系统保留内存)
- [3.3 InnoDB缓冲池](#3.3 InnoDB缓冲池)
- [3.4 线程缓存](#3.4 线程缓存)
-
- [3.4.1 MySQL线程池相关配置参数](#3.4.1 MySQL线程池相关配置参数)
- [3.4.2 MySQL线程池工作原理](#3.4.2 MySQL线程池工作原理)
- 4、配置MySQL的IO行为
如有侵权,请联系~
如有错误,也欢迎批评指正~
本篇文章大部分是来自学习《高性能MySQL》的笔记
1、MySQL的配置是如何工作的
确定合适的配置不是从研究配置选项并咨询如何配置开始的,而是需要了解MySQL的内部结构和行为。最佳的配置文件不仅仅根据硬件配置,还要结合工作负载、数据和应用程序的需求来配置。
通常更好的做法是正确的配置基本设置【MySQL默认设置是有充分理由的,修改潜在的风险比较大】,并将更多的时间还在scheme优化、索引和查询的设计上。在不了解其影响的范围下,根据网上论坛修改配置可能会出现崩溃、卡顿、性能下降等问题。
比较好的办法就是使用默认配置。如果真的需要修改,首先保持InnoDB缓冲池、日志文件大小等基础设置合适,再确定是由服务器的哪个部分引起的,可以通过相应的配置选项进行纠正。
1.1 MySQL配置文件
MySQL获取配置信息:命令行参数和配置文件中的配置项。
可以通过命令查看配置文件:
shell
mysqld --verbose --help | grep 'my.cnf'
可以得到如下结果。MySQL读取配置文件会根据如下顺序进行读取:
sql
/etc/my.cnf
/etc/mysql/my.cnf
/usr/local/mysql/my.cnf ~/.my.cnf
如果 MySQL 是新安装的,可能默认情况下并没有创建配置文件。你可以手动创建一个默认的配置文件。
my.cnf配置文件如下:
powershell
[client]
port = 3306
socket = /usr/local/services/mysql/var/data/mysql.sock
[mysqld]
bind-address = 0.0.0.0
port = 3306
socket = /usr/local/services/mysql/var/data/mysql.sock
pid-file = /usr/local/services/mysql/var/logs/mysql.pid
character-set-server = utf8
basedir = /usr/local/services/mysql
datadir = /usr/local/services/mysql/var/data
skip-external-locking
skip-name-resolve
lower_case_table_names = 1
log-bin-trust-function-creators = 1
max_connections = 6000
max_user_connections = 6000
max_connect_errors = 4000
wait_timeout = 86400
interactive_timeout = 86400
table_open_cache = 512
max_allowed_packet = 32M
sort_buffer_size = 2M
join_buffer_size = 2M
thread_cache_size = 8
thread_concurrency = 8
query_cache_size = 32M
#default-storage-engine = InnoDB
#sql_mode="STRICT_ALL_TABLES,NO_AUTO_CREATE_USER"
server-id = 1
log-short-format
log-error = /usr/local/services/mysql/var/logs/mysql.log
slow_query_log
long_query_time = 2
slow_query_log_file = /usr/local/services/mysql/var/logs/mysql-slow.log
log-bin = /usr/local/services/mysql/var/binlog/mysql-bin
log_bin_trust_function_creators=1
binlog_format = MIXED
expire_logs_days = 10
# INNODB Specific options
innodb_data_home_dir = /usr/local/services/mysql/var/data
innodb_log_group_home_dir = /usr/local/services/mysql/var/redolog
innodb_additional_mem_pool_size = 10M
innodb_buffer_pool_size = 4G
innodb_data_file_path = ibdata1:100M:autoextend
innodb_file_io_threads = 4
innodb_thread_concurrency = 8
innodb_flush_log_at_trx_commit = 0
innodb_flush_method = O_DIRECT
innodb_log_buffer_size = 128M
innodb_log_file_size = 256M
innodb_log_files_in_group = 3
innodb_max_dirty_pages_pct = 90
innodb_lock_wait_timeout = 50
innodb_file_per_table = 1
# MyISAM Specific options
key_buffer_size = 384M
read_buffer_size = 4M
read_rnd_buffer_size = 8M
myisam_sort_buffer_size = 128M
myisam_max_sort_file_size = 1G
myisam_repair_threads = 1
myisam_recover
[mysqldump]
quick
max_allowed_packet = 16M
[mysql]
default-character-set = utf8
no-auto-rehash
socket = /usr/local/services/mysql/var/data/mysql.sock
[myisamchk]
key_buffer_size = 256M
sort_buffer_size = 256M
read_buffer = 2M
write_buffer = 2M
[mysqlhotcopy]
interactive-timeout
可以在配置文件中增加、删除或者修改相关配置。
1.2 语法、作用于和动态性
系统变量的三种设置方式:启动时命令行、配置文件、动态设置【部分变量】。
无论是命令行还是配置文件,配置设置全部使用小写,单词之间可以用下划线或者短横线分割,但是最好保持一种风格。
启动时命令行:
powershell
mysqld --auto_increment_offset=5
or
mysqld --auto-increment-offset=5
配置文件:
上述my.cnf配置文件可以看到
动态配置:
配置设置可以有多个作用域,分别是服务器级别【全局作用域】、每个连接不同【会话作用域】。如:
sql
// 会话级别
SET SESSION sort_buffer_size = value;
SET @@session.max_connections = 100;
// 设置全局作用域
SET GLOBAL sort_buffer_size = value;
SET @@global.max_connections = 200;
这种方式无论是设置全局作用域还是会话作用域,MySQL服务器重启之后都会失效。如果想要保持设置,必须更新配置文件。
可以通过命令查看当前配置的值:
sql
SHOW GLOBAL VARIABLES;
SHOW SESSION VARIABLES;
如果设置了全局变量,当前会话和其他现在的会员都不会受影响,只有新创建的会话才会起作用。如果客户端以来服务器的长连接,可能需要查看是否符合预期。
持久化系统变量
MySQL8.0引入一个名为持久化系统变量的新功能,即用户在系统启动时通过该语法设置全局系统变量,MySQL将这个配置写入磁盘,以便下次重启之后继续使用这个值。传统的配置文件,设置之后需要MySQL重启之后才能生效。
sql
SET PERSIST max_connections = 500;
1.3 设置变量的副作用
动态设置变量可能会引起意想不到的副作用,在线修改一定要小心,可能会导致服务器执行大量工作。例如:
- read_buffer_size:只有当查询需要时,MySQL才会为该缓冲区分配内存,而且会立即分配变量指定的整块内存。
- read_rnd_buffer_size:只有当查询需要时,MySQL才会为该缓冲区分配内存,但是只会按需分配内存。
如果在运行的时候设置read_buffer_size会不会导致内存不足情况。
read_buffer_size、sort_buffer_size等变量都是会话级别的,所以,当一个会话结束之后,MySQL就会自动回收掉相应的读缓冲区、排序缓冲区。
2、不可取的调整参数方法
- 建立一个基准测试,然后通过不断的迭代配置来调优服务器,以寻找最佳配置。通常不建议这种方式,因为这需要大量的工作和研究,但是回报比较少,时间浪费多。不如优化索引等操作
- 按比率调优。这是一个经验法则,例如InnoDB缓冲池的命中率比较低,就增加缓存大小,这是不对的。 Oracle DBA几年前就已经放弃了这种方式调优
- 使用网上的一些调优脚本,根据博客论文的建议进行调整
- 内存消耗公式。MySQL在崩溃的时候会输出这个公式,但是这个公式已经是很久之前的,不可靠。
3、配置内存使用
在配置文件中有一个参数innodb_dedicated_server,用于指示 InnoDB 是否在专用数据库服务器上运行,从而优化内存分配和其他资源分配,提供更好的性能。如果 innodb_dedicated_server 设置为 ON,InnoDB 会根据可用的内存进行更有针对性的调整,例如根据总内存的大小自动调整某些 InnoDB 缓冲区的大小。innodb_dedicated_server通常将50%~75%的的内存分配给InnoDB存储引擎,其他的内存可用于每个连接的内存分配、操作系统开销和其他内存设置。
3.1 每个连接的内存需求
MySQL只需要少量的内存就可以保持一个连接,同时还需要基本的内存量来执行任何特定的查询。可以计算峰值时候的内存,例如最大连接打满并且执行最复杂的大型查询,查看此时的内存消耗。这里指的是MySQL服务器占用的内存,因为可能涉及到排序等操作。
3.2 为操作系统保留内存
同样也需要为操作系统保留足够多的内存完成其工作,包括但不限于本地监控、配置管理、计划作业等等。操作系统内存是否足够的判断依据:没有讲虚拟内存主动交换到磁盘。
3.3 InnoDB缓冲池
InnoDB缓冲池需要的内存比任何组件都多,而且InnoDB缓冲池大小也是最重要的变量。InnoDB缓冲池包括:缓存索引、缓存数据、自适应哈希索引、更改缓冲区、锁、插入缓冲区和其他内部结构等等。
当然如果数据库中数据不多并且不会快速增加,也不需要内存过度的分配给缓冲池,没有必要造成浪费;但是一个快速增长的数据库提前规划,设置一个大的缓冲池没有问题。
大型的缓冲池也会带来一些挑战,例如关闭时间、预热时间更长等。在数据库关闭的时候,因为缓冲池比较大会存在大量的脏数据【已经修改但还没刷新到磁盘的数据】需要写到数据文件中,这就会导致比较长的关闭时间。当然可以强制关闭,但是当下次重启的时候会有大量的时间进行恢复数据,即关闭和恢复时间是没办法减少。
当下次启动的时候缓冲池就是空的,缓存的好处就没了。但是可以使用innodb_buffer_pool_dump_at_shutdown【InnoDB 在服务器关闭时会将缓冲池中的活跃数据页转储到磁盘(通常会存在数据文件中)】和innodb_buffer_pool_load_at_startup【直接从转储的数据中读取】两个参数进行预热缓冲池。虽然启动时需要加载时间,但是比自然查询填充性能要好。
3.4 线程缓存
线程缓冲保存了当前没有与连接关联但已经准备好为新连接提供服务的线程。创建新连接时,如果缓存中有一个线程,MySQL会从缓存中取出该线程并将其提供给新连接。当连接关闭时,如果缓存中还有空间,MySQL会讲线程放回缓存中;如果缓冲中已经没有空间【超过thread_cache_size】,MySQL会销毁线程。
- 连接池:MySQL连接池是MySQL的客户端实现的,这是一个用于管理和复用数据库连接的机制,避免频繁建立和关闭数据库连接带来的性能损失。客户端连接池在应用程序启动时创建若干数据库连接,并在后续的数据库操作中复用这些连接。应用程序请求连接后,可以在使用完成后将连接返回连接池,而不是关闭连接。
- 线程池:是在 MySQL 服务器端 实现的。它用于管理数据库内部的工作线程,旨在提高服务器对并发请求的处理能力。线程池允许 MySQL 服务器优化并发连接的处理,通过重用线程,减少线程创建和销毁的开销,从而提高响应速度和资源利用率。
3.4.1 MySQL线程池相关配置参数
这里讲到了线程缓存的配置,正好恶补一下MySQL的线程池原理【与java正常的线程池略有不同】。参考文献:MySQL线程池
每一个连接到数据库都需要CPU分配线程栈,进行身份验证、建立上下文信息、执行请求、返回结果,关闭连接,释放资源。并且并发访问时,资源会被不断请求和释放,请求释放会大量消耗资源,导致资源率用率降低。因此线程池是一个很好的解决方案。预先创建一定是数量的线程,当有请求访问时,从线程池中分配一个线程提供服务,请求结束后,该线程又去服务其他请求,避免了线程和内存对象的频繁创建和释放,提高资源利用率,减少上下文切换和资源竞争。
sql
show variables like 'thread%';
+-------------------+---------------------------+
| Variable_name | Value |
+-------------------+---------------------------+
| thread_cache_size | 9 |
| thread_handling | one-thread-per-connection |
| thread_stack | 1048576 |
+-------------------+---------------------------+
thread_cache_size: 缓存起来可重用的线程数目
线程有三种处理方式【thread_handling线程模型】:
- No-Threads:主要用于调试,只有一个线程处理所有的连接请求
- one-thread-per-connection:针对于每个连接都会创建一个新的线程
- pool-of-threads: 使用线程池进行处理请求
thread_stack 每个线程堆栈大小,也就是线程被创建的时候,mysql给他分配的内存空间,单位是byte
3.4.2 MySQL线程池工作原理
MySQL线程池整体框架,参考文献【MySQL线程池讲解】:
从架构图中可以看到Thread Pool由一个Timer线程和多个Thread Group【Thread_pool_size决定group的个数】组成,而每个Thread Group又由两个队列、一个listener线程和多个worker线程构成。下面分别来介绍各个部分的作用:
-
队列(高优先级队列和低优先级队列)用来存放待执行的IO任务,分为高优先级队列和低优先级队列,高优先级队列的任务会优先被处理。
什么任务会放在高优先级队列呢?
- 事务中的语句会放到高优先级队列中,比如一个事务中有两个update的SQL,有1个已经执行,那么另外一个update的任务就会放在高优先级中。这里需要注意,如果是非事务引擎,或者开启了Autocommit的事务引擎,都会放到低优先级队列中。
- 还有一种情况会将任务放到高优先级队列中,如果语句在低优先级队列停留太久,该语句也会移到高优先级队列中,防止饿死。
-
listener线程:listener线程监听该线程group的语句,并确定当自己转变成worker线程,是立即执行对应的语句还是放到队列中,判断的标准是看队列中是否有待执行的语句。如果队列中待执行的语句数量为0,而listener线程转换成worker线程,并立即执行对应的语句。如果队列中待执行的语句数量不为0,则认为任务比较多,将语句放入队列中,让其他的线程来处理。这里的机制是为了减少线程的创建,因为一般SQL执行都非常快。
-
worker线程:worker线程是真正干活的线程。
-
Timer线程:Timer线程是用来周期性检查group是否处于处于阻塞状态,当出现阻塞的时候,会通过唤醒线程或者新建线程来解决。
整体流程简述:
Step1:请求连接到MySQL,根据threadid%thread_pool_size确定落在哪个group
Step2:group中的listener线程监听到所在的group有新的请求以后,检查队列中是否有请求还未处理。如果没有,则自己转换为worker线程立即处理该请求,如果队列中还有未处理的请求,则将对应请求放到队列中,让其他的线程处理;
Step3:group中的thread线程检查队列的请求,如果队列中有请求,则进行处理,如果没有请求,则休眠,一直没有被唤醒,超过thread_pool_idle_timeout后就自动退出。线程结束。当然,获取请求之前会先检查group中的running线程数是否超过thread_pool_oversubscribe+1,如果超过也会休眠;
Step4:timer线程定期检查各个group是否有阻塞,如果有,就对wokrer线程进行唤醒或者创建一个新的worker线程。
4、配置MySQL的IO行为
一些配置选项会影响MySQL将数据同步到磁盘和执行恢复的方式,这会涉及到I/O操作,因此会极大的影响性能。这些选项是性能和数据安全的权衡。
InnoDB事务日志: 日志有大小限制,进行循环写入,通过参数innodb_log_file_size 和innodb_log_files_in_group控制。InnoDB使用后台线程智能化的刷新对数据文件的更改,该线程可以将写入分组,并使数据写入顺序化,以提高效率。实际上,事务日志可以将随机数据文件I/O转换为顺序日志文件I/O和顺序数据文件I/O。
表空间相关配置:
查看表空间文件所在的目录:
sql
show variables like 'innodb_data_%';
innodb_data_file_path ibdata1:100M;ibdata2:100M:autoextend:max:2G
innodb_data_home_dir /opt/local/mysql/var/
指定了表空间的位置,以及大小。如果存储满了,最后一个文件会进行扩展,但是指定了最后一个文件最大扩展也就2G。这种方式是innodb_file_per_table=0。
innodb_file_per_table=0:共享表空间。所有表中的数据的存储到ibdata文件中。
innodb_file_per_table=1:独立表空间。innodb会给每个表创建单独的文件、表空间。每个表中的数据都存储在自己的ibd文件中。
参数 | 含义 |
---|---|
innodb_log_buffer_size | 控制日志缓冲区大小。Innodb修改数据的时候会先将修改记录写入到日志缓冲区,保存在内存中。当缓冲区满了或者事务提交或者每秒一次,InnoDB就会将缓冲区刷新到磁盘上的日志文件中。如果有大型事务,可以增加缓冲区的大小。通常不需要设置的太大,建议1~8MB。 |
innodb_flush_log_at_trx_commit | 控制日志缓冲区的刷新位置和刷新频率。 0: 每秒定时将日志缓冲区写入到日志文件,并刷新日志文件,但是在事务提交的时候不做任何操作。 -1: 每次事务提交时,将日志缓冲区写入到日志文件,并将其刷新到持久存储中。这是默认的,也是最安全的。 2: 每次事务提交时豆浆日志缓冲区写入到日志文件中,但不执行刷新。即刷新到操作系统的缓存中。 |
innodb_flush_method | 控制InnoDB与文件系统的实际交互方式,不仅仅是写入数据的方式,而且会影响读取数据的方式。并且针对于日志文件和数据文件都会有影响。三种取值:fdatasync、O_DSYNC、O_DIRECT。innodb_flush_method理解 |
innodb_thread_concurrency | 限制进入innodb内核同时工作的线程数 |
innodb_thread_sleep_delay | 如果内核中的线程已经超过了允许的最大线程innodb_thread_concurrency,则新的线程不能进入内核。innoDB使用两阶段的过程尝试让线程尽可能高效的进入内核。先让线程睡眠innodb_thread_sleep_delay再重试。如果还是不可以则进入等待队列 |
innodb_concurrency_tickets | 进入到内核的线程每执行一次SQL就会消费一个门票,在门票消费完之前不需要前面的判断,直接进入。如果消费完了,则这个线程重新走上述流程【innodb_thread_concurrency、innodb_thread_sleep_delay】。可以简单理解为,进入到内核的线程最多执行innodb_concurrency_tickets这么多次SQL。再多执行重新排队 |
max_connections | MySQL数据库服务器的参数,用于控制通过传统的MySQL协议(通常使用标准的MySQL客户端连接)连接到服务器的最大连接数。它指定了服务器可以同时接受的最大客户端连接数量。如果尝试建立的新连接超过这个限制,服务器将拒绝连接请求。这个参数的默认值通常是150。这个是最大的文件描述符个数,例如netty进行IO的时候会检测文件描述符,感受到连接有数据,会调用线程池执行 |
max_connect_errors | 如果MySQL服务器连续接收到了来自于同一个主机的请求,而且这些连续的请求全部都没有成功的建立连接就被中断了,当这些连续的请求的累计值大于max_connect_errors的设定值时,MySQL服务器就会阻止这台主机后续的所有请求。max_connect_errors排查 |