38

Webクローラーの多くのインスタンスを並行して実行しています。

各クローラーはテーブルからドメインを選択し、その URL と開始時刻をログ テーブルに挿入してから、ドメインのクロールを開始します。

他の並列クローラーは、ログ テーブルをチェックして、クロールする独自のドメインを選択する前に、既にクロールされているドメインを確認します。

別のクローラーが選択したばかりでまだログ エントリがないドメインを、他のクローラーが選択しないようにする必要があります。これを行う方法についての私の最善の推測は、1 つのクローラーがドメインを選択し、ログ テーブルに行を挿入している間 (2 つのクエリ)、他のすべての読み取り/書き込みからデータベースをロックすることです。

どうやってこれを行うのですか?残念ながら、これは非常に複雑で、他の多くのものに依存しています。私が始めるのを手伝ってください。


このコードは良い解決策のようです (ただし、以下のエラーを参照してください)。

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()
)

しかし、次のmysqlエラーが発生し続けます:

You can't specify target table 'crawlLog' for update in FROM clause

この問題なしで同じことを達成する方法はありますか? 私はいくつかの異なる方法を試しました。これを含む:

INSERT INTO crawlLog (companyId, timeStartCrawling)
VALUES
(
    (
        SELECT id
        FROM companies
        WHERE id NOT IN (SELECT companyId FROM crawlLog) LIMIT 1
    ),
    now()
)
4

5 に答える 5

55

次のようにMySQLLOCK TABLESコマンドを使用してテーブルをロックできます。

LOCK TABLES tablename WRITE;

# Do other queries here

UNLOCK TABLES;

見る:

http://dev.mysql.com/doc/refman/5.5/en/lock-tables.html

于 2011-07-08T07:34:10.527 に答える
5

テーブル ロックは、これに対処する 1 つの方法です。しかし、これは並列リクエストを不可能にします。テーブルが InnoDB の場合は、トランザクション内でSELECT ... FOR UPDATEを使用して、代わりに行ロックを強制できます。

BEGIN;

SELECT ... FROM your_table WHERE domainname = ... FOR UPDATE

# do whatever you have to do

COMMIT;

これを機能させるには、インデックス (または WHERE 句で使用する任意の列) が必要になることに注意してください。ただしdomainname、これは一般的に理にかなっています。

于 2011-07-08T07:55:11.027 に答える
4

おそらく、テーブルをロックしたくないでしょう。これを行うと、他のクローラーがデータベースに書き込もうとしたときにエラーをトラップすることを心配する必要があります。これは、「非常に複雑で、他の多くのものに依存している」と言ったときに考えていたことです。

代わりに、次のようにクエリのグループを MySQL トランザクション ( http://dev.mysql.com/doc/refman/5.0/en/commit.htmlを参照)でラップする必要があります。

START TRANSACTION;
SELECT @URL:=url FROM tablewiththeurls WHERE uncrawled=1 ORDER BY somecriterion LIMIT 1;
INSERT INTO loggingtable SET url=@URL;
COMMIT;

またはそれに近い何か。

[編集] 今気づいたのですが、必要なことはすべて 1 つのクエリで実行でき、トランザクションについて心配する必要さえありません。このようなもの:

INSERT INTO loggingtable (url) SELECT url FROM tablewithurls u LEFT JOIN loggingtable l ON l.url=t.url WHERE {some criterion used to pick the url to work on} AND l.url IS NULL.
于 2011-07-08T07:55:03.177 に答える
2

@Eljakimの回答からインスピレーションを得て、この新しいスレッドを開始し、すばらしいトリックを見つけました。それは何もロックすることを含まず、非常に簡単です。

INSERT INTO crawlLog (companyId, timeStartCrawling)
SELECT id, now()
FROM companies
WHERE id NOT IN
(
    SELECT companyId
    FROM crawlLog AS crawlLogAlias
)
LIMIT 1
于 2011-07-08T21:18:22.440 に答える
2

ロックやトランザクションは使用しません。

最も簡単な方法は、ログ テーブルにレコードがまだ存在しない場合は INSERT し、そのレコードを確認することです。

そこにtblcrawels (cra_id)はクローラーがありtblurl (url_id)、URL とtbllogging (log_cra_id, log_url_id)ログファイル用のテーブルが入っているとします。

クローラー 1 が URL 2 のクロールを開始する場合は、次のクエリを実行します。

INSERT INTO tbllogging (log_cra_id, log_url_id) 
SELECT 1, url_id FROM tblurl LEFT JOIN tbllogging on url_id=log_url 
WHERE url_id=2 AND log_url_id IS NULL;

次のステップは、このレコードが挿入されているかどうかを確認することです。

SELECT * FROM tbllogging WHERE log_url_id=2 AND log_cra_id=1

結果が得られた場合、クローラー 1 はこの URL をクロールできます。結果が得られない場合は、別のクローラーが同じ行に挿入され、既にクロールされていることを意味します。

于 2011-07-08T08:06:32.540 に答える