개발 Q&A

제목 이거 속도가 장난이 아니군요. 개선 방법 있을까요?
글쓴이 정도령 작성시각 2012/11/15 07:33:22
댓글 : 12 추천 : 0 스크랩 : 0 조회수 : 20035   RSS
 현재 프로그램이 
1. DB 가져온다.
2. 특정 필드값을 가지고서 For 문을 돌린다.
3. 그 for문 안에서 특정 DB 값을 가져와서 또 다시 for 문을 돌린다.
4. 그 for 문 안에서 if를 사용해서 값이 존재하는지 확인한다.

대충 이런식으로 구성되어 있는데 이거 한번 돌리면 완전 세월입니다.
대충 10시간 이상의 시간이 걸리고 있거든요.

제작년쯤엔가 이런 현상이 있어서 분명 프로그램을 수정해서 10시간이 넘게 걸리던것을 단 10~30분 이내로 
단축 시켰던 기억이 있는데...
머리가 나빠서 그것을 기억해 내지를 못하고 있습니다.

고수님들... 하수를 위하여 조언 좀 해주쉐욤...

참고로 필드가 대략 40만개가 좀 넘습니다.

ㅜㅜ;;

 다음글 foreach 사용할 때 예외처리? (2)
 이전글 url이 포함된 문자열에 자동으로 링크 태그넣는 방법 (2)

댓글

milosz / 2012/11/15 07:38:15 / 추천 0
레코드가 40만개란 말씀이시죠?  'ㅅ'
 
쿼리문에서 IF와 JOIN을 활용해서 처리하는 방법이 있고
그래도 느리다면 temporary table을 구성해 해당 연산을 처리한 값을 넣어 가져오는 방법이 있습니다.

좀더 디테일한 테이블 정보를 주시면 간단한 가사코드 정도는 작성해볼 수 있을 것 같습니다.

한대승(불의회상) / 2012/11/15 07:46:55 / 추천 0
milosz님 의견에 한 표
mysql 5.0 이상이면 sub query도 추천 합니다.
어떤 방법을 쓰시던 지금 보다 엄청난 성능 향상이 있을거라 기대 되는 군요 ^^
정도령 / 2012/11/15 07:58:37 / 추천 0
예전에 어떻게 해결을 했었는가 궁금하여 그때 작업했던것을 찾아 보았습니다.
10시간이 넘게 걸리던것을 단 13분으로 줄일수 있었던 방법을 찾은것 같습니다.

위 설명한 내용에서 1번에서 하나의 DB Table 에서 몽창(전부) 가져와버리거든요. 
그것을 for 문으로 돌려버리는데, 그렇게 해서 속도가 엄청 느린것 같습니다.
그때 당시에 그래서 전체 개수만을 가져와서 for 문 안에서 필요한 필드 한개씩 가져와서 
돌렸던것 같습니다.

아무튼 수정해서 테스팅 하여 보고 결과가 좋으면 댓글 남겨 놓겠습니다.

불의회상님 오래간만이에요? 잘 지내시지요?
milosz 님 아침 일찍 이렇게 빠른 답변 감사합니다.

두분 모두 엄청 부지런들 하시군요. 아무튼 감사드립니다.
결과는 곧~~~ 올리겠습니다. ^L^
milosz / 2012/11/15 08:32:09 / 추천 0
음... PHP에 모든 데이터를 불러온 후 처리를 하신다면
1) 인덱싱을 통해 속도를 올릴 수가 없고
2) 쿼리를 여러번 반복적으로 처리하게 되어 자원이 낭비됩니다.

해당 프로세스를 DB에서 처리한 후 데이터를 가져온다면
13분으로 줄인걸 30초 이내로 줄일 수도 있습니다. 'ㅅ'

...약파는 기분이지만 약파는건 아닙니다 ㅋㅋ
 
한대승(불의회상) / 2012/11/15 08:42:03 / 추천 0
데이터 건수가 40만건... 그걸 모두 메모리로.. ㅎㄷㄷㄷ
좋은 결과가 나올거 같은데요... ^^

헛발이 / 2012/11/15 08:45:10 / 추천 0
저도 그렇게 하고 있습니다.. 절대로 루프안에서 쿼리를 하지 않습니다..
그게 속도르려지는 원인이 되거든요...

milosz님 말씀대로 두개의 테이블을 한번씩 쿼리 해 옵니다...
그 다음에 그걸 배열에 넣고 루프 돌려 데이터를 확인 하는게 좋을거 같습니다..

예를 들면요... 스케쥴을 만든다고 한다면...

