개발 Q&A

제목 php코드가 메모리를 너무 많이먹습니다..
글쓴이 Mook 작성시각 2013/06/04 12:03:17
댓글 : 10 추천 : 0 스크랩 : 0 조회수 : 20111   RSS
제가 짠 코드가 시간복잡도를 눈대중으로 잡아봐도 몇십억(많게는 몇천억)인데..
메모리를 어떻게 줄일수있을까요? 아직 초보라 성능관리를 잘 모르겠네요 ㅜ
제한을 300M까지 올려도 메모리 초과에러가 나는데, 인터넷에 찾아봐도 300M까지 올리는 경우가 일반적으론 없나보더라구요;
질문 대충 나눠보겠습니다.

1. 주로쓰는 함수들은 explode, in_array, array_merge(_recursive), substr, array_keys, str_replace 등과 최종으로 array_multisort 한 번 인데, 유독 메모리를 많이 잡아먹는다던가 하는 함수가 있나요?
메모리땜에 디버깅용 echo는 찍을 생각도 못하고있어요..ㅠ

2. 메소드로 기능을 조각내어놓으면 성능이 더 안좋은가요?
가독성도 가독성이지만 메소드 종료와 함께 변수에 할당된 메모리가 초기화되는걸로 알고있어서 그런효과도 의도한건데..

3. 배열의 키 개수는 얼마안되는데 4차,5차원까지 들어갑니다. 이것도 성능에 영향을 주나요?
   Ex) 1depth: 2개 => 2depth: 5개 => 3depth: 100개 => 4depth:20개 

4. DB에 매번 쿼리를 날리지 않으려고 거의 통째로 뽑아와서 연산하는데 그게 문제가...되겠지요?
근데 그렇다고 매번 쿼리를 날리는것도 너무 속도에 영향을 주는데 어떤 대안이 있을까요??

5. 이외에도 조언해주실 말씀 있으시다면 부탁드립니다.


읽어주셔서 감사합니다. 도와주세요~~!

 다음글 phpmyadmin 에서 디비디자이너 기능 사용. (1)
 이전글 웅파님 책을 보고 싶은데.. 혹시 언제쯤 나오나요? (2)

댓글

criuce / 2013/06/04 12:29:48 / 추천 0
이미지 처리 연산이 들어가는 경우가 아니라면 300M는 좀 과하네요.

메모리 사용량은 처리속도랑도 상관이 있으니 의심되는 함수 앞으로 시간값 찍어서 처리속도가 유난히 느린 부분을 찾아보셔야 할 것 같아요.
한대승(불의회상) / 2013/06/04 12:40:53 / 추천 0
통째로 들고와 연산 하는게 문제일거 같습니다.

들고오기전 메모리와 들고온 후 메모리 비교해 보세요.
변종원(웅파) / 2013/06/04 12:48:50 / 추천 0
광고 리포트쪽 같은 경우 조인 없이 한번에 통 배열로 들고 와서 
몇번을 for, foreach 돌려서 처리하는 소스를 봤습니다.
512정도 잡아주고 돌렸습니다. ㅋㅋ
(ci도 아니고 native php에서...)

적정한 선에서 조인 쿼리로 돌리고 배열 연산을 줄였습니다.

배열 연산이 메모리 상당히 잡아먹습니다.
kokanee / 2013/06/04 13:21:04 / 추천 0
DBMS를 왜 쓰시는지요?
단순 저장을 위함이라면 다른 방법도 많은데 말이죠..

모든 연산은 성능이 좋은 DBMS에게 맡기세요..
Mook / 2013/06/04 16:27:25 / 추천 0
 criuce, 불의회상 // 유난히 느린부분이 다중반복문 부분에 들어가면서 부터입니다. 그전에 DB에서 전부 뽑아와서 변수넣는거에는 메모리가 2~3M정도 쓰이는데, 연산시작하면서부터 기하급수적으로 늘어납니다.
변수 초기화도 중간중간 해주면서 최대한 낭비되는 변수를 줄였는데도 이러네요 ㅜㅜ

웅파 // 그렇게 처음에 구현했는데 연산시간이 10시간이 넘어버려서.. DB에 부하도 많이 가해지는거같구요.. CPU를 네개나 잡아먹던데.. 그래서 통째로 불러오는 방식으로 바꾸니 이런문제가 ㅜㅜ 하....돌아버리겠네요.. 1000M도 overflow 짜잔..

kokanee // 프로시저말씀하시는건가요? DB부하때문에 이런방식으로 리팩토링한건데 그래도 괜찮을까요? 데이터가 업데이트되면 실시간으로 반영되어야하는 연산이라서요..
변종원(웅파) / 2013/06/04 16:31:09 / 추천 0
Mook/ 그래서 광고리포트는 쌓인 리포트 테이블에서 바로 쿼리를 하지 않습니다.

예를 들어, 1시간 단위로 계산한 결과를 B 테이블에 넣어놓고
웹에서는 B테이블에서 최소한의 join과 연산을 하도록 하여 보여줍니다.

cron으로 하루단위 리포트 작업을 하거나 시간단위로 하거나 그렇습니다.
/ 2013/06/04 18:29:43 / 추천 0
 Mook// 
일단
반복량을 줄이고
정확하게 어느부분에서 급격하게 사용량이 늘어나는지 확인을 해보셔야 할 것 같습니다.



웅파님이 써주신 것처럼 광고리포트는 로그테이블과 보고서용 테이블을 별도로 구분하셔서

