MySQL性能优化2

MySQL性能优化2

 

 

       要提高数据库的运行效率,必须从数据库系统级优化、数据库设计级优化、程序实现级优化,这三个层次上同时下功夫。

 

 

根据系统情况,选择硬件优化方向

 

典型OLTP应用系统

 

    这类系统最难维护,最难优化,对主机整体性能要求也是最高的。因为不仅访问量很高,数据量也不小。

 

  • 虽然系统总体数据量较大,但是系统活跃数据在数据总量中所占的比例不大,那么我们可以通过扩大内存容量来尽可能多的将活跃数据cache到内存中;
  • 虽然IO访问非常频繁,但是每次访问的数据量较少且很离散,那么我们对磁盘存储的要求是IOPS表现要很好,吞吐量是次要因素;
  • 并发量很高,CPU每秒所要处理的请求自然也就很多,所以CPU处理能力需要比较强劲;
  • 虽然与客户端的每次交互的数据量并不是特别大,但是网络交互非常频繁,所以主机与客户端交互的网络设备对流量能力也要求不能太弱。

典型OLAP应用系统

  • 数据量非常大,所以磁盘存储系统的单位容量需要尽量大一些;
  • 单次访问数据量较大,而且访问数据比较集中,那么对IO系统的性能要求是需要有尽可能大的每秒IO吞吐量,所以应该选用每秒吞吐量尽可能大的磁盘;
  • 虽然IO性能要求也比较高,但是并发请求较少,所以CPU处理能力较难成为性能瓶颈,所以CPU处理能力没有太苛刻的要求;
  • 虽然每次请求的访问量很大,但是执行过程中的数据大都不会返回给客户端,最终返回给客户端的数据量都较小,所以和客户端交互的网络设备要求并不是太高;
  • 由于OLAP系统由于其每次运算过程较长,可以很好的并行化,所以一般的OLAP系统都是由多台主机构成的一个集群,而集群中主机与主机之间的数据交互量一般来说都是非常大的,所以在集群中主机之间的网络设备要求很高。

特殊的应用系统

 

       他们的数据量不是特别大,但是访问请求及其频繁,而且大部分是读请求。可能每秒需要提供上万甚至几万次请求,每次请求都非常简单,可能大部分都只有一条或者几条比较小的记录返回,就比如基于数据库的DNS服务就是这样类型的服务。

 

  • 虽然数据量小,但是访问极其频繁,所以可以通过较大的内存来cache住大部分的数据,这能够保证非常高的命中率,磁盘IO量比较小,所以磁盘也不需要特别高性能的;
  • 并发请求非常频繁,比需要较强的CPU处理能力才能处理;
  • 虽然应用与数据库交互量非常大,但是每次交互数据较少,总体流量虽然也会较大,但是一般来说普通的千兆网卡已经足够了。

 

 

 

运行环境优化

 

    目前还是Linux的天下,MySQL运行环境的调优往往和Linux的内核调优一并完成。

 

1. 调整Linux默认的IO调度算法

 

IO调度器的总体目标是希望让磁头能够总是往一个方向移动,移动到底了再往反方向走,这恰恰就是现实生活中的电梯模型,所以IO调度器也被叫做电梯 (elevator),而相应的算法也就被叫做电梯算法.而Linux中IO调度的电梯算法有好几种,一个叫做as(Anticipatory),一个叫做 cfq(Complete Fairness Queueing),一个叫做deadline,还有一个叫做noop(No Operation).

 

IO对数据库的影响较大,linux默认的IO调度算法为cfq,需要修改为deadline,如果是SSD或者PCIe-SSD设备,需要修改为noop,可以使用下面两种修改方式。

 

1) 在线动态修改,重启失效。

echo “deadline” > /sys/block/sda/queue/scheduler

 

2) 修改/etc/grub.conf,永久生效。

修改/etc/grub.conf配置文件,在kernel那行增加一个配置,例如:

elevator=deadline

主要关注elevator这个参数,设置内核的话需要重启系统才能生效。

 

 

2. 禁用numa特性

 

新一代架构的NUMA不适合跑数据库,NUMA是为了内存利用率的提高,但反而可能导致一CPU的内存尚有剩余,另外一个却不够用了,发生swap的问题,因此一般建议关闭或修改NUMA的调度。

 

1) 修改/etc/grub.conf关闭NUMA,重启后生效。

