가비지 컬렉션이 문제가 될 경우 Spring Batch가 프로그램이 9400만 건의 트랜잭션을 중지하는 것을 막을 수 있습니까?
이는 Oracle에 삽입하기 위해 1억1500만 개의 레코드를 처리하는 성능 최적화와 유사한 질문으로 보일 수 있지만, 저는 이것이 다른 문제라고 생각합니다.다른 질문에는 명확성이 부족하기 때문에 명확한 답변이 없습니다.
여러 데이터 소스에서 데이터를 수집하기 위해 다음 변수와 차원으로 구성된 netCDF 파일을 데이터베이스의 세 테이블에 로드합니다.
Variables:
Time: 365 entries in hours since Jan 1, 1900
Latitude: 360 entries, center of 1/2 degree latitude bands
Longitude: 720 entries, center of 1/2 degree longitude bands
Precipitation: 3 Dimensional Array Time, Lat, Lon in dimensions
작성하는 세 개의 테이블은 다음과 같습니다.
UpdateLog:
uid year updateTime
Location:
lid lat lon
(hidden MtM table) UpdateLog_Location:
uid lid
Precipitation:
pid lid uid month day amount
계산을 하면 위치(및 숨김 테이블)는 이 파일 하나에 대해 각각 약 25,000개의 항목을 가지며(2017년만 해당) 강수량 테이블에는 최대 9400만 개의 항목이 있습니다.
지금은 Spring Boot을 사용하여 데이터를 읽고 Location에서 시작하는 테이블을 업데이트하려고 합니다.
배치 사이즈가 1인 경우 데이터베이스는 매우 빠르게 업데이트를 시작했지만 시간이 지남에 따라 업데이트가 중단되었습니다.그때는 프로파일링 같은 게 없어서 왜 그랬는지 잘 모르겠더라고요
500으로 설정했을 때 업데이트마다 속도가 느려지기 때문에 스텝을 명확하게 알 수 있었습니다만, 1의 배치 사이즈보다 훨씬 빨리 개시되었습니다.
250,000으로 설정했더니 처음 25만 엔트리가 약 3분 만에 갱신되었습니다.배치 사이즈가 1, 72시간이면 어림도 없습니다.하지만, 저는 그 프로그램을 프로파일링하기 시작했고, 뭔가를 알아챘습니다.이것은 데이터베이스의 문제가 아니라(35~40초면 모든 엔트리를 커밋할 수 있습니다) Java의 경우 Garbage Collection이 모든 오래된 POJO를 따라가지 못하는 것 같습니다.
저는 이 문제에 대해 2가지 가능한 해결책을 찾고 있습니다.Spring Batch 및 MariaDB로의 직접 CSV Import.가능하다면 통일된 상태를 유지하기 위해 전자를 선호합니다.그러나 Spring Batch에서는 각 아이템에 대해 POJO를 작성하도록 지시하고 있는 것을 알게 되었습니다.
Spring Batch가 이 문제를 해결할 수 있을까요?여러 GC를 동시에 실행할 수 있도록 스레드 매니저와 멀티 스레드 조작을 사용하여 이 문제를 해결할 수 있습니까?아니면 MariaDB로 CSV 파일을 직접 가져오기만 하면 됩니까?
문제는 이 파일 하나를 며칠 안에 끝낼 수 있다고 해도, 우리는 모든 유형의 과거 날씨 데이터베이스를 구축하고 있다는 것입니다.Import할 파일이 더 많아 각각의 파일에 사용할 수 있는 실행 가능한 프레임워크를 설정하고 싶습니다.이 데이터 소스 하나에 116년의 데이터가 더 있습니다!
편집: 문제가 가비지 컬렉션이라는 내 생각을 뒷받침하는 어젯밤 실행 메트릭을 추가합니다.
194880 nanoseconds spent acquiring 1 JDBC connections;
0 nanoseconds spent releasing 0 JDBC connections;
1165541217 nanoseconds spent preparing 518405 JDBC statements;
60891115221 nanoseconds spent executing 518403 JDBC statements;
2167044053 nanoseconds spent executing 2 JDBC batches;
0 nanoseconds spent performing 0 L2C puts;
0 nanoseconds spent performing 0 L2C hits;
0 nanoseconds spent performing 0 L2C misses;
6042527312343 nanoseconds spent executing 259203 flushes (flushing a total of 2301027603 entities and 4602055206 collections);
5673283917906 nanoseconds spent executing 259202 partial-flushes (flushing a total of 2300518401 entities and 2300518401 collections)
보시다시피 실제 작업보다 메모리 플러싱 시간이 2배 더 오래 걸립니다.
테이블 4개?원본 데이터가 그렇지 않더라도 열 4개로 구성된 테이블 1개를 만들 수 있습니다.
dt DATETIME -- y/m/d:h
lat SMALLINT
lng SMALLINT
amount ...
PRIMARY KEY (dt, lat, lng)
그리고 모든 작업은 SQL에서 직접 수행하게 될 것입니다.
LOAD DATA INFILE파일(들)과 일치하는 것으로 변환합니다.- 몇 가지 SQL 문을 실행하여 위의 스키마로 변환하십시오.
- 원하는 보조 인덱스를 위의 표에 추가합니다.
어플리케이션에서는, 을 「」로 했습니다.MEDIUMINT3번입니다.여러 테이블에 걸쳐 94M 행이 훨씬 넘는 컬럼이 필요했습니다.)
의 lid의 3바이트입니다.MEDIUMINT 2바이트 2바이트 2바이트 2바이트 2바이트 2바이트SMALLINTs 。할 수 입니다.복잡성이 더해져 94MB의 절약량보다 더 클 것입니다.
전체 크기: 약 5GB나쁘지 않은데요.
Spring Batch에서 각 아이템에 대해 POJO를 작성하도록 지시받았습니다.
Spring Batch에서는 데이터를 해석하고 POJO를 매핑할 필요가 없습니다.패스 사용 가능ThroughLineMapper 및 원시 형식(필요한 경우 바이너리 형식에서도)으로 항목을 처리합니다.
사용 예에서는 파티션을 사용하는 것을 권장합니다.
제가 몇 가지 질문에 대한 답을 찾았는데, 저를 도와주신 분들께 감사의 말씀을 드리고 싶습니다.
이 문제는 Hibernate가 POJO당 1,000개의 가비지 수집 작업을 생성하게 되어 배치 처리에 그다지 좋은 시스템이 아니기 때문에 발생합니다.대규모 배치에 대한 좋은 치료법은 모두 휴지 상태를 사용하지 않도록 합니다.
첫 번째 방법은 휴지 상태 없이 스프링 부트를 사용하는 것입니다.저장소 인터페이스에서 자체 대량 저장 방법을 생성함으로써 POJO나 휴지 상태를 사용하여 쿼리를 생성하지 않고도 SQL 삽입 쿼리에 직접 바인딩할 수 있었습니다.그 방법의 예를 다음에 나타냅니다.
@Query(value = "insert ignore into location (latitude, longitude) values(:latitude, :longitude)",
nativeQuery = true)
public void bulkSave(@Param("latitude") float latitude, @Param("longitude") float longitude);
이렇게 하면 가비지 수집 오버헤드가 대폭 줄어들어 프로세스가 항상 느려지지 않고 실행될 수 있습니다.그러나, 제 목적에서는, 9400만 회선에 3일이 걸리는 등, 훨씬 빠른 속도였지만, 제 목적에는 아직 너무 늦었습니다.
나에게 보여 준 또 다른 방법은 쿼리를 한번에 보내는 대신 스프링 배치를 사용하여 대량으로 보내는 것이었습니다.데이터 소스가 특이했기 때문에 플랫 파일이 아니었기 때문에 데이터를 처리하여 한 번에 한 항목씩 ItemReader에 입력하여 파일에서 직접 가져온 것처럼 보이게 해야 했습니다.이것 또한 속도를 향상시켰지만, 나는 이것을 시도하기 전에 훨씬 더 빠른 방법을 찾아냈다.
가장 빠른 방법은 원하는 테이블을 CSV 파일에 쓴 다음 압축한 파일을 데이터베이스로 전송하여 압축 해제 및 데이터베이스로 직접 Import하는 것입니다.이는 위의 표에 대해 다음 SQL 명령을 사용하여 수행할 수 있습니다.
LOAD DATA
INFILE `location.csv`IGNORE
INTO TABLE Location
COLUMNS TERMINATED BY `,`
OPTIONALLY ENCLOSED BY '\"'
LINES TERMINATED BY `\n`
(latitude, longitude)
SET id = NULL;
이 프로세스에서는 파일을 로드하는 데 15분, 2.2 Gbs의 파일을 압축하는 데 5분, 파일 압축 해제에 5분, 파일 생성에 2~3분이 소요되었습니다.파일의 전송은, 네트워크의 기능에 의해서 다릅니다.30분에 네트워크 전송 시간을 더한 이 방법은 필요한 대량의 데이터를 데이터베이스로 Import하는 가장 빠른 방법이었지만, 상황에 따라서는 더 많은 작업이 필요할 수 있습니다.
그래서 제가 발견한 이 문제에 대한 세 가지 가능한 해결책이 있습니다.첫 번째는 동일한 프레임워크를 사용하여 솔루션을 쉽게 이해하고 구현할 수 있습니다.두 번째는 프레임워크의 확장을 사용하여 같은 기간에 더 큰 전송을 허용합니다.마지막이 가장 빠르고 데이터 양이 너무 많은 경우 유용하지만 이를 위해 소프트웨어를 구축하려면 사용자의 작업이 필요합니다.
언급URL : https://stackoverflow.com/questions/54083550/will-spring-batch-prevent-my-program-from-grinding-to-a-halt-on-94-million-trans
'programing' 카테고리의 다른 글
| 여러 Vue 애플리케이션, 여러 엔트리 파일, 동일한 Vuex/Vue3 Compostition Api 스토어 [반응성 상실] (0) | 2023.02.03 |
|---|---|
| @SmallTest, @Medium의 목적은 무엇입니까?Android에서 테스트 및 @LargeTest 주석을 사용하시겠습니까? (0) | 2023.02.03 |
| PHP에서 if 문을 깨는 방법은 없나요? (0) | 2023.02.03 |
| MySQL: 페이지란? (0) | 2023.02.03 |
| js 또는 jQuery를 사용하여 Ajax 요청에 커스텀 HTTP 헤더를 추가하려면 어떻게 해야 합니까? (0) | 2023.02.03 |