基于PHP处理大量会员积分清零的可靠性之分页批处理

在上一个项目【人人商城积分清零插件】,有一块儿是处理大量会员积分定期清零,要确保所有会员的积分操作都能顺利执行完毕是至关重要的。

考虑到可能涉及大量的数据更新操作,直接在一个HTTP请求中完成所有操作可能会遇到性能瓶颈、超时等问题,我们尝试分享一个不限服务器环境的方案【批处理】来保证任务的完整性和可靠性。

分页批处理

将积分清零的任务分解为多个小批量处理,以避免单次查询影响数据库性能或导致脚本超时。你可以根据会员ID或其他唯一标识进行分页处理。

<?php
// 假设每批处理1000个会员
$batchSize = 1000;

do {
	// 获取一批需要更新的会员
	$members = getMembersForUpdate($batchSize, $lastId);

	if (empty($members)) break;

	foreach ($members as $member) {
		clearMemberPoints($member['id']);

		// 更新最后处理的会员ID
		$lastId = $member['id'];
	}

} while (!empty($members));

//分页读取需要处理的会员数据
function getMembersForUpdate($limit, $lastId = null) {
	// 假设你使用PDO连接数据库
	global $pdo;

	$query = "SELECT id FROM ims_ewei_shop_member WHERE 1=1";
	if ($lastId !== null) {
		$query .= " AND id > :lastId";
	}
	$query .= " ORDER BY id LIMIT :limit";

	$stmt = $pdo->prepare($query);
	if ($lastId !== null) {
		$stmt->bindParam(':lastId', $lastId, PDO::PARAM_INT);
	}
	$stmt->bindValue(':limit', $limit, PDO::PARAM_INT);
	$stmt->execute();

	return $stmt->fetchAll(PDO::FETCH_ASSOC);
}

//执行积分清零操作
function clearMemberPoints($memberId) {
	global $pdo;

	// 执行积分清零操作
	$stmt = $pdo->prepare("UPDATE ims_ewei_shop_member SET points = 0 WHERE id = :id");
	$stmt->bindParam(':id', $memberId, PDO::PARAM_INT);
	$stmt->execute();
}

记录日志和状态跟踪

要在每个处理节点上,详细记录操作日志信息,包括更新成功和失败的操作,这有助于事后审计和问题排查。

同时,还要以批次为参考,创建一个“记录表”来跟踪每个会员的状态,以便于查询。

拓展:消息队列

使用 Redis 队列系统应该是更合理和可靠的,但受限于客户主机环境,本次不拓展来写示例了,只提供个思路,大家来交流吧。

大概思路是将每个会员的积分清零任务,作为一个独立的消息推送到队列中,操作如下:

任务队列:当需要执行积分清零时,遍历所有会员并将他们的ID推送到队列。

任务处理:一个或多个会员从队列中取出任务并逐一处理。