numa=off

 

2) 修改/etc/init.d/mysql或mysqld_safe脚本,设置启动mysqld进程时的NUMA调度机制,如 

numactl –interleave=all。

 

 

3. 修改swappiness设置

 

swappiness是linux的一个内核参数,用来控制物理内存交换出去的策略.它允许一个百分比的值,最小的为0,最大的为100,改值默认是60.这个设置值到底有什么影响呢?

 

vm.swappiness设置为0表示尽量少使用swap,100表示尽量将inactive的内存页交换到swap里或者释放cache。inactive内存的意思是程序映射着,但是”长时间”不用的内存。我们可以利用vmstat查看系统里面有多少inactive的内存。

# vmstat -a 1

 

这个值推荐设置为1,设置方法如下,在/etc/sysctl.conf文件中增加一行。

vm.swappiness = 1

 

 

4. 扩大文件描述符

 

这个是经常修改的参数,高并发的程序都会修改.

 

1) 动态修改,重启失效,只能使用root,并且当前session有效。

ulimit -n 51200

 

2、修改配置文件,永久生效。

在/etc/security/limits.conf配置文件中增加

* hard nofile 51200

* soft nofile 51200

 

面向session的进程文件描述符的修改稍有不同,在云上的修改也略有差异,可以参见一样的“open too many files”

 

 

5. 优化文件系统挂载参数。

 

对于文件系统,如无特殊要求,最好采用ext4.

 

文件系统挂载参数是在/etc/fstab文件中修改,重启时候生效。

noatime表示不记录访问时间,nodiratime不记录目录的访问时间。

barrier=0,表示关闭barrier功能.

 

barrier的主要目的是为了保证磁盘写数据的安全性,但是会降低性能。如果有BBU之类的电池备份电源保证控制卡不瞬间掉电,那么这个功能就可以放心大胆的关闭。

 

 

 

数据库架构调优

       从底层来到了应用层,最终到架构层,然而脱离业务逻辑谈架构就是耍流氓。数据库架构同样是依赖业务系统的,稳定而又弹性地服务业务系统是关键。架构调优的方向有:

 

  1. 分区分表,不建议,原因:http://blog.csdn.net/qq_19707521/article/details/59058135
  2. 业务分库
  3. 主从同步与读写分离
  4. 数据缓存
  5. 主从热备与HA双活

 

 

数据库存储引擎的选择

 

MyISAM

 

  • 保存在三个文件,表结构(.frm),表的数据(.MYD),索引数据(.MYI)
  • B-Tree索引
  • R-Tree索引:B-Tree索引有一些区别,主要设计用于为存储空间和多维数据的字段做索引,所以目前的MySQL版本来说,也仅支持geometry类型的字段作索引。
  • Full-text索引:就是我们长说的全文索引,他的存储结构也是b-tree。主要是为了解决在我们需要用like查询的低效问题。
  • 表锁

 

Innodb

 

  • 支持事务安装
  • 数据多版本读取
  • 行锁:锁定机制的改进
  • 实现外键

 

NDBCluster

 

  • 分布式:分布式存储引擎,可以由多个NDBCluster存储引擎组成集群分别存放整体数据的一部分
  • 支持事务:和Innodb一样,支持事务
  • 可与mysqld不在一台主机:可以和mysqld分开存在于独立的主机上,然后通过网络和mysqld通信交互
  • 内存需求量巨大:新版本索引以及被索引的数据必须存放在内存中,老版本所有数据和索引必须存在与内存中
  • 行锁

 

Merge

 

  • 对结构相同的多个MyISAM表,通过一些特殊的包装对外提供一个单一的访问入口,以达到减小应用的复杂度的目的。
  • 要创建MERGE表,不仅仅基表的结构要完全一致,包括字段的顺序,基表的索引也必须完全一致。

 

Memory

  • 仅仅存放了一个表结构相关信息的.frm文件在磁盘上面。
  • 数据和索引都是缓存的,一定MySQL重启或主机重启,数据和索引都会没了,只剩下表结构。
  • 页锁
  • 不支持BLOB和TEXT类型的字段。

BDB

       BDB存储引擎全称为BerkeleyDB存储引擎,和Innodb一样,也不是MySQL自己开发实现的一个存储引擎,而是由SleepycatSoftware所提供,当然,也是开源存储引擎,同样支持事务安全。

