2012-12-23 2 views
5

하나의 스레드에 대해 하나의 테이블 행을 명시 적으로 선택하는 방법을 찾고 있습니다. 나는 크롤러를 작성했다.이 크롤러는 약 50 개의 병렬 프로세스로 작동한다. 모든 프로세스는 테이블에서 한 행을 가져 와서 처리해야합니다.높은 병렬 연결에서 하나의 테이블 행만 선택하십시오.

CREATE TABLE `crawler_queue` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT, 
`url` text NOT NULL, 
`class_id` tinyint(3) unsigned NOT NULL, 
`server_id` tinyint(3) unsigned NOT NULL, 
`proc_id` mediumint(8) unsigned NOT NULL, 
`prio` tinyint(3) unsigned NOT NULL, 
`inserted` int(10) unsigned NOT NULL, 
PRIMARY KEY (`id`), 
KEY `proc_id` (`proc_id`), 
KEY `app_id` (`app_id`), 
KEY `crawler` (`class_id`,`prio`,`proc_id`) 
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 

지금 내 프로세스는 다음을 수행

  • 시작 DB 트랜잭션
  • SELECT * FROM crawler_queue WHERE class_id=2 AND prio=20 AND proc_id=0 ORDER BY id LIMIT 1 FOR UPDATE
  • 같은 선택을 한 후 트랜잭션을 커밋 UPDATE crawler_queue SET server_id=1,proc_id=1376 WHERE id=23892
  • 으로이 행을 갱신

이것은 다른 프로세스가 아직 처리 된 행을 가져올 수 없도록 도와줍니다.

mysqli::query(): (HY000/1205): Lock wait timeout exceeded; try restarting transaction (in /var/www/db.php l 
ine 81) 

mysqli::query(): (40001/1213): Deadlock found when trying to get lock; try restarting transaction (in /var/www/db.php line 81) 
: 가끔 내 로그 (그래서 5 분마다 또는) 오류/경고의 두 가지 유형을 볼 수 있습니다 때문에이 선택 쇼

id select_type table   type possible_keys key  key_len ref rows Extra 
1 SIMPLE  crawler_queue ref proc_id,crawler proc_id 3  const 617609 Using where 

그러나 프로세스가 너무 높은 병렬 처리를 야기하는 것에 EXPLAIN 수행

내 질문은 : 누구나 올바른 방향으로 이러한 잠금 문제를 최소화 할 수 있습니까?

편집 2012년 12월 29일 (생산 상태로, 병렬 처리가 지금보다 3 ~ 4 배 높은 것, 그래서 더 많은 잠금 문제가있을 것이라고 가정) : 나는 인덱스 crawler를 사용하는 SELECT 수정 힌트 : USE INDEX(crawler). 내 문제는 이제 lockwait 시간 초과입니다 (교착 상태가 사라짐).

편집 2012-12-31 : USE INDEX()EXPLAIN 지금이다 (표는 이제 더 많은 데이터를 포함하고 있기 때문에 어떤 행 높다.가) : 나는 문제가 무엇을 말할 수에서

id select_type table   type possible_keys key  key_len ref    rows  Extra 
1 SIMPLE  crawler_queue ref proc_id,crawler crawler 5  const,const,const 5472426 Using where 

답변

0

더 나은 해결책은 업데이트를 수행하고 전체적으로 선택을 건너 뛰는 것입니다. 그런 다음 last_insert_id()을 사용하여 업데이트 된 항목을 선택할 수 있습니다. 이렇게하면 동시에 업데이트를 수행하는 동안 잠금을 완전히 건너 뛸 수 있습니다. 레코드가 업데이트되면 모든 초기 조건이 더 이상 일치하지 않는 것으로 간주하여 정확히 동일한 쿼리로 다시 선택되지 않으므로 레코드를 처리 할 수 ​​있습니다.

이것은 잠금과 관련된 모든 문제를 해결하는 데 도움이되고 병렬로 원하는만큼 많은 프로세스를 실행할 수 있어야한다고 생각합니다.

추 신 : 그냥 명확히 말하면, update ... limit 1에 대해 한 행만 업데이트하는지 확인하고 있습니다.

편집 : Solution

아래 지적 올바른 하나입니다.

+1

'LAST_INSERT_ID()'는'INSERT' 데이터 나'UPDATE'가 autoincrement 칼럼을 증가시킬 때만 값을 반환합니다 : ** EDIT ** http://stackoverflow.com/questions/ 1388025/how-to-get-id-of-the-last-updated-row-in-mysql 시도 – rabudde

+0

테스트 한 결과 last_insert_id 값을 얻었으나 속임수가 듭니다. 하지만 그렇지 않았습니다.) 나는 그 질문에 설명 된 해결책이 갈 길이라고 믿습니다. 나는 나의 대답도 업데이트 할 것이다. – Xnoise

