2010-05-11 8 views
4

큰 테이프를 작은 작업으로 분할하여 약 30 개의 스레드를 시간으로 사용하는 시스템이 있습니다. 개별 스레드가 완료되면 계산 된 결과가 데이터베이스에 지속됩니다. 내가 원하는 것은 각각의 스레드가 자체 스레드에서 실행되는 동안 이중 버퍼링 및 데이터 지속성 유형을 수행 할 새로운 persisance 클래스에 결과를 전달하도록하는 것입니다.다중 스레드 프로그램에서 DB 삽입 버퍼링

예를 들어, 100 개의 스레드가 버퍼로 데이터를 이동 한 후에 persistance 클래스는 persistance 클래스가 버퍼를 스왑하고 모든 100 개의 항목을 데이터베이스에 유지합니다. 이렇게하면 준비된 문을 사용할 수 있으므로 프로그램과 데이터베이스 간의 I/O가 줄어 듭니다.

이러한 유형의 멀티 스레딩 이중 버퍼링의 패턴이나 좋은 예가 있습니까?

+0

작업자 스레드가 결과를 얼마나 자주 내뱉습니까? – Justin

답변

4

이 패턴은 비동기 데이터베이스 쓰기 또는 쓰기 패턴이라고합니다. 캐시 업데이트가 기본 데이터베이스에 변경 사항을 기록하는 것을 원하지 않기 때문에 분산 캐시 제품 (Teracotta, Coherence, GigaSpaces, ...)이 지원하는 일반적인 패턴입니다.

이 패턴의 복잡성은 손실 된 데이터베이스 업데이트에 대한 허용 범위에 따라 달라집니다. 작업을 완료하고 결과를 데이터베이스에 기록하는 것 사이에 지연이 있기 때문에 버그, 전원 장애, ... (사진을 얻음)로 인해 업데이트를 잃을 수 있습니다.

DB에 기록 된 결과를 대기열로 정리 한 다음 (예를 들어 사용) 100 번 일괄 처리하거나 일정 시간 후에 처리 할 것을 제안합니다. 시간 지연을 사용하는 이유는 100으로 나눌 수없는 결과 세트를 처리하는 것입니다.

복원력/지속성에 대한 요구 사항이없는 경우 동일한 프로세스에서이 모든 작업을 수행 할 수 있습니다. 그러나 손실을 용인 할 수없는 경우 in-vm 대기열을 영구 JMS 대기열로 대체 할 수 있습니다 (느리지 만 안전합니다).

+0

+1 가능한 문제를 신고합니다. –

+0

야간 일괄 처리 프로세스이므로 생성 된 모든 데이터를 데이터베이스에 기록하기 위해 프로세스가 맨 끝까지 기다리는 것이 좋습니다. 거기에 충분한 메모리가 끝날 때까지 기다릴 수 있도록 그래서 그것은 특정 숫자의 스레드가 자신의 데이터를 전달한 후 DB에 지속됩니다 그것을 설정하는 계획입니다. – Winter

1

동기화 오버 헤드를 줄이려면 각 계산 스레드에 대해 로컬 스레드를 사용하여 결과 일괄 처리를 작성하십시오. 몇 가지 결과에 도달하면 배치를 차단 대기열에 대기열에 추가합니다. ArrayBlockingQueue를 사용하여 퍼시스턴스 클래스를 백업하십시오. 아마도 메모리 사용량에 제한이 없기를 바랄 것입니다. 결과의 그룹을 가져 와서 데이터베이스에 저장하는 여러 데이터베이스 작성자 스레드를 가질 수 있습니다.

class WriteBehindPersister { 
ThreadLocal<List<Result>> internalBuffer; 
static ArrayBlockingQueue<List<Result>> persistQueue; 
static { 
    persistQueue = new ArrayBlockingQueue(10); 
    new WriteThread().start(); 
}  

public WriteBehindPersister() { 
    internalBuffer = new ThreadLocal<List<Result>>(); 
} 

public void persist(Result r) { 
    List<Result> localResult = internalBuffer.get(); 
    localResult.add(r); 
    if (localResult.size() > max) { 
    persistQueue.put(new ArrayList(localResult)); 
    localResult.clear(); 
    } 
} 

class WriteThread extends Thread { 
    public void run() { 
    while (true) { 
    List<Result> batch = persistQueue.take(); 
    beginTransaction(); 
    for (Result r : batch) { 
    batchInsert(r); 
    } 
    endTransaction(); 
    } 
    } 
} 

} 

또한, 하나 개 이상의 DB 연결을 사용하는 트레이드 오프에서 동시에 DB에 여러 배치를 유지하기 위해 실행 프로그램 서비스 (대신 하나의 쓰기 스레드)를 사용할 수 있습니다. 드라이버가 지원하는 경우 JDBC 일괄 처리 API를 사용해야합니다.

+0

steven이 지적한 것처럼 계산이 끝날 때 대기열을 플러시하는 방법 (또는 오랜 기간 동안 요청이없는 경우)을 결정해야합니다. 그것은 모두 당신이 있어야하는 '온라인'에 달려 있습니다. – Justin

+0

각 작업 스레드마다 고유 한 WriteBehindPersister가 있거나 WriteBehindPersister가 싱글 톤이 될까요? – Winter

+0

패턴은 각 스레드가 자체 ThreadLocal 내부 버퍼를 가지고 있기 때문에 싱글 톤으로 작동합니다. 스레드 로컬 항목을 사용하지 않으려는 경우 스레드 당 하나의 Persister (자체 버퍼 포함)를 인스턴스화하고 공유 대기열에 삽입 된 참조로 정적 대기열을 바꿀 수 있습니다. – Justin

관련 문제