Federated

       FEDERATED存储引擎所实现的功能,和Oracle的DBLINK基本相似,主要用来提供对远程MySQL服务器上面的数据的访问接口。如果我们使用源码编译来安装MySQL,那么必须手工指定启用FEDERATED存储引擎才行,因为MySQL默认是不起用该存储引擎的。

Archive

  • 通过较小的存储空间来存放过期的很少访问的历史数据
  • 不支持索引
  • 通过一个.frm的结构定义文件,一个.ARZ的数据压缩文件还有一个.ARM的meta信息文件
  • 不支持删除,修改操作,仅支持插入和查询操作
  • 行锁

BlackHole

       BLACKHOLE存储引擎是一个非常有意思的存储引擎,功能恰如其名,就是一个“黑洞”。就像我们unix系统下面的“/dev/null”设备一样,不管我们写入任何信息,都是有去无回。

CSV

       CSV存储引擎实际上操作的就是一个标准的CSV文件,他不支持索引。起主要用途就是大家有些时候可能会需要通过数据库中的数据导出成一份报表文件,而CSV文件是很多软件都支持的一种较为标准的格式,所以我们可以通过先在数据库中建立一张CVS表,然后将生成的报表信息插入到该表,即可得到一份CSV报表文件了。

 

配置参数调优

 

       my.cnf中的配置参数调优取决于业务,负载或硬件,在慢内存和快磁盘、高并发和写密集型负载情况下,都需要特殊的调整。

 

主要参考这个

http://blog.csdn.net/nightelve/article/details/17393631

 

基本配置

 

1. query_cache_size

query cache是一个众所周知的瓶颈,甚至在并发并不多时也如此。 最 好是一开始就停用,设置query_cache_size = 0,并利用其他方法加速查询:优化索引、增加拷贝分散负载或者启用额外的缓存(比如memcache或redis)。如果已经启用了query cache并且还没有发现任何问题,query cache可能有用。如果想停用它,那就得小心了。

 

2. innodb_buffer_pool_size

缓冲池是数据和索引缓存的地方:这个值越大越好,这能保证你在大多数的读取操作时使用的是内存而不是硬盘。典型的值是5-6GB(8GB内存),20-25GB(32GB内存),100-120GB(128GB内存)。

 

3. innodb_log_file_size

redo日志被用于确保写操作快速而可靠并且在崩溃时恢复。从MySQL 5.5之后,崩溃恢复的性能的到了很大提升,可以同时拥有较高的写入性能和崩溃恢复性能。在MySQL 5.6里可以被提高到4GB以上。如果应用程序需要频繁的写入数据,可以一开始就把它这是成4G。

 

4. max_connections

max_connection值被设高了(例如1000或更高)之后一个主要缺陷是当服务器运行1000个或更高的活动事务时会变的没有响应。在应用程序里使用连接池或者在MySQL里使用进程池有助于解决这一问题。

 

5. back_log

要求 mysql 能有的连接数量。当主要mysql线程在一个很短时间内得到非常多的连接请求,这就起作用,然后主线程花些时间检查连接并且启动一个新线程。back_log指明在mysql暂时停止回答新请求之前的短时间内多少个请求可以被存在堆栈中。只有如果期望在一个短时间内有很多连接,需要增加它,换句话说,该值对到来的tcp/ip连接的侦听队列的大小。

 

 

Innodb配置

1. innodb_file_per_table

这项设置告知InnoDB是否需要将所有表的数据和索引存放在共享表空间里(innodb_file_per_table = OFF)或者为每张表的数据单独放在一个.ibd文件(innodb_file_per_table = ON)。每张表一个文件允许你在drop、truncate或者rebuild表时回收磁盘空间。这对于一些高级特性也是有必要的,比如数据压缩。但是它不会带来任何性能收益。MySQL 5.6中,这个属性默认值是ON。

2. innodb_flush_log_at_trx_commit

默认值为1,表示InnoDB完全支持ACID特性。当关注点是数据安全的时候这个值是最合适的,比如在一个主节点上。但是对于磁盘(读写)速度较慢的系统,它会带来很巨大的开销,因为每次将改变flush到redo日志都需要额外的fsyncs。如果值为0速度就更快了,但在系统崩溃时可能丢失一些数据, 所以一遍只适用于备份节点。

3. innodb_flush_method

