第18章 MySQL Server调优
本章将为读者介绍针对MySQL Server的优化,这也是DBA最熟悉的领域之一。
首先咱们介绍MySQL的主要参数,而后,讲述常见硬件资源的优化。
咱们假设读者已经具有了足够的基础知识,因此,本章将更多的针对一些特定的主题进行叙述。
18.1 概述
衡量数据库性能的指标,通常衡量数据库的性能有两个指标:响应时间和吞吐率。
响应时间又包括等待时间和执行时间。
咱们进行优化的主要目的是下降响应时间,提升吞吐率。
下面咱们来看下MySQL是如何执行优化和查询的? 大体的步骤以下所示。
1)客户端发送SQL语句给服务器。
2)若是启用了Query Cache,那么MySQL将检查Query Cache,若是命中,就返回Query Cache里的结果集,不然,执行下一个步骤。
3)MySQL Parser解析SQL,MySQL优化器生成执行计划。
4)MySQL查询处理引擎执行此执行计划。
MySQL性能优化显然是要对以上的部分环节或全部环节进行优化,尽可能下降各个环节的时间,以提升吞吐率。
对于性能的优化,正确的策略是衡量各个环节的开销,优化开销大的环节,而不是使用网上的一些所谓的参数调优和脚本调优,
由于他们并非针对你的特定状况而进行的调优,只是一些泛泛的建议,每每帮助不大。
对于客户端来讲,发送SQL语句的开销通常很小,若是是响应缓慢的网络,网络延时较高,那么能够考虑使用长链接或链接池等手段进行加速,
或者一次发送多条语句,或者使用存储过程等手段减小网络包的往返次数。
本书主要聚焦于后面的3个步骤,咱们须要关注Query Cache是如何加速响应;如何进行查询优化,生成良好的执行计划;
实际查询处理过程当中对于I/O、CPU、内存等资源的使用是怎样的。
咱们须要尽可能确保高效地利用资源,突破资源的限制。
以前的开发篇和运维篇章已经讲述了许多基础知识,这里再也不赘述,本章我将主要从系统资源和MySQL参数设置的角度,讲述一些咱们须要关注的优化点。 node
18.2 MySQL的主要参数
本节将列举一些主要的参数,下面将详细介绍各个参数。
1.innodb_buffer_pool_size
一个简单的策略是若是数据库很大,远远超过内存,那么应设置尽量大的缓冲池(buffer pool)。
若是数据库较小,通常来讲,缓冲池的大小设置为稍大于数据库的10%就能够了,大于10%是由于MySQL不仅是缓存数据页,还有一些额外的开销。
若是咱们使用ps命令检查MySQL实际占用的内存,就会发现实际分配的内存会比咱们设定的内存要大一些。
更合适的策略是衡量你的热点数据的大小,若是设置的缓冲区能容纳绝大部分的热点数据,没有产生过多的物理读,那么这个设置就是比较合理的设置。
须要注意的是,不要设置得过大,由于咱们须要给操做系统和其余程序预留内存,增长的负荷也会致使更多的内存使用,
咱们还可能须要预留内存以用于文件缓存,好比InnoDB的日志文件、MySQL的二进制日志文件等。
要注意32位系统的内存限制,超过了内存限制,可能会致使实例崩溃,系统宕机。
通常计算MySQL须要多少内存比较难,也难以预测,比较可靠的方式是查看目前的生产环境的内存消耗。
MySQL所消耗的内存还和链接数有关,每一个链接所消耗的内存总量将依赖于负荷,
若是查询很复杂,那么它会消耗更多的内存,若是只是简单的查询,平时基本上是sleep的状态,那么它实际占用的内存就不多,
因此,对于链接数多的业务,你应该实际观察下,操做系统下实际占用的内存和你设置的InnoDB缓冲池之间的区别,
以衡量是否要调整设置,设置一个更安全一点的参数值,以避免链接数暴涨消耗了过多的内存。
图18-1描述了存储的访问层次,对于图18-1中所示的架构,上一层应尽量缓存下一层的“热点”数据,也就是说,咱们须要平衡好内存和磁盘的成本,尽可能避免磁盘访问,
对于MySQL来讲,内存的主要部分就是InnoDB缓冲池,优化好了InnoDB缓冲池的访问,就成功了一大半,
通常采起的策略是利用空间和时间的局部性,频繁地访问,应该尽量去访问内存而不是磁盘,
因为数据的访问可能会很复杂,也许1%的缓存未命中(cache miss)须要几十乃至上百GB的内存来避免,而不只仅是cache miss对应的数据大小,
因此咱们还要充分理解数据库的物理设计和逻辑设计,设计出合理的方案,减小cache miss致使的物理读。
须要清楚的一个道理是,通常而言,数据库系统的专用存储系统比操做系统的存储系统高效得多。
InnoDB缓冲池就是如此,而MyISAM仍然是须要OS来缓存数据的,加速访问,因此每每表现得不那么好。 mysql
2.innodb_flush_method
这个选项只在Unix系统上有效。
若是这个选项被设置为fdatasync(默认值),那么InnoDB将使用fsync()来刷新数据和日志文件。
若是被设置为O_DSYNC,那么InnoDB将使用O_DSYNC来打开并刷新日志文件,但使用fsync()来刷新数据文件。
若是指定了O_DIRECT(在一些GNU/Linux版本上可用),那么InnoDB将使用O_DIRECT来打开数据文件,并使用fsync()来刷新数据和日志文件。
笔者的建议是设置innodb_flush_method=O_DIRECT,由于这样设置能够避免双重缓冲,让数据库跳过文件系统缓冲直接和设备进行交互,
须要留意的是,若是你的磁盘作了RAID,那么你必须使用带电池的RAID卡。 sql
3.innodb_log_file_size
日志组里每一个日志文件的大小。
早期的版本中,在32位的计算机上,日志文件的合并大小必须小于4GB。默认是5MB。
不太好去肯定innodb_log_file_size这个参数的大小,早期MySQL版本的配置文件里建议的InnoDB缓冲大小的25%是没有什么道理的,
首先InnoDB缓冲不必定就设置对了,并且InnoDB事务日志通常和你的日志写入量、写入频率有关系,和你的缓冲池大小不存在必然的关系。
MySQL的灾难恢复分为redo和undo两个过程,redo即找到日志文件里记录的已经更改了可是并未写入数据文件的记录,而后应用这些日志。
undo即回滚那些没有提交的操做,undo的时候,数据库已经能够访问了,可是undo的那部分数据还不能更改。
MySQL在切换事务日志的时候,可能会进行一次“check point”的操做,将部分数据写入到磁盘,也就是说,要确保咱们的缓存里比日志还旧的数据写入了磁盘。
其余时刻也可能发生“check point”。
若是数据库宕机,MySQL从新启动,那么,它会去事务日志里找到“check point”的标记信息。
在这个“check point”标记以前的数据, 咱们能够认为都已经写入到了磁盘。
那么,咱们就只须要执行这个“check point”以后的全部操做,应用这些日志到数据库便可。
事务日志不能太小,不然可能会致使性能问题。
事务日志是循环写的,先写第一个日志文件,再写第二个日志文件,而后又会去写第一个日志文件,而在覆盖旧的日志以前,须要确保咱们的缓存里比日志还旧的数据已经写入磁盘。
若是事务日志太小,那么磁盘的I/O操做就可能会变得很频繁,由于MySQL必须写入一些脏数据到数据文件中。
一次性刷新大量数据,可能会致使性能降低。
事务日志也不能太大了,由于这个时刻,咱们的“check point”会不怎么频繁,那么MySQL的灾难恢复可能须要更长的时间,由于它须要应用更多的日志。
生产环境的恢复速度将取决于应用日志的进度,一个1GB的事务日志,若是要所有应用,有可能须要应用半个小时以上来执行恢复。
咱们能够配置事务日志能够写入半个小时到1个小时的日志。这样对于大部分应用已经足够了。
过小了,会频繁切换日志;太大了,可能会致使故障恢复的时间过长。个人经验值是256~512MB。
日志的写入量能够查看变量innodb_os_log_written。咱们能够每隔一分钟查看一次,统计每分钟写入的日志量。
下面的命令将会每分钟检查一第二天志的写入量。
mysqladmin extended -uroot -pxxxxxxxx -r -i 60 -c 3 |grep "innodb_os_log_written"
若是由上面的命令得知每分钟写入量为10MB,那么咱们配置能够连续写45分钟的日志。
默认有2个日志,那么每一个日志的大小=10*45/2=225,大约等于 256MB,那么咱们能够配置innodb_log_file=256MB。
若是得出的结论是InnoDB日志须要几个GB那么大,那么极可能是不正常的,你要深究为何会写入这么大的日志,为何有这么多/大的变动,你可能须要在应用层就规避这种状况。 数据库
4.innodb_flush_log_at_trx_commit
当innodb_flush_log_at_trx_commit被设置为0时,日志缓冲将每秒一次被写到日志文件中,而且对日志文件进行磁盘操做的刷新,可是在事务提交时不进行任何操做。
当这个值为1(默认值)时,在每一个事务进行提交时,日志缓冲将被写到日志文件,且把对日志文件的变动刷新到磁盘中。
当设置为2时,在每一个事务进行提交时,日志缓冲将被写到文件,但不会对日志文件进行到磁盘操做的刷新,对日志文件的刷新每秒发生一次。
咱们能够看到,设置为2比设置为0更安全。
咱们的生产环境通常推荐设置为innodb_flush_log_at_trx_commit=2,由于它能够兼顾效率和必定的安全性,理想状况下,最多可能丢失1秒的事务。
若是设置为1, 则对于性能的影响会很大,由于每次提交事务,都会伴随着磁盘I/O的操做,须要把数据刷新到磁盘,I/O可能会成为瓶颈,
对于高安全性的数据,在可以知足I/O性能的前提下,能够考虑将其设置为1。缓存
5.sync_binlog
这个参数是设置,每当写了sync_binlog次二进制日志后,把日志实际刷新到磁盘中,默认值是0,不与硬盘同步。
绝大部分公司的生产环境广泛使用的是auto commit模式(自动事务提交),每次写一个语句,就会写一次二进制日志,若是不是自动事务提交,那么每一个事务将写入一次二进制日志。
若是设置为1,那么最多丢失1条记录(事务),这是最安全的选择。
生产环境的推荐设置是8~20,这样能够兼顾效率和安全,
若是设置为1,你可能会碰到I/O瓶颈,你须要选用更好的SSD设备,或者使用带电池的RAID卡来缓解I/O瓶颈,优化文件系统也是一个选项,ext4和xfs就比ext3的表现要好得多。
生产实践证实,sync_binlog会对事务吞吐率有比较大的影响。
事务日志、数据文件、二进制日志文件是须要同步的。
数据库能够看做一个巨大的同步机,各个组件之间存在复杂的通讯和同步等待,若是sync_binlog操做较慢,那么可能对整个系统的吞吐率形成严重的影响。
有一个相关的参数innodb_support_xa咱们须要了解。i
nnodb_support_xa设置为1时,这个变量容许InnoDB支持XA事务,即Distributed(XA)Transactions分布式事务,MySQL部分支持XA事务。
通常互联网公司的业务不须要分布式事务,并且应该尽可能避免,那么,是否是要禁用innodb_support_xa呢?
并非像一些人理解的那样,没有分布式事务,就不要这个特性,MySQL内部会使用XA来协调存储引擎和二进制日志,以确保灾难恢复功能工做正常,因此应该开启它。
MySQL存储引擎各自独立,互不知晓其余引擎的状态,所以,跨引擎的事务能够看做一个分布式的事务,且须要一个第三方来协调它,这个第三方就是MySQL Server。
咱们能够把“二进制日志”看做一个“存储引擎”。MySQL Server须要协调二进制日志的写入和InnoDB事务的写入。
生产环境为了安全和复制,必须开启binlog和innodb_support_xa。
若是将sync_binlog参数设置为1,那么存储引擎和二进制日志须要彻底同步。
若是二进制日志所在的磁盘存在性能问题,那么也会影响到咱们的事务提交。
生产繁忙的系统,有时常常会看到许多commit慢查询,就是由于二进制日志的写入瓶颈致使了InnoDB事务的提交缓慢。 安全
6.innodb_thread_concurrency
InnoDB试着在InnoDB内部保持操做系统线程的数量少于或等于这个参数给出的限制。
官方建议是将其设置为处理器数目加磁盘数之和,对于高并发事务,也许你应该把这个值设置得更大一些。
对于一些资源等待异常的状况,后来的事务会被已经在等待队列中的事务卡住,你能够经过临时增大这个值,让更多的事务并发执行。 性能优化
7.innodb_max_dirty_pages_pct
这是一个范围从0到100的整数。默认是90。
InnoDB中的主线程试着从缓冲池写数据,使得脏页(没有被写的页面)的百分比不超过这个值。
能够运行以下命令进行修改:SET GLOBAL innodb_max_dirty_pages_pct = value;
生产环境建议将其设置为更小的值:50~75。 服务器
8.read_buffer_size
每一个线程连续扫描时为扫描的每一个表分配的缓冲区的大小(字节)。
若是进行屡次连续扫描,可能还须要增长该值,默认值为131072。
只有当查询须要的时候,才分配read_buffer_size指定的所有内存。 网络
9.read_rnd_buffer_size
排序后,按照排序后的顺序读取行时,则经过该缓冲区读取行,以免搜索硬盘。
将该变量设置为较大的值能够改进ORDER BY的性能。
可是,这是为每一个客户端分配的缓冲区,所以你不该该将全局变量设置为较大的值。
相反,只为须要运行大查询的客户端更改会话变量便可。 数据结构
10.sort_buffer_size
每一个排序线程分配的缓冲区的大小。增长该值能够加快ORDER BY或GROUP BY操做。
查询须要排序的时候(如filesort)才分配sort_buffer_size指定的内存,不要设置得过大,不然小的排序也须要大的内存。
在咱们肯定须要进行大的排序操做的时候,咱们能够在会话级别定义大的排序sort_buffer_size。
11.myisam_sort_buffer_size
当运行REPAIR TABLE命令修复表、运行CREATE INDEX命令建立索引或运行ALTER TABLE命令修改表结构时,排序过程当中需分配的缓冲区,能够在会话级别进行设置。
12.query_cache_size
为缓存查询结果分配的内存的数量,默认值是0,即禁用查询缓存。
请注意:即便将query_cache_type设置为0也将分配query_cache_size设置的内存。从新定义大小会清除原来缓存的结果集。
对于写操做很频繁的应用,能够禁用它,以消除失效Query Cache的开销,这样可能得到性能上的提高。禁用的办法是设置 query_cache_size=0。
建议生产环境中将其设置为64MB~256MB,不要太大,对于绝大部分业务,256MB就已经足够了。
若是要启用QueryCache,那么须要同时设置query_cache_type=1。
13.join_buffer_size
用于彻底链接(当不使用索引的时候使用链接操做)的缓冲区的大小。给不能利用索引的链接使用的。
多表链接须要多个join buffer,因此一个查询可能要用到多个join_buffer_size。
14.max_connections 容许的并行客户端链接数目。
15.max_connect_errors
若是中断与主机的链接超过了该数目,则该主机会阻塞后面的链接。
你能够用FLUSH HOSTS语句解锁锁定的主机。默认值过小了,能够设置在5000以上。
16.skip-name-resolve
不要解析客户端链接的主机名,只使用IP。
若是你要使用该项,那么受权表中的全部Host列值必须为IP号或localhost。
生产环境中必须设置这个参数,不然反向解析缓慢时,会致使MySQL链接缓慢,出现严重的性能问题。
18.3 MySQL内存优化
18.3.1 如何避免使用swap
这里咱们仅仅讨论Linux系统下的swap(交换),其余系统,如Solaris,会有一些区别。
简单地说,swap指的是将最近不常使用的内存移动到下一级存储里(硬盘),在须要的时候,再载入到主内存中。
swap空间通常是指咱们磁盘上的预先配置的一个分区,也能够是文件,用于将内存中的数据交换到磁盘上。
物理内存和swap空间之和就是咱们可用的虚拟内存的大小。
当咱们的内存不够了或应用程序消耗了太多的内存,操做系统会把不须要当即使用的数据传输到磁盘,以释放内存空间,
若是之后须要了,再从磁盘上复制回内存,这样一个过程也称为交换(swap out/swap in)。
经过这样一个交换的动做,增长了实际可用的内存,能够提升系统的吞吐能力,可是数据的交换若是太频繁,就会大大增长磁盘的延时时间,可能会致使严重的性能问题。
通常来讲,数据库负载,须要尽可能避免使用到swap。
咱们能够使用free、vmstat、sar等命令查看 swap使用的统计信息。
经过free命令,若是咱们看到了一小部分swap空间被使用,那么这通常是正常的,不须要额外关注,咱们须要关注的是是否有正在进行的swap in/swap out操做。
一些人建议将swap分区设置为物理内存的大小,对于Linux系统来讲,这个建议有必定的意义,为了避免浪费过多的硬盘空间,建议使用以下的策略。
若是MEM<2GB,那么SWAP=MEM×2,不然SWAP=MEM+2GB。
对于内存很是大的系统,如32GB、64GB,咱们能够使用0.5×内存大小。
MySQL避免使用swap的一些方法以下。
(1)设置memlock
可在参数文件中设置memlock,将MySQL InnoDB buffer锁定到内存,永不使用swap,
但这是有风险的,若是内存不够大,MySQL会被操做系统的OOM机制杀掉。
若是由于物理内存故障致使内存总量变少,那么它可能还会致使系统没法顺利启动,由于MySQL会不断申请内存。
(2)使用大内存页
能够设置MySQL使用Linux系统的大内存页(操做系统和MySQL都须要设置),Linux系统的大内存页是不会被交换出去的。
(3)设置vm.swappiness
能够设置vm.swappiness=0,以减小使用swap的可能。
swappiness参数,它能够在运行时进行调优。
这个参数决定了,将应用程序移动到交换空间而不是移动到正在减小的高速缓存和缓冲区中的可能性,
下降 swappiness能够提升交互式应用程序的响应能力,可是会下降系统的整体吞吐量。
(4)禁用NUMA或调整NUMA
在生产环境中你可能会碰到在没有内存压力的状况下,也发生swap in/swap out的状况,致使不定时出现的性能问题。
尤为是在使用了大的buffer pool size的状况下,这通常是由于使用了NUMA技术,须要考虑禁用NUMA或更改程序分配内存的方式,
numactl命令能够实现这个目的,使用方式为:numactl --interleave all command,例如,/usr/bin/numactl --interleave=all mysqld,详情请参考18.3.2节NUMA。
注意:
不要去禁用swap,并非全部内核在swap分区被禁用的状况下都能工做得很好,这可能会致使服务异常,
某个服务在禁用swap的时候可以工做得很好,并不表明全部程序都能很好地工做。
并且内存不够的几率更高了,当使用了过多的内存时,程序更容易被操做系统的OOM机制杀掉。
咱们须要意识到,swap分区为咱们处理问题留了一个缓冲,给咱们争取到了处理问题的时间,
因此咱们不要把swap分区设置得太小,相对于你所得到的收益,“浪费”一些磁盘空间是值得的。
18.3.2 NUMA
从系统架构来讲,目前的主流企业服务器能够分为3类:SMP(Symmetric Multi Processing,对称多处理架构)、
NUMA(Non-Uniform Memory Access,非一致存储访问架构)和MPP(Massive Parallel Processing,海量并行处理架构)。
下面咱们来看下SMP和NUMA架构。
1.SMP
如图18-2所示的是一个SMP系统。
在这样的系统中,全部的CPU共享所有资源,如总线、内存和I/O系统等,多CPU之间没有区别,都可平等地访问内存和外部资源。
由于CPU共享相同的物理内存,每一个CPU访问内存中的任何地址所须要的时间也是相同的,
所以SMP也被称为一致存储器访问结构(Uniform Memory Access,UMA),尤为是在和NUMA架构对比的时候。
对于SMP服务器而言,每个共享的环节均可能是瓶颈所在。
因为全部处理器都共享系统总线,因此当处理器的数目增多时,系统总线的竞争冲突也会加大,系统总线成为了性能瓶颈,
因此其扩展性有限,这种架构已经被逐步淘汰,但在CPU内部还有应用,单个CPU的全部核共享访问该CPU的本地内存。
2.NUMA
如图18-3所示的是NUMA系统。
在这种架构中,每颗CPU有本身独立的本地内存,CPU节点之间经过互联模块进行链接,访问本地内存的开销很小,延时比访问远端内存(系统内其余节点的内存)小得多。
这也是非一致存储访问NUMA的由来。
综上所述能够得知,NUMA对内存访问密集型的业务更有好处,NUMA系统提高了内存访问的局部性,从而提升了性能。
关于CPU信息,咱们能够查看/proc/cpuinfo。
对于NUMA的访问统计,咱们能够使用numastat命令进行检查,也能够查看/sys/devices/system/node/node*/numastat文件。
如图18-4所示,NUMA使用了default策略,这将致使内存分配的不均衡,numastat命令的输出以下。
各项输出的含义以下:
numa_hit:在此节点分配内存并且成功的次数。
numa_miss:因为内存不够,在此节点分配内存失败转而在其余节点分配内存的次数。
numa_foreign:预期在另外一个节点分配内存,但最终在此节点分配的次数。
interleave_hit:交错分布策略分配内存成功的次数。
local_node:一个运行在某个节点的进程,在同一个节点分配内存的次数。
other_node:运行在其余节点的进程,在此节点分配内存的次数。
在Linux上NUMA API支持4种内存分配策略,具体以下:
缺省(default):老是在本地节点分配(分配在当前线程运行的节点上)。
绑定(bind):分配到指定节点上。
交织(interleave):在全部节点或指定的节点上交织分配。
优先(preferred):在指定节点上分配,失败后在其余节点上分配。
绑定和优先的区别是,在指定节点上分配失败时(如无足够内存),绑定策略会报告分配失败,而优先策略会尝试在其余节点上进行分配。
强制使用绑定有可能会致使前期的内存短缺,并引发大量换页。
咱们能够检查程序具体的内存分配信息,假设pid是mysqld的进程ID,经过查看/proc/pid/numa_maps这个文件,咱们能够看到全部mysqld所作的分配操做。
各字段的显示以下:
2aaaaad3e000 default anon=13240527 dirty=13223315
swapcache=3440324 active=13202235 N0=7865429 N1=5375098
各字段及其解析以下:
2aaaaad3e000:内存区域的虚拟地址。实际上能够把这个看成该片内存的惟一ID。
default:这块内存所用的NUMA策略。
anon=number:映射的匿名页面的数量。
dirty=number:因为被修改而被认为是脏页的数量。
swapcache=number:被交换出去,可是因为被交换出去,因此没有被修改的页面的数量。这些页面能够在须要的时候被释放,可是此刻它们仍然在内存中。
active=number:“激活列表”中的页面的数量。
N0=numberand N1=number:节点0和节点1上各自分配的页面的数量。
咱们能够使用numactl命令显示可用的节点。
numactl --hardware
available: 2 nodes (0-1)
node 0 size: 64570 MB
node 0 free: 8556 MB
node 1 size: 64640 MB
node 1 free: 1982 MB
node distances:
node 0 1
0: 10 20
1: 20 10
如上命令告诉咱们,系统有两个CPU节点:node0、node1。每一个节点分配了64GB的内存。
distance衡量了访问内存的成本,系统认为访问本地节点内存的成本是10,访问远端内存的成本是20。
NUMA架构存在的一个问题是:
对于NUMA架构,Linux默认的内存分配方案是优先在请求线程当前所处的CPU的本地内存上尝试分配空间,通常是node0。
若是内存不够,系统就会把node0上已经分配的内存交换出去,以释放部分node0的内存,尽管node1上还有剩余的内存,可是系统不会选择向node1去申请内存。
显然,swap的成本远比访问远端内存的成本高,这将致使不定时地出现性能问题。
解决办法具体以下:
1)关闭NUMA。
若是是单机单实例,则建议关闭NUMA,关闭的方法有以下两种。
硬件层,在BIOS中设置关闭。
OS内核,启动时设置numa=off。
可用相似以下的方式进行修改。
[root@db1000 ~]# cat /proc/cmdline
ro root=LABEL=/ rhgb quiet
vi /etc/grub.conf
kernel /vmlinuz-2.6.18-164.el5 ro root=LABEL=/ rhgb quiet numa=off
确认NUMA是否关闭,检查numactl--show的输出信息。
[root@db1000 home]# /usr/bin/numactl --show
policy: default
preferred node: current
physcpubind: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
cpubind: 0
nodebind: 0
membind: 0
关闭以前这个命令会显示多个节点的信息,输出结果以下所示。
policy: default
preferred node: current
physcpubind: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
cpubind: 0 1
nodebind: 0 1
membind: 0 1
而关闭以后则只会显示一个节点的信息,nodebind项只有一个值0。咱们也能够检查启动信息dmesg | grep-i numa。
2)使用numactl命令将内存分配策略修改成interleave(交叉)或绑定CPU
可经过修改单实例启动脚本mysql.server或多实例启动脚本mysqld_multi,例如,修改msyqld_multi脚本(MySQL 5.1)320行
将$com="$mysqld"更改成$com="/usr/bin/numactl--interleaveall$mysqld";
也能够修改启动脚本和参数,绑定MySQL的各个实例到固定的CPU节点,笔者更推荐使用这种方式。
下面的例子,在节点0的CPU上运行名为program的程序,而且只在节点0和1上分配内存。
numactl --cpubind=0 --membind=0,1 program
下面的例子,在节点1上运行$MYSQLD程序,只在节点内分配内存。
numactl --cpunodebind=1 --localalloc $MYSQLD
3)设置参数memlock。
MySQL进行初始化启动的时候,就已经预先把InnoDB缓冲池的内存锁住了,即设置参数memlock等于1,
设置这个参数,也有必定的风险,若是内存不够,可能会致使系统启动不正常,由于MySQL Server会不断申请内存。
4)使用大内存页。
还有一些其余的辅助手段。
配置vm.zone_reclaim_mode=0使得内存不足时倾向于向其余节点申请内存。
echo-15>/proc/<pid_of_mysqld>/oom_adj,将MySQL进程被OOM_killer强制kill的可能性调低。
18.4 MySQL CPU优化
系统的性能通常取决于系统全部组件中最弱的短板,CPU、内存、I/O、网络均可能会成为瓶颈所在。
现实中,通常是CPU瓶颈或I/O瓶颈,I/O瓶颈也多是因为内存不够所致使的。
CPU的瓶颈通常是大量运算和内存读取所致使的,好比加密操做、索引范围查找、全表扫描等。
生产环境中出现CPU瓶颈每每是由于大量的索引范围查找或链接了太多表。
I/O瓶颈每每是由于内存已经不能保存住数据库的热数据,所以读写操做必须访问实际的物理磁盘,从而致使过多的物理读。
实际生产环境中,更多的会碰到I/O瓶颈,而不是CPU瓶颈,你能够使用top或mpstat判断数据库服务器是否存在CPU瓶颈。
因为MySQL在多CPU主机上的扩展性有限,不能充分利用多CPU的主机,因此生产中可能会在同一个主机上部署多个实例。
有时咱们会绑定MySQL实例到某个CPU节点上。
若是想要优化性能,那么咱们更倾向于选取速度更快的CPU,而不是增长CPU。
从理论上来讲,若是操做比较集中于一些资源对象,瓶颈可能是由于锁和队列等待,那么这个时候应该选取更强劲的CPU。
而若是操做分散于诸多不相干的资源上,那么并发程度能够更高,能够倾向于使用更多的CPU,但可否使用更多的CPU、 并发多线程执行操做,还要受制于存储引擎的设计。
就目前来讲,InnoDB的扩展性仍是不佳。
下面咱们来看看CPU的高级特性。
PC Server上有一种节能模式,通常是处于关闭的状态,这种电源管理技术能够在负载低的时候,调低CPU的时钟速度,下降能耗,
但这种技术并不能和突发的负荷协做得很好,有时会来不及调整时钟以响应忽然的高并发流量。
还有另一种电源管理技术,它经过分析当前CPU的负载状况,智能地彻底关闭一些用不上的核心,而把能源留给正在使用的核心,并使它们的运行频率更高,从而进一步提高性能。
相反,须要多个核心时,应动态开启相应的核心,智能调整频率。
这样,就能够在不影响CPU的TDP(热功耗设计)的状况下,把核心工做频率调得更高。
这种加速技术可能会破坏咱们的性能规划,由于系统的行为并非“线性”的了。
18.5 MySQL I/O优化
18.5.1 概述
咱们的生产环境通常是OLTP应用,I/O瓶颈通常来自于随机读写,随机读的消除和写的缓解主要靠缓存,因此咱们要确保MySQL的缓冲区可以缓存大部分的热点数据。
固然,也没有必要缓存全部的热点数据,能够接受必定的缓存未命中(cache miss)。
注意,传统的一个调优方法是基于命中率进行调优,更靠谱的方案是基于缓存未命中的状况进行调优,虽然有时命中率很高了,
但只要缓存未命中次数达到必定的频率,你就会碰到I/O瓶颈。
数据库引擎比操做系统或RAID更了解数据,可以更高效地访问数据,文件系统和RAID层面的预读要关掉,由于它们帮不上什么忙,应该交给数据库以更智能地判断数据的读取。
内存的随机读写速度比硬盘的随机读写速度快了几个数量级,因此若是有I/O的性能问题,那么添加内存会是最简便的方案。
数据库缓冲是调优的重点,咱们须要确保数据库缓冲可以缓存大部分的热点数据,
理论上来讲,若是数据库缓冲已经不够了,那么文件系统或RAID缓冲也没有什么用,由于它们要小得多,且不了解数据,
缓存应该考虑在更接近用户的地方进行优化,因为应用比数据库更了解数据,
因此对于高并发的业务,客户端/应用程序的本地内存或缓存服务(如 Memcached)会比MySQL更有效率,提供更好的扩展性。
顺序读写不管是在内存仍是在磁盘中,都比随机读写更快。通常是不用考虑特殊的缓存策略。
对于机械硬盘,因为磁盘的工做原理,顺序读写的速度比随机读写速度快得多,咱们须要着重优化随机读写,尽可能减小随机读,以提升吞吐。
对于SSD,虽然顺序读写也很快,相对而言,随机读写并无差太多,并且优化随机读写也不是那么迫切,
可是仍是有必要优化大量随机读写的SQL,由于随着访问量的上升,贡献大量随机读写的SQL,将会很快致使整个系统出现瓶颈。
随机读写每每来自质量不高的SQL,这些SQL每每是由于索引策略不佳或表链接过多,从应用层优化或进行索引优化,会更有效果,也更具可行性。
文件碎片也可能会致使更多的随机I/O,尽管数据库是顺序访问数据的,可是I/O却不是顺序的,
MySQL自身并无提供工具来检查数据文件是否碎片不少,咱们也不建议频繁地进行表的重建和优化,
可是在进行了大批量数据操做以后,好比大量删除数据以后,在不影响服务的前提下,优化一下表(OPTIMIZE TABLE)仍是可取的。
对于OLAP应用,I/O调优和OLTP有些类似,也是要先考虑应用调优和SQL调优,尽可能减小I/O操做,若是必需要执行大量的I/O操做,那么应该尽可能将其转换为顺序读写。
18.5.2 选择合适的I/O大小
通常来讲,MySQL的块大小是操做系统块的整数块,你能够经过命令getconf PAGESIZE来检查操做系统的块大小,更大的I/O大小,意味着更大的吞吐,
尤为是对于传统的机械硬盘,一次更大的I/O,意味着不须要进行屡次I/O,能够减小寻道的时间。
对于数据库,因为每每是一些随机记录的检索,所以并不须要一次性读取大量的记录,因此一次I/O不须要太大。
许多人把默认的数据库的块大小调整为8KB,以得到更高的性能。
18.5.3 日志缓冲如何刷新到磁盘
对于数据库的I/O性能调整,须要在性能和数据的安全性上求得平衡。
若是生产环境有严重的I/O性能问题,那么它每每是由程序的不良设计形成的。
一个应用级别的SQL调整,可能就能解决了问题。而从操做系统的I/O层面可能就会无解。
InnoDB使用了数据缓冲和事务日志,数据缓冲大小、日志大小、日志缓冲,InnoDB如何刷新数据和缓冲,都会对性能产生影响。
InnoDB的脏数据并非立刻写入数据缓冲(数据文件)的,而是会先写日志缓冲(日志文件),将脏数据暂时保留在数据缓冲区中,
这是一种常见的数据库持久化的技术,这些日志记录了数据变动,能够用来作故障恢复。
数据的读写通常是随机读写,而日志的写入,是顺序写入,日志写入的要效率高得多,经过延缓数据的持久化,能够将数据更高效率地写入到磁盘中。
InnoDB在缓冲区满的状况下会将日志缓冲区刷新到磁盘,通常不须要调整日志缓冲区的大小(innodb_log_buffer_size),除非有不少有BLOB字段的记 录,
innodb_log_buffer_size的大小默认是1MB,建议是1~8MB。
咱们经过配置innodb_flush_log_at_trx来控制如何将日志缓冲刷新到磁盘,innodb_flush_log_at_trx的值可设置为0、一、2,默认为1。
设置为1的状况下,每一个事务提交都要写入磁盘,这是最安全的作法,而设置为其余值时,可能会丢失事务。
通常机械磁盘受磁盘旋转和寻道的限制,最多只能达到几百次IO/每秒,因此这个设置会严重下降事务并发,
若是你数据库的安全性要求很高,那么设置innodb_flush_log_at_trx为1,这时你可能要把日志文件放在更好的磁盘设备上,如SSD设备或带电池的磁盘阵列上。
若是将innodb_flush_log_at_trx设置为2,那么每次事务提交时会将日志缓冲写到操做系统缓存中,但不实际刷新到磁盘中,每秒再刷新日志缓冲到磁盘中,
这样作能够减轻I/O负荷,若是不存在极端的状况,理论上宕机最多只会丢失最近1秒的事务。
若是innodb_flush_log_at_trx设置为0,那么每秒都会将日志缓冲写到日志文件中,且将日志文件刷新到磁盘,但在事务提交的时候并不会将日志缓冲写到日志文件中,
通常不建议将其设置为0,在设为0时,若是mysqld进程崩溃,那么停留在日志缓冲区的数据将被丢失,所以你会丢失事务。
当为2时,进程虽然会崩溃,但每次事务提交,都写入了日志,只是暂时没有被刷新到磁盘,因此不会丢失事务,
由于操做系统负责把这些数据写入文件,固然,若是宕机了,那么你的数据仍是会被丢失的。
设置为1将会更安全,但每次事务提交时都会伴随磁盘I/O,受机械硬盘的寻道和旋转延迟限制,可能会成为系统瓶颈,在确承认以知足I/O性能的前提下,可将 其设置为1。
建议在生产环境中将innodb_flush_log_at_trx设置为2。
18.5.4 事务日志
若是日志文件里记录的相关数据并未写入数据文件,那么这个日志文件是不能被覆盖的。
日志文件若是太小,那么可能会过多地检查点操做,增长I/O操做,而若是日志文件过大,则会增长实例崩溃的恢复时间。
通常建议在生产系统中将其大小设置为256~512MB,以平衡恢复时间和性能。
你也能够定量分析实际须要的事务日志大小,
方法是衡量一段时间(如0.5~2个小时)内写入的日志记录(innodb_os_log_written)的大小,你所分配的多个日志的总计大小应能确保保留此段时间的日志。
事务日志通常没有必要和数据文件分离,除非你有许多(20+)盘。若是只有几个盘,却专门使用独立的盘来存放二进制日志、事务日志,则有些浪费。
在有足够多的盘的状况下,磁盘I/O分离才有意义,否则成本就会过高了,且没法充分利用有限的资源。
建议将日志文件和数据文件放在同一个盘/卷的另外一个缘由是日志文件和数据文件放在一块儿,能够作LVM快照。
18.5.5 二进制日志
若是分离二进制日志和数据文件,可能会带来一点性能上的提高,但分离的主要目的不是性能,而是为了日志的安全。
若是没有带电池的RAID卡,那么分离就是有必要的。
若是有带电池的RAID卡,那么通常状况下就没有必要进行分离,即便有许多顺序日志写入,RAID卡也能够合并这些操做,最终只会看到很少的一些顺序I/O。
若是将二进制日志存放在独立的盘上,那么即便咱们的数据文件损坏了,咱们也能够利用备份和日志作时间点恢复。
18.5.6 InnoDB如何打开和刷新数据、日志文件
InnoDB有几种方式和文件系统进行交互,默认是以fdatasync的方式读写文件的,生产环境中推荐设置为O_DIRECT。
如下将简单介绍这两种方式:
fdatasync:默认InnoDB使用fsync()刷新数据和日志。使用默认设置没有什么问题,但也许发挥不了你硬件的最高性能。
O_DIRECT:对于数据文件,MySQL Server也是调用fsync()刷新文件到磁盘的,可是不使用操做系统的缓存和预读机制,以免双重缓冲,
若是你有带电池的RAID卡,则能够配合这个选项一块儿使用。
注意:RAID卡须要开启写缓存,默认策略是Write Back。
18.5.7 InnoDB共享表空间和独立表空间
InnoDB表空间不只仅能够存储表和索引数据,还有UNDO(能够理解为数据前像)、insert buffer、double write buffer等其余内部数据结构。
目前有两种表空间的管理方式,共享表空间和独立表空间。
默认的是共享表空间的管理方式,InnoDB表空间的管理比较简单,并无Oracle那样丰富的特性。
若是使用默认的共享表空间的话,数据和索引就是放在一块儿的,全部数据都存储在innodb_data_file_path参数设置的数据文件里。
咱们能够经过innodb_data_file_path设置多个InnoDB数据文件,通常将最后一个文件设置为可自动扩展的,以减小数据文件的大小,你也能够将数据文件分离到不一样的磁盘中。
因为数据文件不能收缩,因此使用共享表空间存在的一个严重的问题是空间的释放。
若是你增长了数据文件,那么你还须要重启数据库实例,这些都加大了管理开销。
当自动扩展的数据文件被填满之时,每次扩展默认为8MB,咱们能够调整为更大的值,如32MB、64MB,这个选项能够在运行时做为全局系统变量而改变。
由于每次分配小空间,代价都会比较大,因此预分配一个较大的文件是有道理的。
另外一种方式是独立表空间,咱们须要将innodb_file_per_table设置为1。
这个选项能够将每一个InnoDB表和它的索引存储在它本身的文件中,因为每一个表都有本身的表空间,因此又称为独立表空间。
UNDO、各类数据字典等其余数据仍然存储在共享表空间内。
你能够经过操做系统命令比较直观地看到数据大小,也方便删除表释放空间,
因此许多有经验的DBA都设置MySQL实例为独立表空间, 从而能够更方便地释放空间和减小文件系统的I/O争用。
InnoDB也支持在裸设备上存储,经过这种方式,你也许能够获得少量的性能提高,但因为管理难度比较大,所以不多有人使用这种方式管理数据库文件。
18.5.8 UNDO暴涨的可能性
有时咱们的共享表空间会暴涨,实际上是因为UNDO空间发生了暴涨,UNDO空间暴涨的缘由主要有以下两点。
存在长时间未提交的事务,由于未提交的事务须要使用发布查询时刻的UNDO的数据,因此共享表空间内的这部分UNDO数据不能被清除,将会积累得愈来愈多。
也许是负载过高,清理线程还来不及清除UNDO,这种状况下,性能将会急剧降低。
18.5.9 关于double write buffer
InnoDB可以使用double write buffer来确保数据安全,以免块损坏。
double write buffer是表空间的一个特殊的区域,可顺序写入。
当InnoDB从缓冲池刷新数据到磁盘时,它首先会写入double write buffer,而后写入实际的数据文件。
InnoDB检查每一个页块的校验和,以判断是否坏块,
若是写入double write buffer的是坏块,那么显然尚未写入实际数据文件,那么就用实际数据文件的块来恢复double write buffer。
若是写入了double write buffer,可是数据文件写的是坏块,那么就用double write buffer的块来重写数据文件,这也是MySQL灾难恢复的一个基本步骤。
若是操做系统自己支持写入安全,不会致使坏块,那么咱们能够禁用这个特性。
18.5.10 数据库文件分类
能够考虑把二进制日志文件、InnoDB数据文件的物理文件分布到不一样的磁盘中,
这样作主要考虑的是把顺序I/O和随机I/O进行分离。
你也能够把顺序I/O放到机械硬盘上,把随机I/O放到SSD上,若是有带电池的RAID卡且开启了写缓存,那么顺序I/O的操做通常是很快的。
具体如何放置文件,还须要综合考虑性能、成本和维护性等多个因素。
笔者的作法是,若是没有性能问题,就把全部文件都放在一个盘上,这样维护起来将会更方便。
以下是按照顺序I/O和随机I/O对数据库文件作了下分类。
(1)随机I/O
表数据文件(*.ibd):启用了独立表空间(innodb_file_per_table=1)。
UNDO区域(ibdata):UNDO里存储了数据前像,MySQL为了知足MVCC,须要读取存储在UNDO里的前像数据,这将致使随机读,
若是你要运行一个须要很长时间的事务或一个时间很长的查询,那么可能会致使不少随机读,由于长事务或未提交的事务将有更多的可能性读取前像数据。
(2)顺序I/O
事务日志(ib_logfile*)。
二进制日志(binlog.xxxxxxx)。
double write buffer(ibdata)。
insert buffer(ibdata)。
慢查询日志、错误日志、通用日志等。
18.5.11 什么时候运行OPTIMIZE TABLE
有些人会建议定时运行一些OPTIMIZE TABLE之类的命令,以优化性能,这点与Oracle相似,也总会有些人建议你定时运行重建索引的操做。
通常来讲,除非在进行了大量会影响数据分布的操做以后,好比删除了大量的数据、导入数据等,通常状况下是不须要重整表的。
定时地运行OPTIMIZE TABLE命令不现实,还可能会致使生产系统的不可用。
OPTIMIZED TABLE命令会优化InnoDB主键的物理组织,使之有序、紧凑,可是其余索引仍然会和之前同样未被优化。
哪个索引对性能更重要呢?也许历来没有基于主键的查询条件。
其实,数据、索引的分布也是须要一个过程的,随着时间的演变,天然而然会达到一个平衡。
强制优化以后,过一段时间,它又会回到原来的很差不坏的状态。
因此MySQL 5.1的官方文档中才会建议:若是您已经删除了表的一大部分,
或者若是您已经对含有可变长度行的表(含有VARCHAR、BLOB或TEXT列的表)进行了不少更改,则应使用OPTIMIZE TABLE。
18.5.12 MySQL磁盘空间
磁盘空间若是出现瓶颈,每每是由于数据库规划失误,前期没有进行足够的调研,也有小部分缘由是由于业务发展得太快了,数据呈现爆炸式增加。
大部分业务,通常预留1到2年的数据增加空间就已经足够了,若是你预计数据将来会有一个海量的规模,那么提早进行分库分表则是有必要考虑的。
你须要尽量地了解占据数据库整体空间比重较大的一些数据,清楚哪些表是能够被清理或归档的,
许多状况下,咱们并不须要这么多的数据,或者许多数据是不须要保留好久的,是彻底能够清除的,
你越了解数据,就越可以和研发团队一块儿制定合理的数据保留策略。
在系统上线以前,你就须要制订好将数据进行批量清理和归档的方案,能够使用按期任务删除数据,你也能够利用分区表删除旧的历史数据。
当数据库实例的数据变得很大,单台机器已经很难保存全部数据的时候,你能够考虑将实例、数据库分离到其余的机器。
因为处理器和高速缓存存储器速度的提高超过磁盘存储设备速度的提高,许多业务将受磁盘空间所累。
一些业务拥有海量数据,但大部分都是冷数据,你又不能进行简单的归档处理,这个时候数据压缩就派上用场了。
目前的数据库主机,CPU资源每每过剩,数据压缩能够减小数据库的大小,减小I/O和提升吞吐量,而压缩仅仅只会消耗部分CPU成本。
MySQL 5.5开始提供了InnoDB表压缩的功能,在MySQL 5.6中InnoDB表压缩的功能获得了进一步的完善,真正能够用于生产环境了。
对于真正海量高并发的应用,内存为王,你应该在内存中尽量地保证热点数据和索引,更多的索引和数据能够放在一个内存块中,
那么查询的响应也将更快,表是压缩的也意味着你须要更少的存储空间和更小、更少的I/O操做。
对于MySQL 5.五、5.6,你须要配置为独立的表空间才能使用表的压缩功能,对于MySQL 5.7,你也能够不使用独立表空间。
因为固态硬盘通常比传统机械硬盘要小,且成本更高,因此压缩对固态硬盘尤为有意义。
不一样的内容压缩率将会不同,若是你须要将表修改成压缩表,那么你须要在更改以前进行测试验证,以确认压缩率和转换表的时间,
通常来讲,设置 KEY_BLOCK_SIZE为8KB能够适用于大部分状况,8KB意味着将每一个页压缩为8KB,你也能够将标准的16KB页压缩为4KB或2KB,
但可能会致使过多的性能损耗而压缩率并不能获得提高。
小结:本章讲述了调优将会涉及的MySQL参数及在使用MySQL的过程当中,内存、CPU、I/O的优化。笔者不推荐读者对生产环境的参数作大的调整,也不推荐使用各类不经常使用的手段去优化硬件资源的利用率,压榨硬件的性能。保持一个维护性更好的数据库,使用通用的参数,可让工做变得更简单些,笔者认为这才是更重要的。可是,做为DBA,必定要熟悉各类调优的手段,由于你可能会碰到极端的场景。