시냅스

무지렁이 개발자의 대량 데이터 처리 유람기 본문

개소리

무지렁이 개발자의 대량 데이터 처리 유람기

ted k 2023. 2. 14. 21:00

 

걸어서 4000만개 데이터가 있는 테이블 8개 속으로...

 

지금 하고 있는 프로젝트는 전국의 어떤 데이터(대외비임다... )를 관리하고, 맞춰서 GIS 로 개발하는 프로젝트를 하고 있다.

프론트 오피스에서는 데이터들을 지도에 그려주고, 백 오피스에서는 데이터들을 관리 혹은 처리하게 된다.

나는 대체로 백오피스의 업무들을 배당 받았는데,

이미 엑셀 파일을 유저가 업로드 할 수 있게 처리하거나 권한 작업들은 해두었던 상태였다.

그리고 대망의 각 500만개 데이터가 있는 테이블 8개 속으로 걸어들어갔다.

 

전국의 데이터는 시도 별로 시군구 별로 테이블이 따로 있었고 정렬될 필요가 있었다.

전국의 어떤 데이터가 들어왔을 때, 서울시라고 한다면

다시 서울시 중랑구 까지 분류가 되어 각개 테이블에 적재되었어야 했다.

또 유출과 유입을 구분해야 하는데 서울시에서 부산시로 유출이 이뤄졌다면

반대로 서울에서 부산으로의 유입도 데이터로 잡아 또 다른 테이블에 적재해줬어야 했다.

 

관리를 위한 테이블이나 필요한 컬럼을 추가할 필요는 있었지만,

기존 데이터들이 있었기 때문에 테이블들이 마련은 되어 있었다.

테이블에 권역별 합계나, 합계에 대한 비율, 순위에 해당하는 컬럼도 있어 처음 들었던 생각은

'아 ㅇㅋ 테이블에 때려 넣고 다시 데이터 계산하면 되겠다 ㅋㅋ' 이었다.

일 자체는 그렇게 까다로운 편은 아니라고 생각했다.

실제로 수행했고, 500개 데이터를 넣는데 3분이 걸렸다...

 

기존 방식의 데이터 처리 순서도

 

패착은 이랬다. 

데이터는 엑셀의 형태로 들어가고 한 row 마다 데이터는 새로 산정되어야 했다.
(들어오는 row 들이 같은 권역이라는 확신이 없기 때문에.)

그러므로 데이터 1개가 들어가면 데이터를 insert 하고 8개의 테이블에 넣은 후에 각 8개 테이블에서

해당하는 권역(시도 혹은 시군구) 합계와 순위를 update 해야 했다.

만약, 유출에 해당하고 시군구의 테이블이라고 하면 서울시 중구 ... 노원구 데이터가 테이블에 상존해있었고,

심지어 감수광 제주도의 데이터까지 그야말로 전국의 데이터가 들어있었기 때문에 허접 개발자인 나는

성능은 저언혀 고려하지 않고 하나 넣고 다시 계산하고 하나 넣고 다시 계산하는 데이터를 향한 구애를 보여주고 있었다.

 

1개의 데이터가 들어가면 8개의 테이블에 들어가야 하는데, 이 때 또 꼭 8개라고 말하지 못한다.

왜냐면 시군구의 데이터는 시도로 뭉뚱그려 다시 합산해야 하기 때문이었다.

이야기인 즉슨 서울시 중구 ... 노원구의 합계와 순위는 따로 산정되는데,

데이터들은 다시 서울시로 합산이 이뤄져야 했다.

수식으로 짜보면 (n * 6) + (((n / 260 * 17) + 1)* 2) 였다.

유입과 유출을 산정해야 했고, 260 과 17 은 각각 전국의 시군구와 시도 개수이다.

(정확한 숫자는 아니다, 소수점은 버린다.)

 

또... 이미 테이블에는 각 500 만개 정도의 데이터들이 들어가 있었다.

아주 많은 수의 데이터는 아니었지만 각각을 select 하거나 update 하기에는 무리가 있었다...