这项配置决定了数据和日志写入硬盘的方式。一般来说,如果你有硬件RAID控制器,并且其独立缓存采用write-back机制,并有着电池断电保护,那么应该设置配置为O_DIRECT;否则,大多数情况下应将其设为fdatasync(默认值)。sysbench是一个可以帮助你决定这个选项的好工具。

4. innodb_log_buffer_size

这项配置决定了为尚未执行的事务分配的缓存。但是如果事务中包含有二进制大对象或者大文本字段的话,看Innodb_log_waits状态变量,如果它不是0,增加innodb_log_buffer_size。

其他配置

1. log_bin

如果数据库服务器充当主节点的备份节点,那么开启二进制日志是必须的。就算只有一个服务器,如果你想做基于时间点的数据恢复,这也是很有用的。二进制日志一旦创建就将永久保存。如果不想让磁盘空间耗尽,你可以用 PURGE BINARY LOGS 来清除旧文件,或者设置 expire_logs_days 来指定过多少天日志将被自动清除。记录二进制日志不是没有开销的,所以如果你在一个非主节点的复制节点上不需要它的话,那么建议关闭这个选项。

2. interactive_timeout

服务器在关闭它前在一个交互连接上等待行动的秒数。一个交互的客户被定义为对 mysql_real_connect()使用 client_interactive 选项的客户。 默认数值是28800,建议改为7200。

3. table_open_cache

MySQL每打开一个表,都会读入一些数据到table_open_cache缓存中,当MySQL在这个缓存中找不到相应信息时,才会去磁盘上读取。假定系统有200个并发连接,则需将此参数设置为200*N(N为每个连接所需的文件描述符数目);当把table_open_cache设置为很大时,如果系统处理不了那么多文件描述符,那么就会出现客户端失效,连接不上。

4. max_allowed_packet

接受的数据包大小;增加该变量的值十分安全,这是因为仅当需要时才会分配额外内存。例如,仅当你发出长查询或MySQLd必须返回大的结果行时MySQLd才会分配更多内存。该变量之所以取较小默认值是一种预防措施,以捕获客户端和服务器之间的错误信息包,并确保不会因偶然使用大的信息包而导致内存溢出

5. skip_name_resolve

当客户端连接数据库服务器时,且当DNS很慢时,建立连接也会很慢。因此建议在启动服务器时关闭skip_name_resolve选项而不进行DNS查找。

数据库表设计的优化

  • 有一个与业务无关的主键
  • 适度冗余,让SQL尽两减少Join
  • 选择合适长度的字段
  • 索引的选择
  • 是否使用外键保证数据的完整性
  • 不要在数据库中存放:二进制多媒体数据、流水队列数据、超大文本数据

关于数据类型

  1. 尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。
  2. 尽可能的使用 varchar/nvarchar 代替 char/nchar ,因为变长字段存储空间小,对于查询来说,在一个相对较小的字段内搜索效率显然要高些。
  3. 最好不要给数据库留NULL,尽可能的使用 NOT NULL填充数据库.备注、描述、评论之类的可以设置为 NULL,其他的,最好不要使用NULL。
  4. 任何地方都不要使用 select * from t ,用具体的字段列表代替“*”,不要返回用不到的任何字段。

关于索引

根据索引方法分:

  • BTREE索引(分B-Tree、B+Tree)
  • Hash索引(主要Memory存储引擎使用)

根据索引类型分:

  • 普通索引
  • 唯一索引
  • FullText索引(仅有CHAR,VARCHAR和TEXT这三种数据类型的列可以建Full-text索引)

程序上调用优化

  • 尽量减少查询数据库的次数
  • 写SQL时,查询尽量少的字段
  • 少用MySQL函数,用程序代替,一方面是好调试和控制,另一方面方便数据库迁移
  • 记得用PrepareStatement(预编译SQL语句),防止SQL注入
  • 程序加入数据库连接池,初始连接数(200),最大连接数(400),最小空闲连接数(100),最大空闲连接数(200),最大等待时间(3分钟)

SQL 语句调优

    在应用层,通过pt工具和慢查询日志的配合,可以轻松地分辨出全表扫描的语句。

基本原则

  1. 避免全表扫描
  2. 建立索引
  3. 尽量避免向客户端返回大数据量,若数据量过大,应该考虑相应需求是否合理
  4. 尽量避免大事务操作,提高系统并发能力
  5. 使用基于游标的方法或临时表方法之前,应先寻找基于集的解决方案来解决问题,基于集的方法通常更有效。尽量避免使用游标,因为游标的效率较差。

