云计算之路-阿里云上:2013年4月7日14:15~18:35服务器故障经过

无地自容的道歉之后,向大家汇报一下故障的整个经过。在此再次向大家表示歉意,望大家能谅解!

14:15,有园友在闪存上说博客后台不能发布博文(见下图)。

14:17左右,我们看到了这条闪存。立即进入博客后台测试,发现提交时会出现如下的错误:

"Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding."

这是数据库写入超时的错误,对这个错误信息我们记忆犹新。之前遇到过两次(3月14日4月2日),都是数据库服务器所在的云服务器磁盘IO问题引起的。

登上云服务器,查看Windows性能监视器,发现日志文件所在的磁盘的IO监测数据Avg.Disk Write Queue Length平均值在5以上。性能监视器中这个值的纵坐标最高值是1,可想而知5是多么高的一个值。性能监视器中的走势图几乎是一条直线。见下图(最高值竟然达到了20,真恐怖):

 

(为什么数据库写入超时会表现于日志文件所在的磁盘IO高?因为数据库恢复模式用的是Full,对数据库的数据写入,会先在日志中进行写入操作。)

这次问题与3月14日磁盘IO问题的表现是一样的,所以我们断定这次也是同样的原因,是云服务器所在集群的磁盘IO负载高引起的。

14:19,我们向阿里云提交了工单,特地在标题中加了“紧急”;

14:23,阿里云客服回复说正在核实我们提交的问题;

14:31,阿里云客服回复说已反馈给相关部门检查;

14:42,没有阿里云客服的进一步消息,我们就回复说“如果短时间内解决不了,希望尽快进行集群迁移”(3月14日就是通过集群迁移解决这个问题的,阿里云的技术人员也说过对于集群负载高引起的磁盘IO问题,目前唯一的解决办法就是集群迁移);

14:47,阿里云客服只回复说正在处理;

14:59,还是没消息,我们心急如焚(40分钟过去了,连个说法都没有),在工单中说:“能不能先做集群迁移?”;

然后,接到阿里云客服的电话,说集群中其他云服务器占用的磁盘IO高影响了我们,他们正在处理。。。

过了会,阿里云客服又打电话过来说可能是我们云服务器中的系统或应用导致服务器磁盘写入卡死,让我们重启一下云服务器。(这样的考虑可能是因为这时集群的负载已经降下来,但我们的云服务器磁盘IO还是高。)

15:23左右,我们重启了数据库服务器,但问题依旧。

15:30,阿里云客服终于决定进行集群迁移(从提交工单到决定集群迁移耗时1小10分钟)

15:45,完成集群迁移(上次迁移5分钟不到,这次用了15分钟,这也是阿里云客服所说的进行集群迁移所需的最长时间)

迁移之后,傻眼了,磁盘IO(Avg.Disk Write Queue Length)还是那么高!

为什么这次集群迁移不能像上次那样立即解决问题?我们猜测有两个可能的原因:

1. 迁移后所在的集群磁盘IO负载依然高;

2. 云服务器上出现磁盘IO很高的这个分区放的都是数据库日志文件,可能这个时间段日志写入操作比平时频繁(但暴增几乎没有可能)而且所有日志文件在同一个分区,超过了云服务器磁盘IO的某个极限,造成磁盘IO性能骤降(可能性比较大,依据是云计算之路-入阿里云后:解决images.cnblogs.com响应速度慢的诡异问题)。虽然之前使用物理服务器时,日志文件也是放在同一个分区,从未出现过这个问题,但现在云服务器的磁盘IO能力无法与物理服务器相比,而且磁盘IO会被集群上其他云服务器争抢(详见云计算之路-迁入阿里云后:问题的根源——买到她的“人”,却买不到她的“心”)。

不管是哪一个原因,要解决问题只有一招也是最后一招——减轻日志文件所在的磁盘分区的IO压力。

怎么减压呢?根据“迁入阿里云后的一些心得”一文中的“提高整体磁盘IO性能的小偏方”,另外购买一块磁盘空间,然后将存放博文内容的数据库CNBlogsText(大文本数据写入,对磁盘IO产生的压力很大)的日志文件移至独立的磁盘分区。

在SQL Server中,无法在线完成将数据库日志文件从一个磁盘分区移至另一个磁盘分区。需要先detach数据库,然后将日志文件复制至目标分区,然后再attach这个数据库;在attach时,将日志文件的位置修改为新的路径。

于是,在别无选择的情况下,我们CNBlogsText数据库进行detach操作,并且选择了drop connections,哪知在detach的过程中悲剧发生了,detach失败了,错误是:

Transaction (Process ID 124) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction. 

在detach的过程中竟然发生了死锁,然后“被牺牲”了。让人困惑的是,不是drop connections吗,怎么还会发生死锁?可能drop connections是在detach操作正式开始前,在detach的过程中,还会发生数据库写入操作,这时的写入操作引发了deadlock。为什么偏偏要让detach牺牲?不合情理。

detach失败后,CNBlogsText数据库就处于Single User状态。继续detach,同样的错误,同样的“被牺牲”。

