您好,欢迎访问三七文档
当前位置:首页 > 金融/证券 > 股票经典资料 > Mysql应用层面的优化
Mysql应用层面的优化本书若不讲解一章关于连接到MySQL的应用程序优化的内容,那就不能算完整,因为人们常常把一些性能方面的问题都归咎到MySQL身上。书里面我们更多地是讲到MySQL的优化,但是,我们不想让你错过这个更大的图景。一个糟糕的应用设计会使你无论怎么优化MySQL也弥补不了它带来的损失。实际上,有时候对于这类问题的答案是把它们从MySQL上脱离开来,让应用自己或其他工具来做这些事情,这样或许会有较好的性能表现。本章不是构建高性能应用的参考书,我们只是希望通过阅读这一章让你避免那些常见的会伤及MySQL性能的小错误。下文中我们以Web应用为主要讲解对象,因为MySQL主要是用在Web应用上的。1.应用程序性能概述对于更快性能的追求开始时很简单:应用响应请求花费了太长的时间,你总要为此做点什么吧。然而,真正的问题是什么呢?通常的瓶颈是缓慢的查询、锁、CPU饱和、网络延时和文件I/O。如果应用配置错误,或者不恰当地使用资源,以上任何一个因素都会引出一个大问题。1.1找出问题的根源第一个任务是找出肇事者。如果你的应用具备了显示系统运行概况的功能,这做起来就简单了。如果你已经做到了这一步,但还是没法找出引起性能低下的原因,那你就要增加更多的概况信息的调用,去找出那些要么缓慢要么被多次调用的资源。如果你的应用因为CPU高占用率而一直等待,并且应用里有高并发性,那我们提到过的丢失的时间可能就成问题了。鉴于此,有些时候在有限的并发条件下生成应用的概况信息是很有用的。网络延时会占用大块的时间,哪怕是在局域网里。应用层面的概况信息已经包括了网络延时,因此,你应该在概况系统里看到网络往返延时带来的影响了。举例来说,如果一个页面执行了1000个查询,即使每次只有1毫秒的延时,那累加起来也有0.5秒的响应时间,这对高性能应用来说已经是个很大的数目了。如果应用层面概况信息收集得很充分,那就不难找出问题的根源。如果还没有内置概况功能,那就尽可能地加上它。如果你无法添加这个功能,那也可以试试第76页的当你无法加入概况信息代码时里提供的那些建议。这个总比钻研像什么引起应用变慢那样没头绪的理论设想要更快更容易。1.2寻找常见问题同样的问题我在应用里一次又一次地遇到,其原因往往是人们使用了设计糟糕的原有系统,或者采用了简化开发的通用框架。虽然这在某些时候能让你在开发一些功能时变得方便又快速,但它们也给应用增加了风险,因为你不知道它们底下是怎么工作的。这里有一张清单你应该逐个检查一下:在各个机器上的CPU、磁盘、网络和内存资源的使用情况如何?使用率对你而言是否合理?如果不合理,就检查那些影响资源使用的应用的程序基础。配置文件有时就是解决问题的最简单方法,举例来说,如果Apache耗光了内存,那是因为它创建1000个工作者进程,每个工作进程需要50MB内存,这样,你就可通过配置文件配置这个应用能申请的Apache工作者进程数。你也可以配置系统,使之创建进程时少用些内存。应用是否真正使用了它所取得的数据?一个常见的错误是:读取了1000行数据,却只要显示10行就够了,其他990行就丢弃了(然而,如果应用缓存了余下的990条记录供以后使用,那么这可能是特意做的优化)。应用里是否做了本该由数据库来做的处理?反之亦然。有个对应的例子是:读取了所有行的数据,然后在应用里计算它们的总数;以及在数据库里做复杂的字符串处理。数据库擅长于计数,而应用的编程语言擅长于正则表达式。你该使用正确的工具去干正确的活。应用里执行了太多的查询?那些号称能把程序员从SQL代码里解救出来的ORM(Object-RelationalMapping)就因此常被人们责备。数据库服务器是被设计用来匹配多表数据的,因为要移除那些嵌套循环,代之以联接(Join)来做同样的查询。应用里执行的查询太少了?我们只知道执行了太多的查询会成为问题。但是,有时手工的联接和与其相似的查询是个好主意,因为它们可以更加有效地利用缓存,更少的锁(尤其是MyISAM),有时当你在应用的代码里使用一个散列联接时(MySQL的嵌套循环的联接方法往往是低效的),查询的执行速度会更快。应用是不是在毫无必要的时候还连到MySQL上去了?如果你能从缓存里读取数据,就不要去连数据库了。应用连接到同一个MySQL实例的次数是不是太多了?这可能是因为应用的各个部分都各自开启了自己的数据库连接。有个建议在通常情况下都很对:从头到尾都重用同一个数据库连接。应用是不是做了太多的垃圾查询?一个常见的例子是在做查询前才去选择需要的数据库。一个较好的做法是连接到名称明确的数据库,并使用表的全名做查询。(这样做,也便于通过日志或SHOWPROCESSLIST去查询情况,因为你可以直接执行这些查询语句,无需再更改数据库)。准备数据库连接又是另一个常见的问题,特别是Java写的数据库驱动程序,它在准备连接时会做大量的工作,它们中的大多数你都可以关闭。另一种垃圾查询是SETNAMESUTF8,这纯粹是多此一举(它无法改变客户端连接库的字符集,它只对服务器有影响)。如果你的应用已确定在多数任务下使用的是某一个字符集,那你就可以避免这样无谓的字符集设置命令。应用使用连接池了吗?这既是好事情也是坏事情。它限制了连接的数量,这在连接上查询数不多的情况下(Ajax应用就是个典型的例子)是有利的;然而,它的不好的一面是,应用会受限于使用事务、临时表、连接指定的设置和定义用户变量。应用使用了持久性连接吗?这样做的直接结果是会产生太多的数据库连接连到MySQL上。通常情况下,这是个坏主意,除了一种情形:由于慢速的网络导致MySQL的连接成本很高,如果每条连接上只执行一两个快速的查询,或者频繁地连接到MySQL,那样你会很快用完客户端的所有本地端口(更多内容请查看第328页的网络配置)。如果你正确地配置了MySQL,根本不需要持久性连接,可以使用跳过名称解析来防止DNS的查找,并确认该线程的优先级足够高。即使没有使用,应用是不是还打开着连接?如果是,特别是当这些连接连向多台服务器时,它们可能占用了其他进程需要的连接。举例来说,假设你连接到10台MySQL服务器。由一个Apache进程占用10个连接数,这不是个问题,但是它们中只有一条连接是在任何指定时间里做着一些操作,而其他9条连接绝大多数时间都处于睡眠状态。如果有一台服务器响应变得迟缓,或者网络延时变长,那其他几台服务器就遭殃了,因为它们根本没连接可用。对于这个问题的解决办法是控制应用使用数据库连接的方式。举例来说,你可以在各个MySQL实例中依次做批量操作,在向下一个MySQL发起查询前,关闭当前的所有连接。如果你要的是时间消耗很大的操作,比如调用一个WebService,可以先关闭与MySQL的连接,等这个耗时的调用完成后,再打开MySQL的连接,完成剩余的需要在数据库上操作的任务。持久性连接与连接池的不同点比较模糊。持久性连接有与连接池相同的副作用,因为在各种情况下重新使用的连接往往都带有状态。然而,连接池并不总是导致许多连接到服务器的联接,因为它们是队列化的,并在各进程间共享这些连接。在另一方面,持久化连接是基于每个进程来创建的,无法被其他进程所使用。与持久性连接相比,连接池在连接策略上有更多的控制。你可以把一个连接池配置成自动扩充的,但是通常的做法还是当连接池满的时候,新的连接请求都被放在队列里等待。这使得这些请求都在应用服务器上等待,总好过MySQL因为连接太多而超载。有太多的方法使查询和连接更加快速,一般性准则是避免把它们放在一起,胜于试着把它们加速。2.Web服务器的议题Apache是Web应用中使用最广泛的服务器软件。在各种用途下,它都能运行良好,但如果使用得不恰当,它也会占用大量的资源。最常见的一个情况是让它的进程活动了太长的时间,并把它用在各种不同类型的任务下却没有做相应的优化。Apache经常在prefork配置项里使用mod_php、mod_perl、mod_python。预分叉(Prefork)是为每个请求分配一个进程。因为PHP、Perl和Python等脚本语言运行起来很费资源,每个进程占用50MB或100MB内存的情形也不罕见。当一个请求处理完后,它会把绝大多数内存归还给操作系统,但不会是全部。Apache会让这个进程保持在运行状态,以处理将要到来的请求。这就意味着如果这个新来的请求只是为了获得一个静态文件,比如一个CSS文件或一张图片,你都需要重新启用那个又肥又大的进程来处理这个简单请求。这也是为什么把Apache用作多用途Web服务器是件危险的事情。它是多用途的,若你对它进行了有针对性的配置,它才会有更好的性能表现。另外有个主要的问题是如果你打开了Keep-Alive参数项,进程就会长时间地保持忙碌状态。即使你不这么做,有些进程也会这样。如果内容是像填鸭一样传给客户端的,那这个读取数据的过程也会很漫长。人们也经常犯这样的错误:按Apache默认开启的模块来运行。你可以按照Apache使用手册里的说明,把你不需要的模块都关闭掉,做法也很简单:查看Apache的配置文件,把不需要的模块都注释掉,然后重启Apache。你可以从php.ini文件中把不需要的PHP模块都移除。如果你创建了一个多用途Apache才需要的配置当作Web服务器来用,你最后可能会被众多繁重的Apache进程所拖垮,这些进程纯粹浪费你的Web服务器上的资源。而且,它们会占用大量与MySQL的连接,以至于也浪费了MySQL的资源。这里有一些方法能给你的服务器减负:不要把Apache用作静态内容的服务,如果一定要用,那也至少要换个另外的Apache实例来处理这些事情。常见的替代品有lighttpd和nginx。使用一个缓存代理服务器,比如Squid或Varnish,使用户请求无须抵达Web服务器后才能被响应。即使在这个层面上你无法缓存所有的页面,你也能缓存大部分页面,并通过EdgeSideIncludes(ESL,)技术把页面上的小块动态部分放到缓存的静态部分里。对动态内容和静态内容都设置过期策略。你可以使用缓存代理软件,像Squid,去验证内容的明确性。Wikipedia就是用这样的技术在缓存里移除内容已发生变化的文档。有时你可能需要改变一下应用,使它能使用更长的超期时间。举例来说,如果你告诉浏览器要永久缓存CSS和JavaScript文件,然后又对这个网站静态HTML文件做了一些修改,这样这些页面的显示效果可能会变得很糟。对此,你需要使用一个唯一的文件名对每次修订后的页面文件都作一个明确的版本标记。举例来说,你可以自定义你的网站发布脚本,把CSS文件复制到/css/123_frontpage.css目录下,这里的123就是Subversion里的修订号。你也可以用同样的方法来处理图片文件--不要重用原来的文件名,否则,即使你更新了文件内容,页面不会再被更新,不管浏览器要将原来的页面缓存多久。不要让Apache与客户端做填鸭式通信。这不仅仅是慢,而且很容易招致拒绝性服务攻击。典型地,硬件化的负载平衡器会处理好缓存,Apache就能很快地结束响应,然后让负载平衡器从缓存里读出数据去喂客户端。你也可以使用lighttpd、Squid,或者设为事件驱动模式下的Apache作为应用的前端。开启gzip压缩。现在的CPU很廉价,它可以用来节省大量的网络流量。如果你想节省CPU周期,那可以使用轻量级的Web服务器,比如lighttpd,来缓存和提供压缩过的页面。不要将Apache上的长距离连接配置为保活(Keep-Alive)模式,因为它会使Apache上臃肿的进程长时间处于运行状态。代替的方案是,用一个服务端的代理来处理保活的连接,使服务器免受这类客户端的伤害。如果将Apache与代理之间的连接方式设为保活,那是不错的主意,因为代理仅使用几个连接从服务器上读取数据。下图说明了以上两者的差异。以上这些策略应该可以帮助Apache减少进程的使用数,使你的服务器不会因为太多的进程而崩溃。然
本文标题:Mysql应用层面的优化
链接地址:https://www.777doc.com/doc-2883639 .html