性能调优社区dynatrace在其博客中分享了客户案例,电商网站在假日客流峰值期间数次崩溃,经过SQL优化和调整负载均衡算法解决了相关问题,值得读者借鉴。
按照博文的描述,该电子商务网站在圣诞节期间崩溃了七次,每次宕机的时间都超过5个小时。这种情况让企业损失了大量的收入和名誉。
我们的一个客户就曾经遭遇到这种情况,我们在此分享下他们的经历。宕机的原因有很多,不过我们这里只强调比较突出的一点,即负载均衡器在采用Round-Robin(轮询)算法时比Least-Busy (最休闲)算法更容易导致应用服务器因堆内存耗尽而崩溃。
在分析性能问题之前,先看一看该网站的拓扑结构。该电子商务网站部署在6个Tomcat应用服务器上,前面是3个Apache Web服务器。
面临的问题是,在负载高峰时刻,每个Tomcat实例处理的响应时间开始增长,等待队列中的请求数目增多。一段时间之后,这些Tomcat实例由于 OutOfMemory异常而崩溃,随后其他实例也因为承受不住由此增加的负载而宕机。
即使在采用平均分布负载的算法(Round Robin)下,有些Tomcat应用服务器也会出现响应时间的跳跃。一旦服务器开始拒绝客户连接,我们会发现一些连锁反应。数据库层出现大量的异常,同时应用层之间会抛出异常,Web服务器会返回给浏览器HTTP 500的错误。
比如,在实际的分析过程中,我们发现某一个Tomcat实例在30分钟内对43000个页面返回了HTTP 500错误。
原因分析:来自数据库层(JDBC)的异常是分析该问题的关键入口。仔细查看这些异常会发现连接池已经耗尽了,从而导致应用的各个部分出现问题。由于连接池的原因,每一个请求都需要平均等待3.8秒来从池里获取连接。
不光是连接池大小的设置问题,而且不少低效的数据库语句在执行应用的一些业务逻辑事务时花费了太长时间。这导致应用服务器维护这个连接的时间也较长。如果把负载均衡器设为Round Robin算法,应用服务器会继续获得其他的请求。最终,由于客户请求的随机性,某个应用服务器收到了不少执行低效数据库处理的请求。一旦连接池耗尽,应用服务器就开始抛出异常,并最终导致JVM崩溃。一旦第一个应用服务器出现问题,用不了多久其他服务器也挂掉了。
解决办法:优化应用程序和负载均衡器
首先要分析执行最慢的数据库语句,并做性能优化,比如增加索引等。同时也优化了连接池大小来满足高峰时刻的需求。然后,企业把负载均衡器的算法从Round-Robin改为了Least-Busy,在生产环境中这个配置经常被人遗忘。自从对应用程序和负载均衡器做了修改之后,网站再也没有崩溃。
该企业之前做过负载测试,但是存在两个问题:
由此等到的经验教训:在生产环境的低访问量时段(凌晨2点到6点)执行负载测试,这样可以虽然有小的交易风险,但是可以避免大的经济损失。高峰时间长为10小时,不要测试太短时间。
关于负载均衡的基本算法,主要有以下几种(参考F5产品):