일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 셀레니움
- 라라벨
- 코드이그나이터
- 우분투 20.04
- 20.04
- 회고
- 맛집
- C
- 맥
- 업비트
- MySQL
- 옵티머스g
- 프레임워크
- 라즈베리파이
- Raspberry Pi
- 우분투
- 옵G
- ubuntu
- codeigniter
- php
- 제주도
- FMS
- 옵지
- upbit
- Selenium
- Ubuntu 20.04
- Laravel
- 옵티머스 g
- 라즈비안
- TiL
- Today
- Total
평범한 이야기들
[PHP] 프로그램 실행 시간 단축 이야기 본문
이전 상황
현 회사에 이직 후 가장 먼저 한 일은 작업시간이 오래 걸리는 프로그램의 실행시간을 단축시키는 것이었습니다.
해당 프로그램은 자정 12시에 시작돼 16~20시간 오래 걸렸을 때에는 22시간까지도 걸리는 작업이었습니다. 작업의 내용은 간단했습니다. 매일 DB에 존재하는 2500~3000만 건의 데이터를 가져와 텍스트로 저장하는 것이었습니다. 따라서 시간을 줄이기 위해 다음과 같은 작업을 진행했습니다.
문제 파악
해당 프로그램을 확인 결과 아래와 같은 문제점들이 있었습니다.
1. 불필요한 로직 및 사용하지 않는 변수 존재
- 많은 계산을 한 후 결과를 사용하지 않는 로직
- 사용하지 않은 if-else 문
- 클래스 객체를 선언하고 사용하지 않는 변수
2. 정보를 가져오기 위한 여러 개의 DB 쿼리 작업
- 단순 이름을 가져오기 위해 쿼리 사용 -> 2500~3000만 번의 쓸데없는 추가 쿼리 실행
3. 큰 용량을 다른 네트워크 디스크에 이동하는 작업
- 10~15G 정도 되는 파일을 네트워크 디스크에 쓰면서 생기는 IO 속도 문제.
위 문제점을 처리하기 위해 불필요한 변수 및 로직을 제거했습니다. 그 후 여러 개로 나누어져 있는 쿼리를 한 번에 가져올 수 있도록 원 쿼리로 변경했습니다. 파일 이동 역시 로컬 디스크 특정한 곳으로 저장해 접근할 수 있도록 변경했습니다.
하지만 시간은 나아지지 않았습니다. 그래서 마지막 방법으로 하나의 프로세스를 이용하던 프로그램을 여러 개의 프로세스가 동시에 처리할 수 있도록 변경했습니다.
해결방법
기존에 한 프로세스를 이용해 작업하는 과정을 여러개의 프로세스로 이용해 작업하는 것으로 변경했습니다.
1. 수동으로 데이터의 키의 범위를 지정하는 작업
2. 범위에 맞는 데이터의 키를 사용해 범위에 있는 데이터 키를 파일로 저장하는 작업
3. 저장된 데이터 키의 파일을 이용해 정보를 가져와 파일에 쓰는 작업
4. 작업이 모두 끝난 후 하나의 파일로 합치고 이동하는 작업.
첫 번째는 수동으로 데이터 키의 범위를 지정해주는 것이었습니다. 해당 부분도 자동으로 하고 싶었지만, 데이터의 수정 삭제가 실시간으로 이루어지고 있는 상태이며 해당 범위를 뽑는 것조차 시간이 상당히 소요되었기 때문에 중요도를 뒤로 미루었습니다. (해당 부분은 더 고민을 해서 작업을 진행해야 될 것 같습니다.)
<?php
// mysql 속도 문제로 수동작업, 150만개씩 작업진행하게 범위 지정.
// TOTAL JOB 20개.
$rangeIndex[] = "0";
$rangeIndex[] = "1500000"; // 1.5
$rangeIndex[] = "3000000"; // 3
$rangeIndex[] = "4500000"; // 4.5
....
두 번째로는 범위에 있는 데이터의 키를 사용해 각 잡에 맞는 데이터 키 파일을 만드는 작업을 했습니다. 그래서 실제 작업하는 프로그램과 해당 프로그램을 여러 개 돌릴 수 있는 실행 파일을 만들었습니다. 실행 파일에서는 수작업 한 인덱스 범위를 이용해 각 프로세스에 알맞은 값을 넘겨주고 끝내는 일이었습니다.
<?php
//...
//프로그램에 맞는 설정
//...
for($i = 1; $i < count($rangeIndex); $i ++) {
$cmd = "php {$path}prewWork.php {$i} {$rangeIndex[$i-1]} {$rangeIndex[$i]} > /dev/null 2>&1 &";
exec($cmd, $output, $error);
}
각 작업은 인자로 받은 인덱스 범위를 이용해 인덱스 키 범위 파일을 각각 생성하게 했습니다.
세 번째 작업은 위에 만들어진 파일을 이용해 실제 데이터 파일을 만드는 일이었습니다. 인덱스 키 범위 파일을 만드는 방법과 동일하게 작업을 했습니다.
<?php
//...
//프로그램에 맞는 설정
//...
for($i = 1; $i < count($rangeIndex); $i ++) {
$cmd = "php {$path}realWork.php {$i} {$rangeIndex[$i-1]} {$rangeIndex[$i]} > /dev/null 2>&1 &";
exec($cmd, $output, $error);
}
네 번째 작업은 각 만들어진 데이터 파일을 합친 후 이동시키는 일이였습니다. 합쳐야 하는 파일이 20개였기 때문에 단순하게 루프를 돌려서 명령어를 만들게 하고 실행시켜 파일을 이동하는 작업이었습니다.
<?php
//...
// 모든 작업이 끝났는지 확인하는 곳
//...
// 파일 합치기 명령어 제작
$cmd = "sudo cat ";
for($i = 1; $i < count($pid); $i ++) {
$cmd .= "파일명_{$i}.txt ";
}
$cmd .= " > 파일명.txt ";
// 실제 명령 실행
exec($cmd, $output, $error);
// 파일을 옮겨준다.
rename($filPath ."파일명.txt", $moveFilePath);
결과
위와 같이 작업을 마무리하고 테스트했을 때 결과는 아래와 같습니다.
기존 작업 결과 | 작업 후 결과 |
평균 20시간 | 2번 작업 : 평균 2분 |
3번 작업 : 평균 2시간 30분 | |
4번 작업 : 평균 3분 |
평균 20시간이 걸리는 프로그램이 평균 3시간 이내로 작업이 완료가 되었습니다. 나름 성공적으로 변경한 것으로 판단했습니다. 하지만 문제점도 아직은 있다고 생각합니다. 수동으로 인덱스 범위를 지정해주는 것입니다. 해당 부분도 DB에서 판단해서 가져올 수 있었으면 좋았을 텐데 해당 쿼리 자체가 시간이 너무 오래 걸렸습니다. (제가 실력이 없어서 생긴 문제로 보입니다. 아무리 시간을 단축시켜서 범위를 뽑아내고 싶어도 시간이 상당히 소요되었습니다.) 따라서 어쩔 수 없이 생긴 수동 작업이 존재합니다. 계속 고민 중이며 풀어야 할 숙제로 남겨두어야겠습니다.