我在没有数据库细节的情况下问这个问题,因为感觉答案可能在于通用设计模式,而且我不一定需要特定于系统的解决方案(我的特定系统设置在问题末尾引用)。
我有一个包含 id、url 和处理字段的公司数据库,以指示该公司当前是否正在由我的一个爬虫处理。我并行运行许多爬虫。每个人都需要选择一家公司进行处理,并在开始之前将该公司设置为处理中,这样每个公司在任何给定时间都只被单个爬虫处理。
我应该如何构建我的系统来跟踪正在处理的公司?
这里的挑战是我无法在我的数据库中搜索未处理的公司,然后更新该公司以将其设置为已处理,因为同时另一个爬虫可能已经选择了它。这似乎是并行处理数据时的常见问题,因此我正在寻找理论上的最佳实践。
我曾经为此使用 MySQL,并使用以下代码来保持处理器之间的一致性。然而,我正在重新设计系统,现在 ElasticSearch 将成为我的主要数据库和搜索服务器。下面的 MySQL 解决方案对我来说总是感觉像是一个 hack,而不是这个并行化问题的正确解决方案。
public function select_next()
{
// set a temp variable that allows us to retrieve id of the row that is updated during next query
$sql = 'SET @update_id := 0';
$Result = $this->Mysqli->query( $sql );
if( ! $Result )
die( "\n\n " . $this->Mysqli->error . "\n" . $sql );
// selects next company to be crawled, marks as crawling in the db
$sql = "UPDATE companies
SET
crawling = 1,
id = ( SELECT @update_id := id )
WHERE crawling = 0
ORDER BY last_crawled ASC, id ASC
LIMIT 1";
$Result = $this->Mysqli->query( $sql );
if( ! $Result )
die( "\n\n " . $this->Mysqli->error . "\n" . $sql );
// this query returned at least one result and there are companies to be crawled
if( $this->Mysqli->affected_rows > 0 )
{
// gets the id of the row that was just updated in the previous query
$sql = 'SELECT @update_id AS id';
$Result = $this->Mysqli->query( $sql );
if( ! $Result )
die( "\n\n " . $this->Mysqli->error . "\n" . $sql );
// set company id
$this->id = $Result->fetch_object()->id;
}
}