于是,重启了一下SQL Server服务。重启之后,CNBlogsText数据库的状态变为了In Recovery。

这时时间已经到了16:45。

这样的In Recovery状态以前没遇到过,不知如何处理,也不敢轻举妄动。

过了一段时间,刷新了一下SQL Server的Databases列表,CNBlogsText数据库又显示为之前的Single User状态。(原来重启SQL Server之后,会自动先进入In Recovery状态,再进入到Single User状态)

针对Single User状态问题,在工单中咨询了阿里云客服,阿里云客服联系了数据库工程师,得到的建议是进行这样的操作:alter database $db_name SET multi_user

于是,执行了这样的SQL:

exec sp_dboption 'CNBlogsText', N'single', N'false'

出现错误提示:

Database 'CNBlogsText' is already open and can only have one user at a time. 

Single User状态依旧,出现这个错误可能是因为这个数据库不断地有写入操作,抢占着Single User状态下只允许唯一的数据库连接。

(更新:后来从阿里云DBA那学习到的解决这个问题的方法:

select spid  from sys.sysprocesses where dbid=DB_ID('dbname');
--得到当前占用数据库的进程id
kill [spid]
go
alter login [username] disable --禁用新的访问
go
use cnblogstext
go
alter database cnblogstext set multi_user with rollback immediate
go

当时的情形下,我们不够冷静,急着想完成detach操作。觉得屏蔽CNBlogsText数据库的所有写入操作可能需要禁止这台服务器的所有数据库连接,这样会影响整站的正常访问,所以没从这个角度下手。

这时时间已经到了17:08。

我们也准备了最最后一招,假如实在detach不了,假如日志文件也出了问题,我们可以通过数据文件恢复这个数据库。这个场景我们遇到过,也实际成功操作过,详见:SQL Server 2005数据库日志文件损坏的情况下如何恢复数据库。所需的SQL语句如下:

use master 

alter database dbname set emergency 

declare @databasename varchar(255) 

set @databasename='dbname' 

exec sp_dboption @databasename, N'single', N'true' --将目标数据库置为单用户状态 

dbcc checkdb(@databasename,REPAIR_ALLOW_DATA_LOSS) 

dbcc checkdb(@databasename,REPAIR_REBUILD) 

exec sp_dboption @databasename, N'single', N'false'--将目标数据库置为多用户状态 

即使最最后一招也失败了,我们在另外一台云服务器上有备份,在异地也有备份,都有办法恢复,只不过需要的恢复时间更长一些。

想到这些,内心平静了一些,认识到当前最重要的是抛开内疚、紧张、着急,冷静面对。

我们在工单中继续咨询阿里云客服,阿里云客服联系了数据库工程师,让我们加一下这位工程师的阿里旺旺。

我们的电脑上没装阿里旺旺,于是打算自己再试试,如果还是解决不了,再求助阿里云的数据库工程师。

在网上找了一个方法:SET DEADLOCK_PRIORITY NORMAL(来源),没有效果。

时间已经到了17:38。

这时,我们冷静地分析一下:detach时,因为死锁“被牺牲”;从单用户改为多用户时,提示“Database 'CNBlogsText' is already open and can only have one user at a time.”。可能都是因为程序中不断地对这个数据库有写入操作。试试修改一下程序,看看能不能屏蔽所有对这个数据库的写入操作,然后再将数据库恢复为多用户状态。

修改好程序,18:00之后进行了更新。没想到更新之后,将单用户改为多用户的SQL就能执行了:

exec sp_dboption 'CNBlogsText', N'single', N'false'

于是,Single User状态消失,CNBlogsText数据库恢复了正常状态,然后尝试detach,一次成功。

接着将日志文件复制到新购的磁盘分区中,以新的日志路径attach数据库。attach成功之后,CNBlogsText数据库恢复正常,博客后台可以正常发布博文,CNBlogsText数据库日志文件所在分区的磁盘IO(单独的磁盘分区)也正常。问题就这么解决了。

当全部恢复正常,如释重负的时候,时间已经到了18:35。

马后炮 

如果我们提交工单后,阿里云客服决定进行集群迁移不是1小时10分钟,而是10分钟后,故障时间就可以缩短1小时。

如果我们先想办法屏蔽对CNBlogsText数据库的所有写入操作,然后再detach,故障时间又可以缩短至少1小时。

可是这两个如果都没发生,结果代价惨重,让故障在访问高峰期持续了4小多小时,给成千上万的园友造成了麻烦,真是罪孽深重,无地自容。。。

亡羊补牢

舍云服务器,取RDS。

之前舍RDS取云服务,原以为可以用更多的内存弥补云服务器磁盘IO性能低的不足。但万万没想到,云服务器的硬伤不是在磁盘IO性能低,而是在磁盘IO不稳定。

阿里云对我们这次遇到的故障很重视,会帮助我们将数据库服务器从云服务器迁移至RDS。

posted @ 2013-04-08 11:55  博客园团队  阅读(7438)  评论(50编辑  收藏  举报