Подтвердить что ты не робот

Как заблокировать чтение/запись в таблицах MySQL, чтобы я мог выбирать, а затем вставлять без чтения других программ в базу данных?

Я запускаю много экземпляров веб-браузера параллельно.

Каждый искатель выбирает домен из таблицы, вставляет этот 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()
)

но я продолжаю получать следующую ошибку 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()
)
4b9b3361

Ответ 1

Я получил вдохновение от ответа @Eljakim и начал этот новый поток, где я понял отличный трюк. Это не связано с блокировкой всего и очень просто.

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

Ответ 2

Вы можете заблокировать таблицы с помощью команды MySQL LOCK TABLES следующим образом:

LOCK TABLES tablename WRITE;

# Do other queries here

UNLOCK TABLES;

См:

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

Ответ 3

Вероятно, вы не хотите блокировать таблицу. Если вы это сделаете, вам придется беспокоиться об ошибках захвата, когда другие сканеры попытаются записать в базу данных - вот что вы думали, когда говорили: "... ужасно сложный и полагается на многие другие вещи".

Вместо этого вы должны, вероятно, перенести группу запросов в транзакцию 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 [email protected];
COMMIT;

Или что-то близкое к этому.

[править] Я только что понял - вы, вероятно, могли бы сделать все, что вам нужно, в одном запросе и даже не беспокоиться о транзакциях. Что-то вроде этого:

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.

Ответ 4

Я бы не использовал блокировку или транзакции.

Самый простой способ - вставить 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-адрес. Если вы не получите никаких результатов, это означает, что другой искатель вставлен в ту же строку и уже сканирует.

Ответ 5

Ну, столовые замки - это один из способов борьбы с этим; но это делает параллельные запросы невозможными. Если таблица InnoDB, вы можете принудительно заблокировать строку, используя SELECT... FOR UPDATE в транзакции.

BEGIN;

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

# do whatever you have to do

COMMIT;

Обратите внимание, что для этого вам понадобится индекс domainname (или любой другой столбец, который вы используете в предложении WHERE), но это имеет смысл вообще, и я предполагаю, что вы все равно это сделаете.