programing

MySQL 테이블에 읽기/쓰기를 잠그면 다른 프로그램이 데이터베이스에 읽기/쓰기하지 않고도 선택하고 삽입할 수 있습니다.

powerit 2023. 9. 10. 13:09
반응형

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

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

테이블 잠금은 이 문제를 해결하는 하나의 방법이지만, 이로 인해 병렬 요청이 불가능합니다.테이블이 InnoDB인 경우 트랜잭션 내에서 SELECT ... FOR UPDATE를 사용하여 대신 행 잠금을 강제할 수 있습니다.

BEGIN;

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

# do whatever you have to do

COMMIT;

다음에 대한 인덱스가 필요합니다.domainname(또는 WHERE 조항에서 사용하는 열에 상관없이) 이 방법을 사용할 수 있습니다. 하지만 일반적으로 이 방법은 타당하며 어쨌든 이 방법을 사용할 수 있을 것이라고 생각합니다.

테이블을 잠그고 싶지 않으실 겁니다.그렇게 하면 다른 크롤러들이 데이터베이스에 쓰려 할 때 오류를 포착하는 것에 대해 걱정해야 합니다. "매우 복잡하고 다른 많은 것에 의존합니다."라고 말했을 때의 생각입니다.

대신 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;

아니면 그에 가까운 것.

[edit] 방금 깨달았습니다. 한 번의 쿼리로 필요한 모든 작업을 수행할 수 있으며 트랜잭션에 대해서도 걱정할 필요가 없습니다.이와 같은 것:

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.

@Eljakim의 대답에서 영감을 얻어 이 새로운 실타래를 시작했는데 거기서 대단한 묘수를 발견했습니다.그것은 아무것도 잠그지 않고 매우 간단합니다.

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

잠금이나 거래는 하지 않겠습니다.

가장 쉬운 방법은 기록 테이블이 아직 없는 경우 기록 테이블에 레코드를 삽입한 후 해당 레코드를 확인하는 것입니다.

당신이 가지고 있다고 가정해 보겠습니다.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을 크롤링할 수 있습니다.결과가 나오지 않으면 다른 크롤러가 같은 줄에 삽입되어 이미 크롤링 중임을 의미합니다.

다른 병렬 요청 컨텍스트가 테이블에 액세스할 수 있도록 행 잠금 또는 트랜잭션 기반 쿼리를 사용하는 것이 좋습니다.

언급URL : https://stackoverflow.com/questions/6621303/how-do-i-lock-read-write-to-mysql-tables-so-that-i-can-select-and-then-insert-wi

반응형