免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 55038 | 回复: 3
打印 上一主题 下一主题

从1秒到0.01秒 [复制链接]

论坛徽章:
1
IT运维版块每日发帖之星
日期:2016-07-05 06:20:00
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2017-08-31 20:24 来自手机 |只看该作者 |倒序浏览
百度等搜索引擎以每秒10次至1000次的水平的持续抓取,这给我们的服务器造成极大的困扰。每天早上经常出现后台打不开,网站无法访问,等502、504、500错误。

我在几个月的时间内,经过以下6个步骤,目前从平均执行时间2秒降到了0.03秒左右。



1. 优化SQL

最开始,每个页面的执行时间长度1-2秒,首先想到的是优化SQL语句,增加索引,这些都是常规,此时的抓取频率在10次以,造成的后果是服务器过载,频繁出现504错误,经常对SQL语句的优化,基本解决这些问题。

经过这次调整,执行时间降到了0.5秒左右。



2. 启用缓存(Redis)

时间不长,大约也就是几个星期,服务器再次频繁出现过载,网站频繁出现500、504错误,经过查看,爬虫提高了频率,达到每秒20次或者更多,经过分析,发现瓶颈在数据读取上,每个页面要执行上百个SQL语句,这时,减少SQL语句执行的数量成为关键,经常分析,发现页面上有一些内容是重复的,有些是全局性重复,有些是局部性重复,例如目录列表、标签列表,找不到相关仪器时给出的推荐仪器列表等,根据相关的参数,我们启用了REDIS缓存,有效的降低了每一个页面平均执行的SQL语句数量。

经过这次调整,页面平均执行时间降到了0.1秒左右。



3. 使用新的数据库(Postgresql)

同样,好景不长,几个星期后,再次出现问题,爬虫抓取频率经常达到100次/秒以上,这次,SQL已经没有多少优化空间,能缓存的也都缓存了,经过分析,我们发现有些语句在MYSQL上的执行时间过长,例如递归性质的读取数据以及多表关联时的数据读取时间都比较长, 而且MYSQL是锁表性质的读(Myisam),造成在频率读取时,与其他程序竞争系统资源,造成MYSQL死锁,无法接受新的请求。

我发现Postgresql可以解决这个问题,读表时不锁表,且递归读取性能非常好,还有分词搜索功能(为模糊搜索作准备),我将这些页面上可能用到的数据,从Mysql数据表同步到Postgresql数据库,将数据连接改为从Postgresql 数据库读取,经过这个优化,Mysql不再出现锁死的情况,虽然速度没有多少提升,但是至少可以防止整个服务器过载,影响其他页面的访问。

经过这次调整,执行时间降到了0.09秒。



4. 优化服务器配置(NGINX)

然而,我知道,好景一定不会太长,果然,一段时间后,问题再次到来,爬虫的抓取频率达到了200次/秒~1000次/秒,服务器再次出现大规模过载。

到这里,有两个方案,一个是增加服务器,一个是限制抓取,增加服务器成本过高,而且增加一台显示解决不了问题,一次性增加10台(5台Webserver,5台数据库),有可能能解决这个问题,但显然,爬虫还会再次提高频率。

后来,我决定通过限制抓取频率来解决这个问题,使用Nginx的Limit_conn 模块,将爬虫连接频率降到50次/秒以下,每秒超过30次的请求,返回503错误。

经过这次调整,执行时间没有下降,但是一定程度上解决了过载的问题。



5. 增加Socket连接数(LINUX)

虽然,前面提到了“一定程度”上解决了过载的问题,但是显然问题还是不停的出现,具体来看有两种表现:

1) 一种是访问无法访问或者访问时间很长(达30秒以上),或者是504错误,而此时,数据库的负载并不高,WebServer的负载也不高;

2) 另一种情况下,百度得到的503错误随着他自身抓取频率的提高而提高,这显示也不利于搜索引擎优化。