优化思路

  1. 优化更需要优化的Query;
  2. 定位优化对象的性能瓶颈;
  3. 明确的优化目标;
  4. 从Explain入手;
  5. 多使用profile
  6. 永远用小结果集驱动大的结果集;
  7. 尽可能在索引中完成排序;
  8. 只取出自己需要的Columns;
  9. 仅仅使用最有效的过滤条件;
  10. 尽可能避免复杂的Join和子查询;

关于where后的条件

  • 应尽量避免在 where 子句中使用 != 或 <> 操作符,否则将引擎放弃使用索引而进行全表扫描。
  • 应尽量避免在 where 子句中使用 or 来连接条件,可以考虑使用union 代替
  • in 和 not in 也要慎用,对于连续的数值,能用 between 就不要用 in,exists 代替 in
  • 尽量避免在 where 子句中对字段进行表达式操作和函数操作

关于join

  • 尽可能减少Join语句中的NestedLoop的循环总次数;“永远用小结果集驱动大的结果集”。
  • 优先优化NestedLoop的内层循环;
  • 保证Join语句中被驱动表上Join条件字段已经被索引;
  • 当无法保证被驱动表的Join条件字段被索引且内存资源充足的前提下,不要太吝惜JoinBuffer的设置;

关于order by

  • 尽可能利用已有的索引来避免实际的排序计算,可以很大幅度的提升ORDER BY操作的性能。
  • 加大max_length_for_sort_data参数的设置;
  • 去掉不必要的返回字段;
  • 增大sort_buffer_size参数设置;
  • order by date,如果是同一秒,就要注意再加order by id

关于group by

       由于GROUP BY实际上也同样需要进行排序操作,而且与ORDER BY相比,GROUP BY主要只是多了排序之后的分组操作。当然,如果在分组的时候还使用了其他的一些聚合函数,那么还需要一些聚合函数的计算。所以,在GROUP BY的实现过程中,与ORDER BY一样也可以利用到索引。   关于distinct        DISTINCT实际上和GROUP BY的操作非常相似,只不过是在GROUP BY之后的每组中只取出一条记录而已。所以,DISTINCT的实现和GROUP BY的实现也基本差不多,没有太大的区别。同样可以通过松散索引扫描或者是紧凑索引扫描来实现,当然,在无法仅仅使用索引即能完成DISTINCT的时候,MySQL只能通过临时表来完成。但是,和GROUP BY有一点差别的是,DISTINCT并不需要进行排序。也就是说,在仅仅只是DISTINCT操作的Query如果无法仅仅利用索引完成操作的时候,MySQL会利用临时表来做一次数据的“缓存”,但是不会对临时表中的数据进行filesort操作。

关于临时表

  • 避免频繁创建和删除临时表,以减少系统表资源的消耗。对于一次性事件, 最好使用导出表。
  • 在新建临时表时,如果一次性插入数据量很大,那么可以使用 select into 代替 create table,避免造成大量 log ,以提高速度;如果数据量不大,为了缓和系统表的资源,应先create table,然后insert。
  • 如果使用到了临时表,在最后将所有的临时表显式删除时,先 truncate table ,然后 drop table ,这样可以避免系统表的较长时间锁定。

关于索引

  • 先应考虑在 where 、order by、group by、distinct 涉及的列上建立索引。
  • 在使用索引字段作为条件时,如果该索引是复合索引,那么必须使用到该索引中的第一个字段作为条件 时才能保证系统使用该索引, 否则该索引将不会 被使用, 并且应尽可能的让字段顺序与索引顺序相一致。
  • 索引并不是越多越好,索引固然可以提高相应的 select 的效率,但同时也降低了 insert和update 的效率,因为 insert 或 update 时有可能会重建索引,所以视具体情况而定。一个表的索引数最好不要超过7个,若太多则应考虑一些不常使用到的列上建的索引是否有必要.

MySQL游标

       游标简单来说就是查询出来的数据索引,通过对游标的操作(第一个位置、最后一个位置、上一个位置、下一个位置)可以遍历出数据。

 

参考:

http://blog.jobbole.com/107264/

http://www.cnblogs.com/luxiaoxun/p/4694144.html

猜你喜欢

转载自youyu4.iteye.com/blog/2336196