그런데 나는... 시군구 한 개의 엑셀 데이터 row 를 insert 하고 다시 해당하는 권역의 데이터를 가져와서

합계를 합산하고... 합계로 비율을 계산하고... 순위를 매겨서... update 하고...

그걸 토대로 다시 시도 데이터를 가져와서... 합계... 순위... update... 유출... 했으면 유입도...

누구도 피해받지 않는, 무공해 브루트포스를 하고 있었다.

 

 

아차 싶었다. 단순히 배수라고 한다면 5천개면 30분이고 5만개면 300분이었다...

그럴 거면 차라리 장부로 쓰는 게 나을 것 같았다... 그러다 보면 천장도 뚫겠지...

 

현재 방식은 db에 커넥션이 너무 잦고, update가 너무 많았다.

거의 매일 밤 집 앞까지 데려다주는 남자친구 같았다...

upsert 나 merge 를 쓰기보다 현재 구조와 헤어질 결심을 했다.

 

우선 내가 하려던 일을 다시 정의했다.

나는 데이터를 각 테이블에 넣고 이력으로 남기고 넣은 데이터들을 합산하고

비율과 비율을 토대로 순위로 매겨서 클라이언트한테 보내주고 싶다.

(사실 내가 보여주고 싶은 건 아니다. 나는 놀고싶다. ㅋㅋ)

데이터를 삭제할 때에는 삭제 처리에 유효한지 검증하고 이력으로 남기고 soft delete 해주고 싶다.

(나는 아니고 회사가. ㅋㅋ)

 

목적이 명확해지는 기분이 들었고,

데이터들을 한 땀 한 땀 update 할 게 아니라

View table 로 파티셔닝과 오더링 하여 합계와 비율, 순위를 보여주면 될 거라는 생각이 들었다.

그렇다면 데이터 자체는 insert, delete 만 하게 되어 데이터베이스와 connection 빈도가 확 줄어들게 되고

클라이언트에서 불러낼 때 쿼리로 매겨 반환하기 때문에 성능적 이점이 있을거라 생각했다.

View table 을 쿼리로 작성하고, 기존 데이터와 일치하는지 검증한 뒤 과감하게 관련된 컬럼을 삭제했다.

(사실 나만 쓰는 테스트 테이블이었다. ㅋㅋ)

 

새로운 방식의 데이터 처리 순서도

 

필요한 데이터들을 정의했다. 

마지막으로 넣은 데이터의 sequence 값과 기타 등등의 데이터들이었고

충분히 유저가 업로드 할 때 마련할 수 있는 데이터들이었다.

단, 시도 관련된 데이터는 메모리 연산을 위해

Dto를 새로 만들어 시군구에 해당하는 데이터들에 대한 조건을 equals로 재정의 하여

같은 시도와 조건인 경우 합산하고 없는 경우라면 list 에 넣어주는 방식으로 진행하였다.

이렇게 진행한다면 딱히 합계나 비율, 순위를 다시 구해줄 필요가 없어졌고

그저 각각의 list 에 들어있는 데이터들을 각각의 데이터베이스에 적재하기만 하면 되었다.

 

덕분에 800개 데이터를 적재할 때 30 ~ 80 ms 가 걸렸고,

(물론 모든 8개 테이블에! 이력과 위에서 설명하지 않은 관련 테이블도 따로 있다. 하하.)

800개 이상의 데이터는 Multi Row insert 를 사용하여

쿼리가 너무 길어 잘라서 넣어줘야 했기 때문에 (( 30 ~ 80 ) * n) ms  가 걸리게 되었다.

뭔가 조금 손해 본 기분이었지만 그래도 만족스러웠다.

내가 비용을 줄였고, 성능을 높였고, 이런 만족감도 물론 있지만 

역시 가장 기분 좋은 것은 '내 말이 맞지?' 일 때인 것 같다.

 

나는 자기 효능감이 매우 중요한 소인배이다.

 

 

 

Comments