T. *_*nes 28 mysql locking web-crawler mysql-error-1093
我并行运行了很多webcrawler实例.
每个爬网程序从表中选择一个域,将该URL和开始时间插入到日志表中,然后开始对域进行爬网.
在选择要爬网的域之前,其他并行爬网程序会检查日志表以查看已爬网的域.
我需要阻止其他抓取工具选择刚刚被其他抓取工具选中但尚未拥有日志条目的域.我最好的猜测是如何锁定数据库以防止所有其他读/写,同时一个爬虫选择一个域并在日志表中插入一行(两个查询).
一个人怎么做到这一点?我担心这非常复杂,并且依赖于许多其他事情.请帮助我开始.
这段代码似乎是一个很好的解决方案(但请参阅下面的错误):
INSERT INTO crawlLog (companyId, timeStartCrawling)
VALUES
(
(
SELECT companies.id FROM companies
LEFT OUTER JOIN crawlLog
ON companies.id = crawlLog.companyId
WHERE crawlLog.companyId IS NULL
LIMIT 1
),
now()
)
Run Code Online (Sandbox Code Playgroud)
但我不断收到以下mysql错误:
You can't specify target table 'crawlLog' for update in FROM clause
Run Code Online (Sandbox Code Playgroud)
没有这个问题,有没有办法完成同样的事情?我尝试了几种不同的方式.包括这个:
INSERT INTO crawlLog (companyId, timeStartCrawling)
VALUES
(
(
SELECT id
FROM companies
WHERE id NOT IN (SELECT companyId FROM crawlLog) LIMIT 1
),
now()
)
Run Code Online (Sandbox Code Playgroud)
qbe*_*220 48
您可以使用MySQL LOCK TABLES命令锁定表,如下所示:
LOCK TABLES tablename WRITE;
# Do other queries here
UNLOCK TABLES;
Run Code Online (Sandbox Code Playgroud)
看到:
http://dev.mysql.com/doc/refman/5.5/en/lock-tables.html
好吧,表锁是解决这个问题的一种方法;但这使得并行请求变得不可能。如果表是 InnoDB,您可以改为强制行锁,在事务中使用SELECT ... FOR UPDATE。
BEGIN;
SELECT ... FROM your_table WHERE domainname = ... FOR UPDATE
# do whatever you have to do
COMMIT;
Run Code Online (Sandbox Code Playgroud)
请注意,您需要一个索引domainname(或您在 WHERE 子句中使用的任何列)才能使其工作,但这通常是有道理的,我认为无论如何您都会拥有它。
我从 @Eljakim 的回答中得到了一些灵感,并开始了这个新线程,我想出了一个很棒的技巧。它不涉及锁定任何东西并且非常简单。
INSERT INTO crawlLog (companyId, timeStartCrawling)
SELECT id, now()
FROM companies
WHERE id NOT IN
(
SELECT companyId
FROM crawlLog AS crawlLogAlias
)
LIMIT 1
Run Code Online (Sandbox Code Playgroud)