일정 시간마다 갱신해주는 형태로 작성하는 것이 좋습니다.

보고서용 테이블은 이후 php에서 작업하기 좋은 구조로 최적화해서 쌓아주시면 더 좋겠죠





그리고 어떻게 된건지는 잘 모르겠지만 10시간씩이나 연산이 걸린다면 일반적인 상황은 아니라고 봅니다.

db에 부하가 많이 걸린다고 하셨는데 인덱스라든지 db 구조라든지

db최적화가 어느정도 되어있는지도 좀 궁금하네요

대략 몇건의 데이터가 사용되고 몇개의 테이블이 있는지도 좀 궁금하구요




그리고 처음 시작하는 메모리 사용량이 3m인데 나중에 몇백메가가 된다면...

사용하고 초기화되지 않는 변수가 존재하는 것이 아닌가 싶네요

혹은 객체를 중간에 계속 생성한다거나...


3메가가 300메가 이상으로 늘어난다고 하셨는데

대략적으로 for문이 100번 이상 수행되는 곳에 문제점이 있지 않을까 싶기도 하네요
Mook / 2013/06/04 19:45:46 / 추천 0
 웅파, 키//

두분다 답변 너무 감사합니다.

일단 반복문 내에서 약간의 구조변경을 진행하였습니다.

규모가 큰 변수들의 경우 원래 초기화를 넣어두었긴 한데, 혹시몰라 나머지도 대부분 다 초기화를 넣어주었습니다.

그리고 아이템속성에서 연산에만 필요하고 결과에는 필요없는 필드들을 연산직후 전부 unset해주고,

범주를 더 세부범주로 나누어 진행하였습니다.

그 결과 input이 몇 백개 단위일때는 메모리 30M 정도에 실행시간은 수분 내로 완료되는데, (input 개수의 1~10배가 비교대상의 수 입니다. 그게 감싸고 있는 반복문들도 있지요....)

몇 천개 단위일때는 깜깜무소식이네요..허허..

돌려야되는 아이템은 몇십만갠데... 까마득하네요...

그냥 나누어 진행하는수밖에 없을 것같은데..

지금 이 수 분이라는게 몇 천개로 쪼개진 세부범주 하나기 때문에, 전체시간으로 따지면 아직도 느리거든요 ㅜㅜ

더 뭘 시도해볼 수 있을까요?

+)
디비 인덱싱은 유니크하게 관리되어야 하는것들은 전부 인덱싱 되어있고, 한 두어개 정도 더 인덱싱이 되어있는 상황입니다만... 구체적으로 디비구조랑 연산을 보여드리지 않는 이상 설명이 되지 않겠죠..ㅜㅜ


/ 2013/06/04 23:15:20 / 추천 0
mook // 

유니크 한 것도 필요하지만 where절에서 연산을 하는 것과 group 에 들어가는 것 등

연산에 필요한 것은 다 인덱스를 걸어주셔야 합니다.



전체를 다 불러오시는 것에서 기하급수저으로 메모리 할당량이 늘어난다고 하셨으니

db를 몇천개에서 몇만개 단위로 쪼개서 limit를 걸어서 불러 온 후 for문을 돌려보는 것도

한 방법이 되겠네요.


(전체를 약 10~20여개 정도로 페이징하여 처리한다고 보시면 될듯)

몇십만개라고 하면... 그렇게 많은 양은 아니라고 생각되는데

지나치게 시간이 오래 걸리네요



현재로써는 가장 빠르게 처리하는 방법은

db 인덱싱 + 페이징 처리로 분할 연산 이겠네요...

시간은 없고 결과값이 1번만 나오면 될 경우라면

무식한게 답이죠... db서버는 한대 두고 php 서버를 여러대로 분할해서

각각 값을 가져오고 합치세요...

물론 db 쿼리시간보다 php 런타임이 훨씬 길 경우에 해당하겠지만요..




...그리고 저라면 부하가 어디서 걸리는 것인지 좀 더 살펴보겠습니다...

db 쿼리 시간은 얼마나 걸리는지.. 

어느 로직 처리하는데 시간이 오래 걸리는지...

현재 가장 큰 문제는.. 뭐가 문젠지 모르는게 문제겠네요..

문제를 알아야 해결책을 찾는데..

Mook / 2013/06/05 16:45:02 / 추천 0
답변해주신 분들께 경과보고차원에서 댓글 남깁니다...ㅋ

메모리문제는 좀 해결됐습니다. 현재 메모리사용량 이전대비 절반정도로 줄었습니다. (그래도 크지만..)

원랜 뭉텅이로 모아서 정렬한후 결과를 뱉는식이였는데, 소범주마다 정렬해주고 바로 DB에 넣고, 그 소범주에 해당하는 아이템들을 unset하는식으로 변경했습니다. 근데 더느려진 기분이... 정렬을 자주해서 그런듯 ㅜㅜ

쿼리는 소범주가 한번 실행되면 20개정도 날리고, 하나당 걸리는 시간은 0.005초 안팎입니다. (DELETE, INSERT 제외하고 SELECT만...)

코드를 쪼개고 쪼개서 가장 오래걸리는 곳을 찾았는데 더 개선점을 찾지 못했습니다 ㅜㅜ

해당 부분은 한 아이템당 0.2~1.5초가량 잡아먹습니다..

지금 계속 수정작업중이라 글이 정신없지만 너그러이 봐주세요...ㅜㅜ