memcached罢工引发的血案-博客园评论超时问题处理过程

  起因

  话说2009年12月4日的下午,博客园团队的一位在数据库服务器中修改了一个设置:


sql_server_govermor

  将"Use query governor to prevent long-running queries"设置为500,500代表的是查询成本,不是查询执行时间,如果SQL Server认为查询成本超过500,就会返回超时错误。

  之前我们经常使用这个设置,这样可以避免一些执行时间过长的查询影响整体数据库的性能。但这不是解决问题的办法,只是让局部问题不去影响整体,真正解决问题还是要优化执行时间长的查询。

  而就在修改过这个设置之后某个时候,担任缓冲重任的memcached服务突然偷偷地罢工了,血案就这样开始了...

  发现问题

  有些用户在博客中发评论时出现了超时错误,有人在闪存上进行反映,我们在闪存看到并进行测试,也遇到了超时问题,当时我们以为是同时连接过多造成服务器压力大造成的,从日志中发现有过于频繁访问的IP,屏蔽了该IP之后,可以正常发表评论了,我们以为问题已经解决了,从最新评论看,大家也能发表评论。

  实际上问题依然存在,会在特定的条件下发生,只不过我们的测试时没有遇到这样的条件。如果没有设置"Use query governor to prevent long-running queries",这个问题更难发现,因为这时发评论只会速度变慢,不会出现超时,大家不会去反馈这个速度慢的问题。后来,超时出现越来越频繁,有用户在闪存上进行反馈,我们才知道了这个问题。

  采取措施

  我们怎么也没想到memcached服务已经罢工了,之前memcached服务还从来没罢工过,于是把解决问题的焦点放在评论功能相关的数据库优化。在SQL Server 2005管理工具中执行添加评论的存储过程,执行速度也很慢,查看执行计划,发现成本在聚集索引的插入上,于是我们以为是评论表的聚集索引设置有问题(看来不能仅从执行计划去分析问题)。评论表的聚集索引本来是建立在自增ID上的(博客文章表的聚集索引建立在发表时间上),因为评论表的查询主要就是根据ParentID进行查询(查询结果根据ID进行排序),之外最多的操作就是插入记录。既然成本在聚集索引的插入上,我们就取消了聚集索引,这样可以避免在插入记录时的聚集索引插入。这样操作之后,发评论的速度提升了,但速度还是不理想,还是会出现超时...

  柳暗花明

  后来,无意间发现memcached服务停掉了,立即想到这才是罪魁祸首,不是聚集索引的问题,为什么会这么想呢,看一下添加评论的存储过程就知道了:

 
  
BEGIN TRANSACTION
UPDATE blog_Content
SET FeedBackCount = FeedBackCount + 1 ,LastCommentTime = GETDATE ()
WHERE [ ID ] = @ParentID
INSERT INTO blog_Comment( [ Text ] ,Author) VALUES ( @Text , @Author )
Select @ID = @@Identity
COMMIT TRANSACTION

  之前发表评论的性能就不怎么理想,瓶颈就在对blog_Content表FeedBackCount字段的更新上,因为blog_Content是查询最频繁的表,我们几乎对所有查询都使用了WITH(NOLOCK),但是发表评论的性能还是存在问题。memcached服务罢工,直接的结果就是对blog_Content的查询大量增加,在发表评论时的Update操作成本就很高,从而造成超时。

  启动memcached服务,问题立即解决。

  解决问题

  但是问题并没有真正解决,真正的问题是在添加评论时对blog_Content表FeedBackCount字段的更新。

  针对这个问题,我采用一种解决方法:在插入评论时不进行Update操作,而是将评论所属的文章ID插入另外一张表中,表结构如下:

 
  
CREATE TABLE [ dbo ] . [ blog_Comment_CountLog ] (
[ EntryID ] [ int ] NOT NULL ,
[ FeedbackCount ] [ int ] NOT NULL
)

  插入评论时,进行下面的操作:  

 
  
INSERT INTO blog_Comment_CountLog(EntryID,FeedbackCount) VALUES ( @ParentID , 1 )

  显然,这个插入操作速度很快。

  删除评论时,进行下面的操作:

 
  
INSERT INTO blog_Comment_CountLog(EntryID,FeedbackCount) VALUES ( @ParentID , - 1 )

  那么怎么更新文章的评论数呢?

  然后通过SQL Server的任务计划定时执行存储过程进行更新:

 
  
SET XACT_ABORT ON
BEGIN TRANSACTION
UPDATE [ blog_Content ]
SET [ FeedBackCount ] = [ FeedBackCount ] + CommentCount
FROM [ CNBlogs ] . [ dbo ] . [ blog_Content ] A
INNER JOIN
(
SELECT EntryID, SUM (FeedbackCount) AS CommentCount FROM blog_Comment_CountLog GROUP BY EntryID ) AS B
ON A.ID = B.EntryID
DELETE blog_Comment_CountLog
COMMIT TRANSACTION

  这样还会带来批量更新的好处,在这个任务计划的间隔时间内添加评论,如果属于同一篇文章,只要执行一次更新操作。

  采用这个方法后,发表评论的性能有了明显提升,现在大家发评论可以看到评论提交的执行时间。

  小结

  在处理问题时,不要着急,要全面分析,把可能引起问题的因素尽量多地考虑到,在动手解决问题之前,多花些时间考虑从何处下手。

  在开发中,异步是一个提高性能的有效方法。

  在我们解决各种问题的过程中,从网上获得了很多启发,如果有人遇到过类似问题并分享了自己的处理经验,会节省我们很多时间。

  大家遇到问题时多数会先Google一下吧,如果大家都只想着Google一下,而不去分享,这个生态链就很难维系。别人的分享帮助了你,你的分享帮助了别人,在这种互相帮助中,大家都会进步。

  在刚解决一个问题的时候,是分享的最佳时期,如果此时不分享,也许就永远不会分享,分享的不仅是解决问题的方法,更是解决问题时的那种兴奋。这种兴奋是程序人生的乐趣所在!

转载于:https://www.cnblogs.com/cmt/archive/2009/12/07/1618410.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值