0

당신을 직면하는 것은 두 개의 스레드가 테이블의 같은 행에 대해 vyying하고 둘 다 가질 수 없다는 것입니다. 그러나 데이타베이스가 "아무도 그걸 가질 수 없으며 또 다른 행을 찾아라"라고 말하는 우아한 방법이 없으므로 오류가 발생합니다. 이를 자원 경합이라고합니다.

이와 같이 매우 비슷한 작업을 수행 할 때 경쟁 기반 문제를 줄이는 가장 쉬운 방법 중 하나는 모든 스레드가 자신이 작업해야하는 행을 알 수있는 방법을 개발하여 경합을 완전히 없애는 것입니다 미리. 그러면 리소스에 대해 경쟁 할 필요없이 잠글 수 있으며 데이터베이스가 경합을 해결할 필요가 없습니다.

가장 적합한 방법은? 보통 사람들은 일종의 thread-id 체계를 선택하고 모듈로 산술을 사용하여 어떤 스레드가 어떤 행을 가져올지를 결정합니다. 만약 당신이 10 개의 스레드라면 0 번 스레드는 0, 10, 20, 30 등의 행을 얻습니다. 스레드 1은 1, 11, 21, 31 등을 얻습니다.

일반적으로 NUM_THREADS이면 각 스레드가 데이터베이스에서 THREAD_ID + i * NUM_THREADS 인 ID를 처리하고 해당 작업을 수행합니다.

우리는 스레드가 멈추거나 죽을 수 있다는 점을 알아 냈습니다. 사용자는 결코 손대지 않는 데이터베이스 행을 만들 수 있습니다.이 문제에 대한 해결책은 여러 가지가 있습니다. 하나는 대부분의/모든 스레드가 완료된 후 "정리"를 실행하는 것입니다. 모든 스레드는 크롤링되지 않은 URL이 없을 때까지 모든 행을 조각으로 잡고 크롤링합니다. 더 정교 해지고 몇 개의 정리 스레드가 계속 실행되거나 각 스레드에서 때때로 정리 작업을 수행하도록 할 수 있습니다.

3

EXPLAIN 보고서에 단일 열 인덱스 proc_id 만 사용 중이며 쿼리에 600,000 개 이상의 행을 검사합니다. 옵티마이 저가 crawler 색인을 선택한 경우 더 나을 것입니다.

InnoDB는 WHERE 절의 전체 조건과 일치하는 행뿐만 아니라 모든 600K 행을 잠글 수 있습니다. InnoDB는 검사 된 모든 행을 잠궈 동시 변경 내용이 잘못된 순서로 binlog에 기록되지 않도록합니다.

해결책은 검사 된 행의 범위를 좁히기 위해 색인을 사용하는 것입니다. 이렇게하면 행을보다 빨리 찾을 수있을뿐 아니라 넓은 범위의 행을 잠그지 않아도됩니다. crawler 색인이 여기에 도움이되지만 그 색인을 사용하지 않는 이유가 즉시 명확하지 않습니다.

최적화 계획에서 색인을 사용하기 전에 InnoDB의 테이블 통계를 crawler 색인에 대해 알 수 있도록 업데이트해야 할 수도 있습니다. ANALYZE TABLE은 저렴한 연산입니다.

SELECT * FROM crawler_queue USE INDEX(crawler) ... 

이 그 인덱스를 사용하는 최적화를 알려줍니다,이 쿼리에 대한 다른 인덱스를 고려하지 않습니다

다른 옵션은 인덱스 힌트를 사용하는 것입니다. 옵티마이 저가 일반적으로 독자적으로 좋은 결정을 내릴 수 있기 때문에 인덱스 힌트를 피하는 것이 더 낫습니다. 코드에서 힌트를 사용하면 옵티마이 저가 미래에 만드는 인덱스를 고려하지 않을 수도 있습니다. .


더 많은 설명을 추가하면 RDBMS를 FIFO로 사용하고 있습니다. 이것은 RDBMS의 효율적인 사용이 아닙니다. 이를 위해 메시지 큐 기술이 있습니다.

은 참조 :

+0

어이 빌, 그게 내가 한 짓이다. (내 질문을 업데이트하지 않아서 미안하지만, +1 할 것이다). 그러나 이상하게도, proc_id 대신'crawler '를 사용하는 경우가 있습니다. 하지만 지금은 색인 '크롤러'의 사용을 강요합니다. 나는 analyze 테이블 명령에 try를 줄 것이다. Thanks – rabudde

+0

EXPLAIN 출력에서'rows' 필드를 확인하십시오. 복합 색인을 사용하면 검사되는 행의 수가 더 적어야합니다. –

+0

아니요, 아니요 (위 참조) – rabudde

관련 문제