1일부터 31일까지 루프돌면서 1일, 2일, 3일....에 해당되는 데이터를 가져 오는것이 아니고...
데이터를 한번에 불러와서 배열에 넣고 1일에 해당되는 배열을 가져오게 하는거죠...
변종원(웅파) / 2012/11/15 09:34:48 / 추천 0
게시물 카운트를 그렇게 한 소스도 봤습니다. 그것도 웹에이전시가 상용으로 판매한 소스에서요. ㅋㅋㅋ

게시물 전체를 가져와서 for문 돌리면서 카운트를 하나씩 올립니다. 그래서 총 게시물 카운트를 구합니다.

데이터가 없을 때는 잘 돌아갔습니다. 컨텐츠를 입력하다보니 점점 느려집니다. 

소스를 까봤습니다. 저래 되어 있더군요. 그외에도 기상천외한 소스가 난발..

결국 에이전시 사장 불렀고 그럴 일 없다고 딱 잡아 떼는데 저 소스 보여주니 "잘못했습니다. 살려주십시오." 

ㅋㅋㅋ

서브쿼리나 인덱스 잘 걸고 조인 하시면 훌륭한 결과 나올 겁니다.
정도령 / 2012/11/15 12:29:07 / 추천 0
이거참 정말 머리 아프군요.

일단 두개의 테이블이 있고 각각 A 와 B 라고 하였을때 A에 1에 해당되어지는 필드가 약 1만개 정도 있다고 보고, B 테이블에는 수십만개의 필드가 존재합니다. 그중에 A 테이블의 a1 이라는 필드값과 B의 b1 이라는 필드값이 일치할 경우 B 테이블에 b2, b_date 등에 값을 넣기 위해 아래와 같이 하면 안되는건가요?
아~ 이거참, 평소에 join 문을 전혀 써보지를 않아서 엄청 헤매는군요.

조언 좀 부탁 드립니다.


  1. $this->db->set('b.b2''b.b2 + 1', FALSE);  
  2. $this->db->set('b.b_date', time());  
  3.   
  4. $this->db->where('a.id', 1);  
  5. $this->db->where('a.id = b.id');  
  6. $this->db->update('table as a, table2 as b');  
milosz / 2012/11/15 13:01:24 / 추천 0
조인을 하기 위해서는 A 테이블의 레코드와 B 테이블의 레코드를 연결하는 키값이
무엇인지 먼저 염두하셔야 합니다.
SELECT A.*, B.*
FROM A
LEFT JOIN B ON A.id = B.target_id

위 SQL의 예는 B테이블의 target_id 필드에는 A테이블에 있는 id필드를 참조해서 데이터를 가져옵니다.
이렇게 조인을 하면 A테이블의 레코드와 B테이블의 레코드가 id값을 기준으로 연결된 상태가 됩니다.

이 상태에서 a1과 b1을 비교해 b_date에 값을 넣는 예제를 적어보면...
SELECT A.*, B.*, IF(A.a1 = B.b1, current_date(), null) as b_date
FROM A
LEFT JOIN B ON A.id = B.target_id
A.*, B.* 이렇게 select한건 그냥 편의상 입니다. 필요한 필드만 선택하시면 되겠습니다.

$this->db->select('A.*, B.*, IF(A.a1 = B.b1, current_date(), null) as b_date',false);
$this->db->join('B', 'A.id = B.target_id');
$query = $this->db->get('A');

잠깐... 적고보니... Select가 아니라 Update 문이네요?;;


milosz / 2012/11/15 13:14:12 / 추천 0
업데이트 쿼리는 요로코롬.. 
update A left join B on A.id = B.target_id
set B.b2 = B.b2 + 1, B.b_date = time()
where A.a1 = B.b1
그런데 이렇게 업데이트를 하면 업데이트 할 때마다 해당하는 행이 갱신 될듯 하네요
a1이나 b1필드의 값을 비워주는 등의 부분도 필요할듯..


그런데 액티브레코드에서 update 할 때도 join이 동작 하나요? 흠;
변종원(웅파) / 2012/11/15 13:37:04 / 추천 0
milosz/ 해보지는 않아서 될지 안될지 모르겠네요. 소스를 까봐야겠네요. ^^
정도령 / 2012/11/15 17:07:05 / 추천 0
완벽히 해결이 되었군요.
JOIN 해서 작업을 하니 열시간이 넘게 걸리던 작업이 고작 50초 정도 걸리네요.
이것도 살짝 거시기 하면 아마도 10초 이내로 수정 가능도 할것 같습니다.

저의 무식함이 여러분들의 도움으로 살짝 업그레이드가 되었네요.
참으로 감사합니다.

다시 한번 웅파님, 불의회상님, milosz 님, 헛발이님 감사드립니다.

시간 좀 만들어서 가평에서 한번 뭉칩시다? 저번에 나 술 먹고 뻗었다고 나보기 엮겨워 그러시는거라면... ㅡㅡ;;

보고파요~~~!