经过分析,我发现WebServer虽然负载不高,但是PHP错误日志里提示无法分配端口给Mysql,我运行了一下netstat -an ,发现TCP连接数达到上万,问了一下度娘,原来一个服务器能同时维持的TCP连接数是有限的,需要加大TCP连接限制,经过一番研究,将该服务器的TCP连接端口范围改为1024~65535,也就是达到64000多个(默认这个范围只有不到2万个),期间也使用了诸如加大文件描述符数据、TCP连接重用、长连接等等方式的调整。

经过这次调整,服务器又安静了一段时间。



6. 再次优化程序

更大的考验来了,这次,60000个端口也不够了,难道我们只能通过增加服务器的数量来解决?显示服务器的负载并不高,只是端口不够用而已。

那么问题来了,为啥端口不够用呢?每秒执行的次数也就1000次,按说1000个端口不就够了吗?为啥现在会出现这么多的端口占用呢?

分析了TCP连接后,发现,有大量的数据库连接处于TIME_WAIT状态,也就是WebSERVER关闭了数据库连接,等待系统回收,然而,这个回收时间需要120秒,显示这样很快就积累到一个非常大的数字了。

如何快速回收端口?没戏,找了半天,Linux只能设置Fin_Wait时间,不能设置Time_wait时间,所以没有用,转回来,是什么原因造成1000次页面执行需要这么多的商品呢?

经过再次检查程序,

原来,我们的程序中有很多的“new db()“ ,特别是在一些循环、函数内容有这个New DB(),也就是每次循环或者每次调用函数都New一个新的数据库连接,看来这就是问题所以了。

这次,解决的办法是,一个页面执行,尽量使用一个全局的数据库连接,在页面开始前生成这个全局连接变量,在函数里,使用 global $db 来调用。

所以,现在很多的函数,开头前,都有这样一句内容:

Function foo(){

Global $dbproxy;

If(empty($dbproxy)){

$dbproxy
= new mydb_proxy(‘read’);

}

……

}

这次,连接降到了3000个左右了,世界安静了好几天了……



期间,通过将标签内容也缓存起来,提升了0.02秒的速度,在进行最后一步前,执行平均速度是0.08秒左右,最后修改了程序,使用全局数据库连接后,平均执行时间降到了0.038秒(并且有大量的页面执行时间在0.01秒左右,也就是说,当系统只运行这类页面时,时间已经降到0.01秒了)。全局数据库连接有以下几个好处:

1) 减少了内存占用(每一个新连接都需要占用一定的内存);

2) 减少了TCP连接时间,每一个新的数据库连接生成时,都需要建立TCP连接,这个累积起来时间也非常可观;

3) 减少了端口占用,第一个TCP连接都需要占用一个新的商品,即使马上关闭,也需要等待2分钟系统回收。



仅仅依靠启用数据库的长连接方式是不能解决问题的,这个已经尝试过,而使用全局数据库连接变量,效果显著,按PHP手册的说明,如果同时也启用了长连接,似乎也会有一些效果。



总结:程序逻辑、编写方式,对于性能影响实在过大!

论坛徽章:
154
2022北京冬奥会纪念版徽章
日期:2015-08-07 17:10:5720周年集字徽章-年
日期:2022-10-26 16:44:2015-16赛季CBA联赛之深圳
日期:2022-11-02 14:02:4515-16赛季CBA联赛之八一
日期:2022-11-28 12:07:4820周年集字徽章-20	
日期:2023-07-19 08:49:4515-16赛季CBA联赛之八一
日期:2023-11-04 19:23:5115-16赛季CBA联赛之广夏
日期:2023-12-13 18:09:34
2 [报告]
发表于 2017-09-07 17:24 |只看该作者
请问你们优化是研发人员后期继续增加优化环节,还是交给专业的优化团队??

论坛徽章:
1
IT运维版块每日发帖之星
日期:2016-07-05 06:20:00
3 [报告]
发表于 2018-04-13 15:08 |只看该作者
回复 2# shang2010

自己干

论坛徽章:
0
4 [报告]
发表于 2018-09-01 07:50 |只看该作者
屏蔽百度虫子的爬取
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP