zoukankan      html  css  js  c++  java
  • LAMP网站性能优化记

      先说说背景:一个LAMP在线测试网站,日均PV1万左右,比赛时一小时就好几万吧。目前数据库大约有30万条记录。服务器配置很高。近期出现性能问题,当访问量增大的时候,数据库服务器的压力非常大,mysql的内存占用率通常能到400%,这时候基本不能提供服务了,连网站页面都打不开。理论上说现在的数据量还不算大,访问量也不是很大,服务器的配置也很高,出现这种状况是不正常的。这个问题究结了很久,昨天终于找到问题的原因了,记录一下,以后遇到类似的问题可以有个参考。先看一张图片,这是glances监控软件的截图,可以看到红色的496.6就是mysql的CPU占用率

      之前遇到这个问题,只能重启mysql服务,但这只是暂时的,重启之后不到1分钟CPU占用率接着就上来了,不是长久之计。

      然后考虑修改数据库和apache的配置文件,加大缓存空间,但效果还是不明显。

      后来又考虑是不是路由器的问题,因为目前是两台服务器通过一个小路由器连起来,组成一个小局域网,一个放apache(压力很小),另一个服务器放数据库(压力很大),交换数据都要先经过路由器。于是开始怀疑路由器的性能问题,干脆直接用一根网线把两个服务器连起来(服务器有两个网卡)。这样两个服务器就能直接通信了。事实证明,这样还是不能解决数据库压力大的问题。

      接下来,还是各种纠结,想过各种方法,比如换nginx,换其他数据库,弄个数据库集群什么的,都比较麻烦,还需要修改php代码,比较费劲,关键是php这东西看着就恶心……  

      有一天,我发现了memcached,memcached是一个缓存系统,这么说吧,假如有一个查询语句,很费时间,如果没有缓存的话,每次刷新页面就要访问数据库查询一次,这样数据库压力就比较大。 如果有缓存就不一样了,如果要查询的数据在缓存中存在的话,就直接从缓存中取出来,这样就不用去数据库查询了,如果访问量大的话可以明显减少数据库的查询次数,当然也减轻了数据库的压力。先贴一段没有缓存的代码:

    $list = $statics->getFirstTenList();

    下面是加上缓存的代码:

    $list = null;
    if (!($list = $cache->get('top_ten_list'))) {
        $list = $statics->getFirstTenList();
        $cache->set('top_ten_list', $list, 7200);
    }

    没有缓存的代码,每次都要调用查询函数从数据库获取数据。下面的有缓存的就不一样了。当需要查询一个数据之前,首先看看缓存里面有没有,就是代码中$cache->get(),如果从缓存取得了,就不用执行那条查询数据库的语句了。如果缓存服务器没有,就要从数据库里面查询,查询以后,用$cache->set()方法就刚才的查询结果保存到缓存服务器,并设置这个缓存存在的时间,这里是7200秒,7200秒之后,这个缓存就失效了,就要从新更新。还有一点比较重要,就是要给每一个缓存取一个名字,不同的缓存名字不能相同,缓存服务器是根据缓存的名字来区分缓存,如果名字相同的话,从缓存取出来的数据就不是我们需要的数据。举个例子来说:有两个比赛,比赛的排名页面做一个缓存,如果两个缓存名字都叫ranklist,首先刷新一下第一个比赛的排名页面,那么第一个比赛的排名数据就存在缓存服务器了。如果这时再刷新一下第二个比赛的排名页面,因为ranklist在缓存中存在,直接就从缓存中取出来了,但是取出来的数据并不是我们需要的,这种问题还是比较小的。如果是不同类型的数据,那么网站就乱了。

      缓存是个好东西,然后打算通过大量增加缓存来减轻服务器的压力。但后来我发现,这样也不行。因为我们的网站是一个在线测评系统,是一个实时动态的系统,一个用户提交一个答案以后,想立即看到评判结果,当答案正确的时候,要立即更新排名信息。如果增加缓存的话,从缓存中取出来的信息就不是最新的,不能显示实时的排名等信息。这就好比你订了一张票,但是系统好久才提示你订票成功,中间的等待时间是最难熬的。还有一个原因也限制了缓存的使用,就是对于大量的页面,比如说服务器上有2000个题,一个题一个页面,如果一个页面一个缓存的话肯定会降低缓存的性能。但是对于一些数据,长时间变动不大,比如说总做题数的排名,就可以考虑增加一个缓存,可以设置半天或者一天的生存时间,也就是半天或者一天更新一次。

      由于大量缓存并不适用与我们这种实时性很高的系统,因此我并没有增加很多缓存。数据库压力还是那么大。还是没找到问题的根源。

      找到一本mysql性能优化的书,然后就开始研究,首先想到的还是加索引,后来用show index from table_name来查看,发现里面已经对常用的查询词加索引了,我甚至开始考虑是不是因为索引太多加重了数据库的压力?经过一些测试(删除某些索引),发现还是不行,看来通过增加索引已经不能很好得解决OJ问题。

      后来发现了一个语句,是 show processlist,这条命令的作用是显示哪些线程正在执行,执行了多长时间,还有一些其他信息,看截图:

    上面的图片是在负载不是很大的时候截的,从上图我们能看到有两条sql查询语句已经执行了7秒了,还有一个正在往临时表拷贝数据的操作。真正比赛的时候比这个问题严重多了!有几个sql语句执行时间超过10秒,总的sql查询语句个数有200多个,当然大部分线程是被阻塞的。 在这里我们就能很明显看出来到底是哪一条sql语句在浪费时间,接下来,我们就要找到底这条sql查询语句是从哪发起的,从php源吗中找这条语句,如果一条一条找就费劲了,这里我们用强大的grep命令来帮助我们查找。

    很明显,这几条语句就在一个文件里面,让我们打开这个文件看看,下面是其中的一个函数,包含上面的查询语句

    这个函数是从哪调用的?再找

     看到了吧,这里有八条类似的语句,这8条语句的作用是什么?其中第一条的作用是从30万的表里找到今天提交的并且AC的题目总数,第二条就是统计今天的提交总数,下面几条分别是统计这周,这月,这年的AC和提交总数。显示提交状态的solution表中目前大约有30万条信息,并且随时都会更新,只要有人提交就会插入新数据并更新一些数据。在数据库理论里面将,统计和插入更新是互相冲突的,这里就需要加锁,当执行查询语句的时候,将这个表锁住,防止其他数据更新而影响查询结果。也就是说,当执行上面的查询语句时,数据库里面的其他语句就被阻塞了,就被迫去sleep。在提交高峰期,数据库里面被阻塞的语句能达到200多条甚至更多,mysql的CPU占用率达到能达到500%,肯定不能对外提供服务了!

      接下来,我们要找找到底是在哪个页面里面有这个统计,哪个页面调用了这个函数,(强大的grep)

    当看到上面的结果的时候,我的心凌乱了~~靠,怎么回事?这个多页面都有这个统计,然后打开页面文件找找,找到了

    (上图是我修改之后的查询结果,大部分都被我用//注释掉了,原来是没有注释的~)

    在一个不起眼的边栏里,我看到了上面的一个统计。我调查了一些用户,你们注意过这个统计信息吗?你们关注过这个统计信息吗?答案和我想的一样,没有!谁都不关注那这个统计还有什么意义?不知道当初设计者怎么想到的这个功能。当初数据量小的时候没有这个问题,随着数据量增加,问题就暴露出来了。就是这8个数字,拖慢了整个网站的速度!

      接下来解决方案也很清晰了,两种方法:1.删除这个统计,2.给这个统计增加缓存。综合各方面的意见,我决定只在总的排名页面保留这个统计,当然是要增加缓存的,6个小时更新一次。其他页面全部去掉!

      解决这个问题之后,再试试网站的访问速度,那叫一个快啊!泪奔啊!纠结了这么久的性能问题终于解决啦!orz


    博主ma6174对本博客文章(除转载的)享有版权,未经许可不得用于商业用途。转载请注明出处http://www.cnblogs.com/ma6174/

    对文章有啥看法或建议,可以评论或发电子邮件到ma6174@163.com


  • 相关阅读:
    Atitit.ati orm的设计and架构总结 适用于java c# php版
    Atitit.ati dwr的原理and设计 attilax 总结 java php 版本
    Atitit.ati dwr的原理and设计 attilax 总结 java php 版本
    Atitit. 软件设计 模式 变量 方法 命名最佳实践 vp820 attilax总结命名表大全
    Atitit. 软件设计 模式 变量 方法 命名最佳实践 vp820 attilax总结命名表大全
    Atitit 插件机制原理与设计微内核 c# java 的实现attilax总结
    Atitit 插件机制原理与设计微内核 c# java 的实现attilax总结
    atitit.基于  Commons CLI 的命令行原理与 开发
    atitit.基于  Commons CLI 的命令行原理与 开发
    atitit.js 与c# java交互html5化的原理与总结.doc
  • 原文地址:https://www.cnblogs.com/ma6174/p/2818666.html
Copyright © 2011-2022 走看看