你的位置: 首页 调试,问题分析

php-resque 长时间运行 redis cpu占用过高的问题

2015-03-20 09:53:51

前些天,公司有台队列服务器,用的是php-resque方案,redis进程cpu占用居高不下,后来到一个核心跑满,系统负载报警多了起来。

本来以为是用这个队列服务的业务大增导致的性能问题,好在能水平拓展,加了一台服务器来一起扛。

神奇的是,新加的服务器cpu使用率在3%以内徘徊,老的服务器cpu使用率依然在50%左右,而访问已经平均分流了。

区别就是老的服务器redis服务跑了大半年了,新的是刚装的。

查看老的redis服务,发现数据居然有好几G,不太正常,因为redis只是用来做存队列数据的,队列完成之后数据应该是删除的,应该只有很小的数据,

进入redis-cli 执行keys * 发现不断跳出类似resque:job:2fee271769ffdca0715f3b45d6ec1005:status的key

这是php-resque用来保存队列任务状态的key,查看php-resque源代码

lib/Resque/Job/Status.php

	/**
	 * Update the status indicator for the current job with a new status.
	 *
	 * @param int The status of the job (see constants in Resque_Job_Status)
	 */
	public function update($status)
	{
		if(!$this->isTracking()) {
			return;
		}

		$statusPacket = array(
			'status' => $status,
			'updated' => time(),
		);
		Resque::redis()->set((string)$this, json_encode($statusPacket));

		// Expire the status for completed jobs after 24 hours
		if(in_array($status, self::$completeStatuses)) {
			Resque::redis()->expire((string)$this, 86400);
		}
	}

更新状态的方法中,设置了已经完成的key 1天以后删除

如果key真的是一天过期,那也没有什么问题,只是稍微存多一点,不过这好几g的数据,就有些异常了。


其实redis的过期删除机制是这样的:

第一种,如果有访问这个key,并且key过期,删除,我们队列的数据,程序不会去访问昨天的数据,所以这个机制没法保证我们的队列数据能删除

第二种,抽样过期,每秒十次随机查找20个key,发现过期删除,当找到大于25个key过期之后,继续执行这一过程,详细介绍见 http://redis.io/commands/expire

我们的数据量是非常大,新增非常快的,不知道第二种过期算法,在这样的数据场景下是不是有问题,结果就是运行快半年后,负载异常了。


其实在我们的业务场景中,已经完成的任务是可以直接删除的,所以将php-resque的代码做了修改,将expire直接改成del,这样就没有负载异常的问题了。

	/**
	 * Update the status indicator for the current job with a new status.
	 *
	 * @param int The status of the job (see constants in Resque_Job_Status)
	 */
	public function update($status)
	{
		if(!$this->isTracking()) {
			return;
		}

		$statusPacket = array(
			'status' => $status,
			'updated' => time(),
		);
		Resque::redis()->set((string)$this, json_encode($statusPacket));

		// 直接删除已经完成的任务
		if(in_array($status, self::$completeStatuses)) {
			Resque::redis()->del((string)$this);
		}
	}


©翟四岚 1989-2089