MYSQL数据库调优
Posted zhangfu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MYSQL数据库调优相关的知识,希望对你有一定的参考价值。
mysql调优
硬件设备
1、多CPU多Core,内存永远不嫌大
2、使用RAID10多磁盘提IO能力或者用NAS,SAN
3、RAID要带电池、缓存,数据库服务需要带UPS(在线电源)
4、全千兆网络环境
系统调优
1、全部采用64位版本(64MYSQL)
2、选择稳定内核(权衡稳定,性能,功能)
3、调整系统默认配置参数(例如TCP/IP堆栈连接数),文件句柄数/进程个数
# vim /etc/sysctl.conf
net.ipv4.ip_local_port_range = 32768 61000
# vim /etc/security/limits.conf--需要重新登录mysql
mysql soft nofile 65535
mysql hard nofile 65535
mysql soft nproc 65535
mysql hard nproc 65535
# ulimit -HSn 65535
MySQL 安装优化
选择合适的发行版本
1. 二进制发行版(包括RPM 等包装好的特定二进制版本)
由于MySQL 开源的特性,不仅仅MySQL AB 提供了多个平台上面的多种二进制发行版本可以供大家选择,还有不少第三方公司(或者个人)也给我们提供了不少选择。(rpm/glibc)
使用MySQL AB 提供的二进制发行版本我们可以得到哪些好处?
a) 通过非常简单的安装方式快速完成MySQL 的部署;
b) 安装版本是经过比较完善的功能和性能测试的编译版本;
c) 所使用的编译参数更具通用性的,且比较稳定;
d) 如果购买了MySQL 的服务,将能最大程度的得到MySQL 的技术支持;
2. 源码安装
与二进制发行版本相比,如果我们选择了通过源代码进行安装,那么在安装过程中我们能够对MySQL所做的调整将会更多更灵活一些。因为通过源代码编译我们可以:
a) 针对自己的硬件平台选用合适的编译器来优化编译后的二进制代码;
b) 根据不同的软件平台环境调整相关的编译参数;
c) 针对我们特定应用场景选择需要什么组件不需要什么组件;
d) 根据我们的所需要存储的数据内容选择只安装我们需要的字符集;
e) 同一台主机上面可以安装多个MySQL;
f) 等等其他一些可以根据特定应用场景所作的各种调整。
在源码安装给我们带来更大灵活性的同时,同样也给我们带来了可能引入的隐患:
a) 对编译参数的不够了解造成编译参数使用不当可能使编译出来的二进制代码不够稳定;
b) 对自己的应用环境把握失误而使用的优化参数可能反而使系统性能更差;
c) 还有一个并不能称之为隐患的小问题就是源码编译安装将使安装部署过程更为复杂,所花费的时间更长;
重要的编译参数的介绍:
● “prefix”:设定安装路径,默认为“/usr/local”;
● “datadir”:设定MySQL 数据文件存放路径;
● “with-charset”:设定系统的默认字符集;
● “with-collation”:系统默认的校验编码的规则;
● “with-extra-charsets”:出了默认字符集之外需要编译安装的字符集;
● “with-unix-socket-path”:设定socket 文件地址;
● “with-tcp-port”:指定特定监听端口,默认为3306;
● “with-mysqld-user”:指定运行mysqld 的os 用户,默认为mysql;
● “without-query-cache”:禁用Query Cache 功能;
● “without-innodb”:禁用Innodb 存储引擎;
● “--enable-thread-safe-client”:以线程方式编译客户端;
● “with-pthread”:强制使用pthread 线程库编译;
● “with-named-thread-libs”:指定使用某个特定的线程库编译;
● “without-debug”:使用非debug 模式;
● “with-mysqld-ldflags”:mysqld 的额外link 参数;
● “with-client-ldflags”:client 的额外link 参数;
MySQL 日志设置优化
MySQL 的日志包括(分离日志文件和数据文件,把它们放不同存储):
1、错误日志(ErrorLog)
2、二进制日志(Binlog)
3、查询日志(Query Log)
4、慢查询日志(Slow Query Log)
5、事务日志 tr_logs(3group)-->innodb
1、在默认情况下,系统仅仅打开错误日志,关闭了其他所有日志,以达到尽可能减少IO 损耗提高系统性能的目的。
2、在生产系统中很少有系统会打开查询日志。因为查询日志打开之后会将MySQL 中执行的每一条Query 都记录到日志中,会该系统带来比较大的IO 负担,而带来的实际效益却并不是非常大。一般只有在开发测试环境中,为了定位某些功能具体使用了哪些SQL 语句的时候,才会在短时间段内打开该日志来做相应的分析。
3、在重要一点的实际应用场景中,都至少需要打开二进制日志,因为这是MySQL很多存储引擎进行增量备份的基础,也是MySQL 实现复制的基本条件。
4、为了调整mysql性能,定位执行较慢的SQL 语句,很多系统也会打开慢查询日志来记录执行时间超过特定数值(由我们自行设置)的SQL 语句。
调优方法:
1、把日志放在单独的存储上,提升mysql IO的性能。
2、Binlog 相关参数及优化策略:
mysql> show variables like ‘%binlog%‘;
+--------------------------------+------------+
| Variable_name | Value |
| binlog_cache_size | 1048576 |
| innodb_locks_unsafe_for_binlog | OFF |
| max_binlog_cache_size | 4294967295|
| max_binlog_size | 1073741824|
| sync_binlog | 0 |
“binlog_cache_size":在对数据增删改的过程中容纳二进制日志SQL 语句的缓存大小。
“max_binlog_cache_size”:和"binlog_cache_size"相对应,但是所代表的是binlog 能够使用的最大cache 内存大小
“max_binlog_size”:Binlog 日志最大值,一般来说设置为512M 或者1G,但不能超过1G。
“sync_binlog”:这个参数是对于MySQL 系统来说是至关重要的,他不仅影响到Binlog 对MySQL 所带来的性能损耗,而且还影响到MySQL 中数据的完整性。
a)sync_binlog=0,当事务提交之后,MySQL 不做fsync 之类的磁盘同步指令刷新binlog_cache 中的信息到磁盘,而让Filesystem 自行决定什么时候来做同步,或者cache 满了之后才同步到磁盘。
b)sync_binlog=n,当每进行n 次事务提交之后,MySQL 将进行一次fsync 之类的磁盘同步指令来将binlog_cache 中的数据强制写入磁盘。
c),“sync_binlog”设置为0 和设置为1 的系统写入性能差距可能高达5 倍甚至更多。
以下参数可以让我们控制需要复制或者需要忽略而不进行复制的DB 或者Table 的,分别为:
● Binlog_Do_DB:设定哪些数据库(Schema)需要记录Binlog;
● Binlog_Ignore_DB:设定哪些数据库(Schema)不要记录Binlog;
● Replicate_Do_DB:设定需要复制的数据库(Schema),多个DB 用逗号(“,”)分隔;
● Replicate_Ignore_DB:设定可以忽略的数据库(Schema);
● Replicate_Do_Table:设定需要复制的Table;
● Replicate_Ignore_Table:设定可以忽略的Table;
3、Slow Query Log 相关参数及使用建议
mysql> show variables like ‘log_slow%‘;
+------------------+-------+
| log_slow_queries | ON |
1 row in set (0.00 sec)
mysql> show variables like ‘long_query%‘;
+-----------------+-------+
| long_query_time | 1 |
1 row in set (0.01 sec)
log-slow-query=on
slow_query_log_file=/data/mysqld-slow.log
log-slow-admin-statements
log-queries-not-using-indexes
long_query_time=1 --超过1秒的查询都会被记录下来
log-short-format --短格式记录慢日志
Query Cache 优化(所有存储引擎都支持查询缓存):
查询缓存:
MySQL 的Query Cache 实现原理实际上并不是特别的复杂,简单的来说就是将客户端请求的Query语句(当然仅限于SELECT 类型的Query)通过一定的hash 算法进行一个计算而得到一个hash 值,存放在一个hash 桶中。同时将该Query 的结果集(Result Set)也存放在一个内存Cache 中的。存放Queryhash 值的链表中的每一个hash 值所在的节点中同时还存放了该Query 所对应的Result Set 的Cache 所在的内存地址,以及该Query 所涉及到的所有Table 的标识等其他一些相关信息。系统接受到任何一个SELECT 类型的Query 的时候,首先计算出其hash 值,然后通过该hash 值到Query Cache 中去匹配,如果找到了完全相同的Query,则直接将之前所Cache 的Result Set 返回给客户端而完全不需要进行后面的任何步骤即可完成这次请求。而后端的任何一个表的任何一条数据发生变化之后,也会通知QueryCache,需要将所有与该Table 有关的Query 的Cache 全部失效,并释放出之前占用的内存地址,以便后面其他的Query 能够使用。
--不适合数据更改频繁的应用场景
Query Cache 的相关系统参数变量和状态变量
mysql> show variables like ‘%query_cache%‘;
+------------------------------+-----------+
| Variable_name | Value|
| have_query_cache | YES |
| query_cache_limit | 1048576 |
| query_cache_min_res_unit | 4096 |
| query_cache_size | 268435456 |
| query_cache_type | ON |
| query_cache_wlock_invalidate | OFF |
● “have_query_cache”:该MySQL 是否支持Query Cache;
● “query_cache_limit”:Query Cache 存放的单条Query 最大Result Set ,默认1M;
● “query_cache_min_res_unit”:Query Cache 每个Result Set 存放的最小内存大小,默认4k;
● “query_cache_size”:系统中用于Query Cache 内存的大小;
● “query_cache_type”:系统是否打开了Query Cache 功能;
● “query_cache_wlock_invalidate”:针对于MyISAM 存储引擎,设置当有WRITE LOCK
如果我们要了解Query Cache 的使用情况,则可以通过Query Cache 相关的状态变量来获取,如通过如下命令:
mysql> show status like ‘Qcache%‘;
+-------------------------+------------+
| Qcache_free_blocks | 7499 |
| Qcache_free_memory | 190662000 |
| Qcache_hits | 1888430018|
| Qcache_inserts | 1014096388 |
| Qcache_lowmem_prunes | 106071885 |
| Qcache_not_cached | 7951123988 |
| Qcache_queries_in_cache | 19315 |
| Qcache_total_blocks | 47870 |
● “Qcache_free_blocks”:Query Cache 中目前还有多少剩余的blocks。如果该值显示较大,则说明Query Cache 中的内存碎片较多了,可能需要寻找合适的机会进行整理()。
● “Qcache_free_memory”:Query Cache 中目前剩余的内存大小。通过这个参数我们可以较为准确的观察出当前系统中的Query Cache 内存大小是否足够,是需要增加还是过多了;
● “Qcache_hits”:多少次命中。通过这个参数我们可以查看到Query Cache 的基本效果;
● “Qcache_inserts”:多少次未命中然后插入。通过“Qcache_hits”和“Qcache_inserts”两个参数我们就可以算出Query Cache 的命中率了:Query Cache 命中率= Qcache_hits / ( Qcache_hits + Qcache_inserts );
● “Qcache_lowmem_prunes”:多少条Query 因为内存不足而被清除出Query Cache。通过“Qcache_lowmem_prunes”和“Qcache_free_memory”相互结合,能够更清楚的了解到我们系统中Query Cache 的内存大小是否真的足够,是否非常频繁的出现因为内存不足而有Query 被换出
● “Qcache_not_cached”:因为query_cache_type 的设置或者不能被cache 的Query 的数量;
● “Qcache_queries_in_cache”:当前Query Cache 中cache 的Query 数量;
● “Qcache_total_blocks”:当前Query Cache 中的block 数量;
Query Cache 的限制
a) 5.1.17 之前的版本不能Cache 绑定变量的Query,但是从5.1.17 版本开始,Query Cache 已经开始支持帮定变量的Query 了;
b) 所有子查询中的外部查询SQL 不能被Cache;
c) 在Procedure,Function 以及Trigger 中的Query 不能被Cache;
d) 包含其他很多每次执行可能得到不一样结果的函数的Query 不能被Cache。
MySQL Server 其它常用优化:
1、网络连接优化:
● max_conecctions:整个MySQL 允许的最大连接数;
这个参数主要影响的是整个MySQL 应用的并发处理能力,当系统中实际需要的连接量大于max_conecctions 的情况下,由于MySQL 的设置限制,那么应用中必然会产生连接请求的等待,从而限制了相应的并发量。所以一般来说,只要MySQL 主机性能允许,都是将该参数设置的尽可能大一点。一般来说500 到800 左右是一个比较合适的参考值
● max_user_connections:每个用户允许的最大连接数;
上面的参数是限制了整个MySQL 的连接数,而max_user_connections 则是针对于单个用户的连接限制。在一般情况下我们可能都较少使用这个限制,只有在一些专门提供MySQL 数据存储服务,或者是提供虚拟主机服务的应用中可能需要用到。除了限制的对象区别之外,其他方面和max_connections 一样。这个参数的设置完全依赖于应用程序的连接用户数,对于普通的应用来说,完全没有做太多的限制,可以尽量放开一些。
● back_log:在MySQL 的连接请求等待队列中允许存放的最大连接请求数。
连接请求等待队列,实际上是指当某一时刻客户端的连接请求数量过大的时候,MySQL 主线程没办法及时给每一个新的连接请求分配(或者创建)连接线程的时候,还没有分配到连接线程的所有请求将存放在一个等待队列中,这个队列就是MySQL 的连接请求队列。当我们的系统存在瞬时的大量连接请求的时候,则应该注意back_log 参数的设置。系统默认值为50,最大可以设置为65535。当我们增大back_log 的设置的时候,同时还需要主义OS 级别对网络监听队列的限制,因为如果OS 的网络监听设置小于MySQL 的back_log 设置的时候,我们加大“back_log”设置是没有意义的。
2、线程连接优化(session/会话):
● thread_cache_size:Thread Cache 池中应该存放的连接线程数。
当系统最初启动的时候,并不会马上就创建thread_cache_size 所设置数目的连接线程存放在Thread Cache 池中,而是随着连接线程的创建及使用,慢慢的将用完的连接线程存入其中。当存放的连接线程达到thread_cache_size 值之后,MySQL 就不会再续保存用完的连接线程了。
● thread_stack:每个连接线程被创建的时候,MySQL 给他分配的内存大小。
当MySQL 创建一个新的连接线程的时候,是需要给他分配一定大小的内存堆栈空间,以便存放客户端的请求Query 以及自身的各种状态和处理信息。不过一般来说如果不是对MySQL 的连接线程处理机制十分熟悉的话,不应该轻易调整该参数的大小,使用系统的默认值(192KB)基本上可以所有的普通应用环境。如果该值设置太小,会影响MySQL 连接线程能够处理客户端请求的Query 内容的大小,以及用户创建的Procedures 和Functions 等。
我们现看看连接线程相关的系统变量的设置值:
mysql> show variables like ‘thread%‘;
+-------------------+--------+
| thread_cache_size | 64 |
| thread_stack | 196608 |
再来看一下系统被连接的次数以及当前系统中连接线程的状态值:
mysql> show status like ‘connections‘;
+---------------+-------+
| Connections | 127 |
mysql> show status like ‘%thread%‘;
+------------------------+-------+
| Delayed_insert_threads | 0 |
| Slow_launch_threads | 0 |
| Threads_cached | 4 |
| Threads_connected | 7 |
| Threads_created | 11 |
| Threads_running | 1 |
通过上面的命令,我们可以看出,系统设置了Thread Cache 池最多将缓存64 个连接线程,每个连接线程创建之初,系统分配192KB 的内存堆栈空间给他。系统启动到现在共接收到客户端的连接127 次,共创建了11 个连接线程,但前有7 个连接线程处于和客户端连接的状态,而7 个连接状态的线程中只有一个是active 状态,也就是说只有一个正在处理客户端提交的俄请求。而在Thread Cache 池中当共Cache 了4 个连接线程。
ThreadCache 命中率,也就是通过Thread Cache 池中取得连接线程的次数与系统接收的总连接次数的比率,如下:
Threads_Cache_Hit = (Connections - Threads_created) / Connections * 100%
我们可以通过上面的这个运算公式计算一下上面环境中的Thread Cache 命中率:Thread_Cache_Hit= (127 - 12) / 127 * 100% = 90.55%
常用存储引擎优化:
1、MyI SAM存储引擎优化(查询缓存+索引缓存)
2、Innodb存储引擎优化(查询缓存+innodb_buffer_pool(索引/修改数据))
物理块-->表-->索引(硬盘)-->索引缓存(buffer)-->查询缓存(cache)-->查询
1、MyI SAM存储引擎优化(所有写操作都是直接操作物理磁盘,所以只能优化它的查询功能)
1)MyISAM 索引缓存相关的几个系统参数和状态参数:
◆ key_buffer_size,索引缓存大小;
这个参数用来设置整个MySQL 中的常规Key Cache 大小。一般来说,如果我们的MySQL 是运行在32 位平台上,此值建议不要超过2GB 大小。如果是运行在64 位平台纸上则不用考虑此限制,但也最好不要超过4GB。
◆ key_buffer_block_size,索引缓存中的Cache Block Size;
在前面我们已经介绍了,在Key Cache 中的所有数据都是以Cache Block 的形式存在,而key_buffer_block_size 就是设置每个Cache Block 的大小,实际上也同时限定了我们将“.MYI”文件中的Index Block 被读入时候的File Block 的大小。
2、Innodb存储引擎优化
1)Innodb缓存相关优化
2)事务优化
3)数据存储优化
innodb_buffer_pool_size 参数用来设置Innodb 最主要的Buffer(Innodb_Buffer_Pool)的大小,也就是缓存用户表及索引数据的最主要缓存空间,对Innodb 整体性能影响也最大。官方建议将Innodb 的Buffer Pool 设置为整个系统物理内存的50% ~ 80% 之间。
mysql> show status like ‘Innodb_buffer_pool_%‘;
+-----------------------------------+-------+
| Innodb_buffer_pool_pages_data | 70 |
| Innodb_buffer_pool_pages_dirty | 0 |
| Innodb_buffer_pool_pages_flushed | 0 |
| Innodb_buffer_pool_pages_free | 1978|
| Innodb_buffer_pool_pages_latched | 0 |
| Innodb_buffer_pool_pages_misc | 0 |
| Innodb_buffer_pool_pages_total | 2048 |
| Innodb_buffer_pool_read_ahead_rnd | 1 |
| Innodb_buffer_pool_read_ahead_seq | 0 |
| Innodb_buffer_pool_read_requests | 329 |
| Innodb_buffer_pool_reads | 19 |
| Innodb_buffer_pool_wait_free | 0 |
| Innodb_buffer_pool_write_requests | 0 |
从上面的值我们可以看出总共2048 pages,还有1978 是Free 状态的仅仅只有70 个page 有数据,read 请求329 次,其中有19 次所请求的数据在buffer pool 中没有,也就是说有19 次是通过读取物理磁盘来读取数据的,所以很容易也就得出了Innodb Buffer Pool 的Read 命中率大概在为:(329 - 19)/ 329 * 100% = 94.22%。
innodb_additional_mem_pool_size 所设置的是用于存放Innodb 的字典信息和其他一些内部结构所需要的内存空间。所以我们的Innodb 表越多,所需要的空间自然也就越大,系统默认值仅有1MB。当然,如果Innodb 实际运行过程中出现了实际需要的内存比设置值更大的时候,Innodb 也会继续通过OS来申请内存空间,一个常规的几百个Innodb 表的MySQL,如果不是每个表都是上百个字段的话,20MB 内存已经足够了。
◆ innodb_flush_log_at_trx_commit = 0,Innodb 中的Log Thread 每隔1 秒钟会将log buffer中的数据写入到文件,同时还会通知文件系统进行文件同步的flush 操作,保证数据确实已经写入到磁盘上面的物理文件。但是,每次事务的结束(commit 或者是rollback)并不会触发Log Thread 将log buffer 中的数据写入文件。所以,当设置为0 的时候,当MySQL Crash 和OS Crash 或者主机断电之后,最极端的情况是丢失1 秒时间的数据变更。(fsync)
◆ innodb_flush_log_at_trx_commit = 1,这也是Innodb 的默认设置。我们每次事务的结束都会触发Log Thread 将log buffer 中的数据写入文件并通知文件系统同步文件。这个设置是最安全的设置,能够保证不论是MySQL Crash 还是OS Crash 或者是主机断电都不会丢失任何已经提交的数据。(fsync)
◆ innodb_flush_log_at_trx_commit = 2,当我们设置为2 的时候,Log Thread 会在我们每次事务结束的时候将数据写入事务日志,但是这里的写入仅仅是调用了文件系统的文件写入操作。而我们的文件系统都是有缓存机制的,所以Log Thread 的这个写入并不能保证内容真的已经写入到物理磁盘上面完成持久化的动作。文件系统什么时候会将缓存中的这个数据同步到物理磁盘文件Log Thread 就完全不知道了。所以,当设置为2 的时候,MySQL Crash 并不会造成数据的丢失,但是OS Crash 或者是主机断电后可能丢失的数据量就完全控制在文件系统上了。(async)
索引规划原则:
1、不使用唯一索引
2. 为了尽量减小secondary index 的大小,提高访问效率,作为主键的字段所占用的存储空间越小越好,最好是INTEGER 类型。当然这并不是绝对的,字符串类型的数据同样也可以作为Innodb 表的主键;
3. 创建表的时候尽量自己指定相应的主键,让数据按照自己预设的顺序排序存放,提高特定条件下的访问效率;
4. 尽可能不要在主键上面进行更新操作,减少因为主键值的变化带来数据的移动。
5. 尽可能提供主键条件进行查询;
分散IO 提升磁盘响应:
建议将数据文件和事务日志文件分别存放于不同的物理磁盘上面以降低磁盘的相互争用,提高整体IO 性能。
可以通过以下两个参数指定:
innodb_log_group_home_dir 参数来指定Innodb 日志存放位置,同时再通过设置数据文件位置
innodb_data_home_dir 参数来告诉Innodb 我们希望将数据文件存放在哪里。
innodb_autoextend_increment 参数让我们可以自行控制表空间文件每次增加的大小。
skip-locking--忽略外部文件锁定(如VFS层的锁定)
skip-networking--启动mysql时不启动网络模块,只允许通过socket连接。
编写常用my.cnf配置文件:
# mysql -N -s -uroot -p456 -e "show variables;" > /root/mysql.cnf
# cat /root/mysql.cnf | sed ‘s/_/-/g‘ |awk ‘{print $1"="$2}‘ > /root/my.cnf
以上是关于MYSQL数据库调优的主要内容,如果未能解决你的问题,请参考以下文章
Day810.MySQL调优之事务:高并发场景下的数据库事务调优 -Java 性能调优实战