高可用性
故障转移和故障恢复
在应用中处理故障转移
有时候让应用来处理故障转移会更加简单或者更加灵活。例如,如果应用遇到一个错误,这个错误外部观察者正常情况下是无法察觉的,例如关于数据库损坏的错误日志信息,那么应用可以自己来处理故障转移过程。
虽然把故障转移处理过程整合到应用中看起来比较吸引人,但可能没有想象中那么有效。大多数应用有许多组件,例如cron任务、配置文件,以及用不同语言编写的脚本。将故障转移整合到应用中可能导致应用变得太过笨拙,尤其是当应用增大并变得更加复杂时。但是将监控构建到应用中是一个好主意,当需要时,能够立刻开始故障转移过程。应用应该也能够管理用户体验,例如提供降级功能,并显示给用户合适的信息.
应用层优化
概述
如果在提高MySQL的性能上花费太多时间,容易使事业局限于MySQL本身,而忽略了用户体验。回过头来看,也许可以意识到,或许MySQL已经足够优化,对于用户看到的响应时间而言,其所占的比重已经非常之小,此时应该关注下其他部分了。这是个很不错的观点,尤其是对DBA而言,这是很值得去做的正确的事。但如果不是MySQL,那有时什么导致了问题?在前面的讨论中,可以通过测量可以快速而准确地给出答案。如果能顺着应用的逻辑过程从头到尾来剖析,那么找到问题的源头一般来说并不困难。有时,尽管问题在MySQL上,也很容易在系统的另一部分得到解决。
无论问题出在哪里,都至少可以找到一个靠谱的工具来帮助进行分析,而且通常事免费的。例如,如果有JavaScript或者页面渲染的问题,可以使用包括FireFox浏览器的Firebug插件在内的调优工具,或者使用Yahoo!的YSlow工具。一些工具甚至可以剖析整个堆栈:New Relic是一个很好的例子,它可以剖析Web应用的前端、应用以及后端。
常见问题。
我们在应用中反复看到一些相同的问题,经常是因为人们使用了缺乏设计的线程系统或者简单开发的流行框架。虽然有时候可以通过这些框架更快更简单地构建系统,但是如果不清楚这些框架背后做了什么操作,反而会增加系统的风险。下面是我们经常会碰到的问题清单,通过这些过程可以激发你的思维。
- 1.什么东西在消耗系统中每台主机的CPU、磁盘、网络,以及内存资源?这些值是否合理?如果不合理,对应用程序做基本的检查,看什么占用了资源。配置文件通常是解决问题最简单的方式。例如,如果Apache因为创建1000各需要50MB内存的工作进程而导致内存溢出,就可以配置应用程序少使用一些Apache工作进程。也可以配置每个进程少使用一些内存。
- 2.应用真的需要所有获取到的数据吗?获取1000行数据但只显示10行,而丢弃剩下的990行,这是常见的错误。(如果应用程序缓存了另外的990行备用,这也许是有意的优化)
- 3.应用在处理本应由数据库处理的事情吗?或者反过来?这里有两个例子,从表中获取所有的行在应用中进行统计计数,或者在数据库中执行复杂的字符串操作。数据库擅长统计计数,而应用擅长正则表达式。要善于使用正确的工具来完成任务。
- 4.应用执行了太多的查询?ORM宣称的把程序员从写SQL中解放出来的语句解耦通常是罪魁祸首。数据库服务器为从多个表匹配数据做了很多优化,因此应用程序完全可以删掉多余的嵌套循环,而使用数据库的关联来代替。
- 5.应用执行的查询太少了?好吧,上面之说了执行太多SQL可能成为问题。到那时,有时候让应用来做"手工关联"以及类似的操作也可能是个好主意。因为它们允许更细的粒度控制和更有效的使用缓存,以及更少的锁争用,甚至有时应用代码里模拟的哈希关联会更快(MySQL的嵌套循环的关联方法并不总是高效的)
- 6.应用创建了没必要的MySQL连接吗?如果可以从缓存中获得数据,就不要再连接数据库
- 7.应用对一个MySQL实例创建连接的次数太多了吗?(也许因为应用的不同部分打开了它们自己的连接)通常来说更好的办法是重用相同的连接。
- 8.应用做了太多的"垃圾"查询?一个常见的例子是发送查询前先发送一个ping命令看数据库是否存活,或者每次执行SQL前选择需要的数据库。总是连接到一个特定的数据库并使用完整的表名也许是更好的方法(这也使得从日志或者通过SHOW PROCESSLIST看SQL更容易了,因为执行日志中的SQL语句的时候不用再切换到特定的数据,数据库名已经包含在SQL语句中了。)"预备"(Preparing)连接时另一个常见问题。Java驱动在预备期间会做大量的操作,其中大部分可以禁用。另一个常见的垃圾查询是SET NAMES UFT8。这是一个错误的方法(它不会改变客户端库的字符集,只会影响服务器的设置)。如果应用在大部分情况使用特定的字符集工作,可以修改配置文件把特定字符集设为默认值,而不需要在每次执行时去做修改。
- 9.应用使用了连接池吗?这既可能是好事,也可能是坏事。连接池可以帮助限制总的连接数,有大量SQL执行的时候效果不错(Ajax应用是一个典型的例子)。然而连接池也可能有一些副作用,比如说应用的事务、临时表、连接相关的配置项,以及用户自定义变量之间相互干扰等
- 10.应用是否适用长连接?这可能导致太多连接。通常来说长连接不是个好主意,除非网络环境很慢导致创建连接的开销很大,或者连接只被一或两个很快的SQL使用,或者连接频率很高导致客户端本地端口不够用。如果MySQL的配置正确,也许就不需要长连接了。比如使用skip-name-resolve来避免DNS反向查询,确保thread_cache足够大,并且增加back_log。
- 11.应用是否在不使用的时候还保持连接打开?如果是这样,尤其是连接到很多服务器时,可能会过多地消耗其他进程所需要地连接。例如,假设你连接到10个MySQL服务器。从一个Apache进程中获取10个连接不是问题,但是任意时刻其中只有1个在真正工作。其他9个大部分时间都处于Sleep状态。如果其中一台服务器变慢了,或者有一个很长地网络请求,其他地服务器就可能因为连接数过多而受到影响。解决方案时控制应用怎么使用连接。例如,可以将操作批量地依次发送到每个MySQL实例,并且在下一次执行SQL前关闭每个连接。如果执行的是比较消耗时间的操作,例如调用Web服务接口,甚至可以先关闭MySQL连接,执行耗时的工作,再重新打开MySQL连接继续在数据库上工作。
长连接和连接池的区别可能使人困惑。长连接可能跟连接池有同样的副作用,因为重用的链接在这两种情况下都是有状态的。然而,连接池通常不会导致服务器连接过多,因为它们会在进程间排队和共享连接。另一方面,长连接是在每个进程基础上创建,不会在进程间共享。
连接池也比共享连接的方式对连接策略有更强的控制力。连接池可以配置为自动扩展,但是通常的实践经验是,当遇到连接池完全占满时,应该将连接请求进行排队而不是扩展连接池。这样做可以在应用服务器上进行排队等,而不是将压力传递到MySQL数据库服务器上导致连接数太多而过载。
有很多方法可以使得查询和连接更快,但是一般的规则是,如果能够直接避免进行查询和连接,肯定比努力提升查询和连接的性能能获得更好的优化结果
Web服务器问题
Apache是最流行的Web应用服务器软件。它在许多情况下都运行良好, 但如果使用不当也会消耗大量的资源。最常见的问题是保持它的进程的存活(alive)时间过长,或者使用各种不同的用途下混合使用,而不是分别对不同类型的工作进行优化。Apache通常是通过prefork配置来使用mod_php、mod_perl和mod_python模块的。 prefork模式会为每个请求预分配进程。因为PHP、Perl和Python脚本是可以定制化的,每个进程使用50MB或100MB内存的情况并不少见。当一个请求完成后,会释放大部分内存给操作系统,但并不是全部。Apache会保持进程处于打开状态以备后来的请求重用。这意味着,如果下一个请求是请求静态文件,比如一个CSS文件或者一张图片,就会出现用一个占用内存很多的进程来为一个很小的请求服务的情况。这就是使用Apache作为通用Web服务器很危险的原因。它的确是为通用目的而设计的,但如果能够有针对性地使用其长处,会获得更好的性能。
另一个主要的问题是,如果开启了Keep-Alive设置,进程可能很长时间处于繁忙状态。当然,即使没有开启Keep-Alive,某些进程也可能存活很久,"填鸭式"地将内容传给客户端可能导致获取数据很慢,(填鸭式抓取发送在一个客户端发起HTTP请求,但是没有迅速获取结果时。直到客户端获取整个结果,HTTP连接------以及处理地Apache进程------都将保持活跃)。人们常犯的另外一个错误,就是保持那些Apache默认开启的模块不动。最好能够精简Apache的模块,移除掉那些不需要的。这很简单:只需要检查Apache的配置文件,注释掉不想要的模块,然后重启Apache就行。也可以在php.ini文件中删除不适用的PHP模块。
最差情况是,如果用一个通用目的的Apache配置直接用于Web服务,最后很可能产生很多重量级的Apache进程。这将浪费Web服务器的资源。它们还可以保持大量MySQL连接,浪费MySQL的资源。下面是一些可以降低服务器负载的方法.不要使用Apache来做静态内容服务,或者至少和动态服务使用不同的Apache实例。流行的替代品有Nginx和lighttpd.
- 1.使用缓存代理服务器,比如Squid或者Varnish,放置所有的请求都到达Web服务器。这个层面即使不能缓存所有页面,并且使用像ESI(Edge Side Includes)这样的技术来将部分页面中的小块的动态内容嵌入到静态缓存部分
- 2.对动态和静态资源都设置过期策略。可以使用Squid这样的缓存代理显式地使内容过期。
有时也许还需要修改应用程序,以便得到更长的过期时间。例如,如果你告诉浏览器永久缓存CSS和JavaScript文件,然后对站点的HTML做了一个修改,这个页面渲染将会出问题。这种情况可以为文件的每个版本设定唯一的文件名。例如,你可以定制网站的发布脚本,复制CSS文件到/css/123_forntpage.css,这里的123就是版本管理器中的版本好。对图片文件的文件名也可以这么做------永不重用文件名,这样页面就不会在升级时出问题,浏览器缓存多久的文件都没问题。
- 1.不要让Apache填鸭式地服务客户端,这不仅仅会导致慢,也会导致DDos攻击变得简单。硬件负载均衡器通常可以做缓冲,所以Apache可以快速地完成,让负载均衡器通过缓存响应客户端的请求,也可以在应用服务器前端使用Nginx、Squid或者事件驱动模式下的Apache
- 2.打开gzip压缩。对于现在的CPU而言这样做的代价很小,但是可以节省大部分流量。如果想节省CPU周期,可以使用缓存,或者诸如Nginx这样的轻量级服务器保存压缩过的页面版本
- 3.不要为用于长距离连接的Apache配置启用Keep-Alive选项,因为这会使得重量级的Apache进程存活很长时间。可以用服务器端的代理来处理保持连接的工作,从而防止Apache进程存活很长时间。可以用服务器端的代理来处理保持连接的工作,从而防止Apache被客户端拖垮。配置Apache到代理之间的连接使用Keep-Alive是可以的,因为代理只会使用很少的Apache连接去获取数据,如图所示。
这些策略可以使Apache进程存活时间变得很短,所以会有比实际需求更多的进程。无论如何,有些操作依然可能导致Apache进程存活时间太长,并且占用大量资源。举个例子,一个请求查询延时非常大的外部资源,例如远程的Web服务,就会出现Apache进程存活时间太长的问题。这种问题通常是无解的