如何锁定对MySQL表的读/写,以便我可以选择然后插入而无需其他程序读/写数据库?

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

  • 对于被锁定的表的@AlbertHendriks查询不会被错误拒绝(除非,我认为,连接与Web服务或其他东西绑定并超时).锁只是暂停任何其他想要访问该表的连接,直到锁被删除.一旦表被解锁,连接将自动恢复访问表. (10认同)

won*_*nk0 5

好吧,表锁是解决这个问题的一种方法;但这使得并行请求变得不可能。如果表是 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 子句中使用的任何列)才能使其工作,但这通常是有道理的,我认为无论如何您都会拥有它。


T. *_*nes 2

我从 @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)

  • 我也不认为这个可靠。我有一个多线程应用程序,类似地尝试在 cron 计时器上插入唯一的作业。如果它们发射得太近——比如低至三位数微秒——尽管进行了 WHERE 检查,它们仍然会插入。 (2认同)