PHP 코드 최적화 가이드

수년전부터 PHP라는 언어에 손을 대기 시작하면서 겪었던 여러가지 문제점을 보완하며 현재의 제작방식의 기초가 되어온 룰들입니다.
국내의 일부 PHP 솔루션들은 이러한 룰의 기본조차 되어있지 않습니다.
높은 PHP 퍼포먼스를 추구하기 위한 코딩방식에 대한 설명입니다.
정말.. 동접 100명도 안됬는데 나가 떨어지는 코드들 질립니다.. 돈주고 산것들마저 한심하더군요.


1. 가장 중요한것은 기초 알고리즘과 로직들이다.
근본적인것이 서있지 않으면 그 위로 어떠한것을 짜더라도 또 이 밑의 어떠한것을 하더라도 소용없습니다
PHP는 코딩이 쉬운 반면 C처럼 메모리 리크를 뿜으며 죽는 경우도 있으며,
(그러나 아파치의 프로세서가 죽어나갔다 다시 리스폰됩니다)
잘못짜면 정말 많은 부하를 불러오는 저질 언어가 될 수 있습니다.
그리고 PHP의 모든 펑션은 메모리 리크현상이 발생할 때 크게 오동작을 하는 경우가 많습니다.
PHP의 메모리 리크는 실행즉시 발견되는것이 아니라 점점 서버에 누적이 되어가는 형식입니다.
한마디로 잘만 짜면 손쉽게 높은 퍼포먼스를 얻을수도있다는 점입니다. (안정성은 다소 떨어집니다만)
모토는 어떤 언어나 마찬가지로 로직, 알고리즘, 기본구조는 모두 부하와 동작전반을 감안하여 신중해야 한다는 점입니다.

잘 모르는 함수를 함부로 이용하지 않아야 합니다.
eregi 와 ereg, preg의 차이를 모른채 불필요한 부분에 eregi를 남발하는것은 가장 극단적인 예중 하나입니다.
eregi는 ereg보다 약 1.7배이상의 연산부하가 걸리게 됩니다.
eregi는 ereg와 유사하나 case insensitive (대소문자 무시기능)이 포함되어있어 c로 치면 strcmp와 stricmp 와 유사한 예라고 볼수있습니다. 두개의 펑션이 어떻게 짜여졌고 어떻게 틀린지는 헤더를 참고해보세요

그러나 한글의 casecomp 에는 대소문자가 없으므로 eregi를 한글에다 쓰는건 비효율적입니다.

2. MVC는 가장 최악의 경우를 불러일으킨다.

1) 파일의 인클루드는 모든 1개의 클라이언트 프로세스당 최대 2회로 제한합니다.
   파일을 자주 불러오는것은 이를테면 멍청한짓중 하나로 분류되죠.
   가장 멍청한짓중 하나가 looping phase내부에 include를 삽입하는 행위입니다.
   핵심 클래스 및 부가 클래스등의 인클루드 횟수를 2회 또한 최대한 적게 제한하는것이 좋습니다.
   소스 정리를 위해 파일을 나누는 깔끔을 떨지 말아야 합니다. vi에는 폴딩기능이 있습니다.

2) 만약 서브모듈단위의 뷰를 형성해야할 때에는 초도(첫접근의) 뷰측의 로더는 PHP가 될수 없습니다.
   웹서버등의 rewrite룰을 적당히 이용해야 합니다.
   PHP 는 만능이 아니라 단지 부하를 잡아먹는 소프트웨어일뿐입니다.
   1개의 PHP에 파일별로 모듈을 인클루드 하는것보다 rewrite를 통해 적재모듈을 선언하는것을 권장합니다.

3) 실시간 스킨은 최악의 결과를 불러온다.
   파일을 인클루드 하여 스킨으로 매핑하는 형식은 권장하지 않습니다.
   파일,메모리,CPU 모두 활용하는 최악의 테크트리로 불리우는데
   스키나이징을 하려면 포매터와 제네레이터로 나누어 3단계 절차를 거쳐 static php 를 생성하는것을 권장합니다.

3. 변수의 선언에는 신경을 써야한다.
php 는 mixed variable을 사용합니다. 즉 변수 선언자체가 타 언어보다 많은 리소스를 할애하게됩니다.
불필요한 변수는 선언하지 않고, 선언된 변수는 모조리 unset해주어야 합니다.
php는 메모리 리크가 심각한 언어라는것을 잊어서는 안됩니다.
(그러나 global을 unset하여서는 안됩니다)

4. 네임 쿼테이션은 최대한 ' 로 통일
$hey["stupid"] 보다는 $hey['thisone'] 이 빠릅니다. 최대한의 연산부하를 줄이는 형식을 유지합니다
수백개의 디피니션을 올리고 테스트해본 결과 약 0.0001초 차이정도 발생합니다만, 별것 아닐것 같지만 동시접속자가 만명이면 1초가량의 차이가 납니다. flops test를 하는 툴까지 올리질 못해보았네요 (사실 많이 귀찮았습니다)
과부하율이 겹쳐지면 그 차이는 10초이상이 벌어지게되며,  프로세스 단위에서 10초가 벌어지면 서버는 무응답 상태로 셧다운조차 할수없도록  중단되는 경우가 발생할 수 있습니다.
통상적으로 프로세스당 연산시간이 3초를 넘어서는 시점에서 연산처리능력의 한계가 온것으로 판단합니다.

물론 의도적인 시간제한도 필요합니다. 사용자의 정적방문 (1~2초이내 이동등)으로 통계나 사이트순위에 신경을 쓴다면 적당히 핸들링을 하는것도 필요하지만 순처리속도가 떨어지는것은 치명적 문제를 일으키게됩니다.

5. 펑션도 메모리다.
불필요한 펑션을 한파일에 넣고 돌리는것은 메모리 관리측면에서 좋지 않습니다.
클래스를 적절히 사용해야 합니다.
잦은 빈도의 펑션은 코어클래스로 묶고 나머지는 extras로 분리하여 "선언하지만 않으면" 적재되지 않습니다.
클래스 파일 또한 용량이 커져서는 안됩니다. 따라서 각 펑션은 극도로 간결하게 작성되어야 합니다.
기본적으로 펑션이 variable 에 해당하는걸 인지해야겠지요.
이러한 형식을 유지하는것은 zend 혹은 액셀러레이터를 통해 극도의 퍼포먼스를 유지하기 위함입니다.
통합 클래스는 이부분에 있어 많은 이점을 주게됩니다.

6. DB의 Select또한 부하다.
불필요한 열을 가져오지말아야 합니다.
그또한 fetch 이전에 어떤 형식의 API던지 다운로드 받아 메모리에 가지고 있게됩니다.
그것을 fetch 할때 쓰거나 result 시 리턴하는 형식으로 구성되어있습니다.
따라서 10MB 분량의 데이터를 select 해왔다면, 그 텍스트 데이터는 메모리에 적재된 상태라는것입니다.
또 이것을 free하지 않을 경우 나중에 메모리 리크가 발생합니다.

7. PHP를 신뢰하지마라!
현재까지 크게 문제가 되었던 3가지가 있습니다. 다음의 부분은 주의를 기울여야 합니다.
아래에는 현재의 버전까지 존재하는 치명적 버그들이며, 해결의 기미가 보이지않는 것 들입니다.

1) mysql handling function
  php 자체에 메모리 리크여파가 잔존할 경우 높은 순위로 맛이가는 기능중 하나입니다.
  fetch 기능인지 query 후 데이터를 받아오는부분인지 아직 정확한 파악은 하지 못했습니다. (귀찮습니다)
  그러나 mysql에서 오는 값은 모두 신뢰할 수 있는것이 아닙니다.
  파괴되거나 오류가 있는 값이 전송되는 경우가 종종 있으며. 이것은 가끔 치명적인 문제를 일으킵니다.

2) serialize, unserialize function
  이 또한 값을 왜곡시키는것이 증명되었습니다.  int type의 287382 이란 값이 string으로 바뀌어 2라는 값만 덜렁 들어오는 경우가 있었습니다. (정확히는 2^제어문자382 형식) 지금도 존재합니다.
  이것으로 인해 2년전 접속한 회원 모두가 운영자 관리권한을 획득하는등의 버그가 발생한적이 있습니다.
  이문제로 인해 serialize handler가 wddx나 php등 여러가지 핸들링 타입이 존재합니다.
  별도의 핸들러를 제작하여 운용하는것도 나쁘지 않을것 같습니다.

3) Exif 기능등..
불안정한 기능이 다수 있습니다. PHP의 모든 펑션은 반푼어치로 보면됩니다. 어느 언어의 클래스처럼 반인분이 짜도 겉으로 티안나게 도는 언어가 있지만, PHP는 반인분짜리가 짜면 바로 티가 나는게 문제입니다.
또 티가 빨리 나는편이랄까요?.
그중 일부 기능이 exif 같은 펑션입니다. 지원되며, 데이터를 잘 가져오는것 같습니다..
그러나 내부적으로는 많은 문제를 포함하고 있습니다.
일부 맥에서 편집된 프로그램중 바이트오더가 맞지 않는파일들 (인텔오더가 아닐경우) 또 특정 카메라 기종에 대해 인지하지 못하는 경우들 다양한 문제가 있는것이 밝혀졌습니다.

이제 iso와 친구가 될 시간이군요, 200장 정도의 단순노가다를 요하는 문서가 있습니다.
이 외에도 상당한 기능들이 생각보다 완성도가 매우 떨어지는 경우가 있습니다.

PHP가 가져오는, 동작하는 모든 펑션에 대해 신뢰를 하면안됩니다., 특별히 위처럼 문제 있는 펑션은 온값조차 재확인할 필요가 있습니다. (중요한 필드라면)


8. 캐시를 적당히 이용하라
xcache, eAccelerator, Zend Studio 등은 PHP를 캐시해주는 소프트웨어로 이외에도 몇가지가 있지만 안정성은 이 두가지가 압도적이라고 할수있습니다. 대응안으로는 APC가 있지만 현재로써는 메모리 리크점이 있다는것을 발견하였으며 (통상적 선언 재선언 해제구문으로 이루어진 로드테스트에서) 아직 이부분은 더 많은 테스트를 거듭하여야만 인정받을 수 있다고 보입니다.

위에도 설명했지만 캐시를 쓰면 파일i/o를 신경쓰지 않아도 되지 않느냐? 라고 생각할지 모르겠습니다만
캐시내에서도 include시 메모리i/o가 발생하는것은 사실입니다.
캐시는 파일을 옵티마이즈하여 메모리에 적재하므로 실행속도를 극도로 올려주는 기능입니다.
(옵코드를 생성하여 이를 별도의 캐시로 shm메모리 영역등에 적재하는 타입)

9. 프로파일링을 위해 시간을 아끼지 말라
xdebug 등의 프로파일링 툴을 이용하여 각 클래스별 메모리, i/o, 부하, 처리시간 등의 리소스를 프로파일하여
최적화된 코드를 만들 수 있도록 옵티마이징 작업을 해야합니다.

9. 서버의 파인튠을 위해 많은 시간을 할당하라
file i/o, memory , cpu 연산등 대부분의 언어들은 서버의 퍼포먼스에 상당히 기대게 됩니다
아무리 잘짠 언어가 동작을 하더라도, 비싼 기름 넣는다고 해도 엔진이 경운기면 소용없습니다.
서버의 파인튠을 찾아낸다면 기존에 비해 배수 이상의 성능을 끌어올릴수 있게됩니다.
가급적 이상적인 성능향상을 위해서는 불필요한 설정과 소프트웨어, 커널코드등을 삭제하는것을 권장합니다.
기본적으로 드라이버, 불필요한 기능은 컨피그에서 제외하고 또 불필요한 i/o 함수들 또한 삭제합니다. 이작업은 가끔 극단적인 결과를 초래하기도 하므로 전문가가 아닐 경우 추천하지는 않습니다.
최신 커널에까지 일부 ext3 나 i/o 처리부에 신규구현된 기능으로 인해 병목이 걸리는 부분이 존재합니다. 과도한 io가 발생하면 메모리를 일부 io 순환처리용 버퍼로 대응하는 부분에서 발생하는 문제점입니다.
disk io가 올라가는것이 아니라 메모리 io가 비정상적으로 상승하는데서 발생하는 문제입니다.

따라서 보통의 경우와 일반인은 절대 경험하기 힘들겠지만 미세하고도 자잘한 i/o로 극도의 퍼포먼스를 올리는 (PHP+캐시상태)에서는 이것은 가끔 치명적인 속도저하로 이어질 때가 있습니다.

윈도우 서버보다 리눅스 서버가 빠른점은 있습니다. 이와 마찬가지로 조금더 특화를 시킨다면 더 빠른 서버로 만들수가 있습니다. 서버는 잡다한 기능이 필요없습니다.
단지 서비스하는 한가지 기능에 특화된 시스템이 이상적입니다.

가장 간단히는 서버는 그래픽 편집을 할필요가 없습니다. 또 3d게임이 돌지 않아도 됩니다.
이러한 연계가 단하나라도 들어있는 윈도우와 리눅스는 퍼포먼스 면에서 차이가 발생하게됩니다.
또한 표준 리눅스에 비해 더 많은 기능을 삭제한 커스텀 리눅스는 대외적인 공격에서도 안정적이며, code exploit 도 통상적으로 적용되지 않습니다.
실제로 동일한 서버에서 이루어진 튜닝만으로 수배이상의 퍼포먼스를 보여준것도 사실입니다.
이부분은 엄격히 프로그래머가 아닌 SE에게 필요한 내용이 아닐까 싶습니다.


10. 이렇게 하면 안된다! 대표적인 배드코딩

1) 버전차이를 구분못하는 코딩
$HTTP_POST_VARS 등은 4.1대부터 deprecated 된 사전정의된 변수들입니다.
$_POST, $_COOKIE 등으로 모두 대체되었으며 5버전대부터는 완전히 deprecated 된것으로 알고있습니다.
따라서 4.1(이 나온지 몇년이 되었는데) 아직도 php 를 두고 3.0때의 코딩을 유지하는 사람들이 있습니다.
그러나 3.x때 제가 짯던 코드를 보면 아직도 $_POST 이런것이 존재하는것으로 보면..
메뉴얼은 오래전부터 업데이트되었던것으로 보입니다. PHP5 에서 종래 디파인을 삭제할 정도면 상당한 기간의 유예를 두었다고 보아야겠지요?

2) php에 퍼지 인공지능을 기대하는 프로그래머
$_POST[test] 형식으로 array를 사용하는 사람들이 일부 있습니다.
쿼테이션 미싱이라고 보아야겠지요.
test란 env를 define 할 경우 php 는 저게 test인지 string의 test인지를 분간하기 위해 여러번 간을 봅니다.
저런게 소스상에 천개가 있다면.. 제대로 쿼테이션한 코드보다 2천번은 더 삽질한다는 이야기입니다.
' 등을 싸지 않으면 문맥간 . 등이 있는지를 재점검하는등의 불필요한 동작이 한번 더있습니다.
정말 별것 아닌것 같지만 동시에 천명이 접속한다 생각해보십시요. 효율적이다 아니다를 따질때가 됩니다.
여기다 registers globals를 키게되면 금상첨화로 해킹에 취약점이 드러나기도 합니다.

3) Register globals 를 쓰는 코딩
한번 쓰기 시작하면 밑도 끝도 없는 삽질로 이어지게 됩니다.
이로인해 발생하는 보안 리스크는 엄청납니다. 만약 소스가 공개된다면 다양한 헛점으로부터 공격을 피하기 어렵게됩니다.
다른 어떤 언어에서 모든변수를 광역으로 지정하고 쓰는것을 보신적이 있으십니까?

4) 업로드한 파일에 대한 퍼미션을 건드리는 코딩
통상적으로 업로드한 파일의 퍼미션은 조정할 필요가 없습니다.
구태여 조절한다면 644 정도가 적당하겠지요. 그러나 여기다 실행권한주는 경우 내 서버에 대한 모든 실행권한을 위임하는것과 마찬가지입니다.

5) 데이터베이스 쿼리의 examination이 없는 경우
2~30개의 열을 가져오는데 수만개의 레코드를 뒤적이는 쿼리가 날라다니는 경우가 자주 있습니다.
이게 참 아이러니하게도 상용으로 판매하는 쇼핑몰 소스들이 이꼬라지더군요
몇군데 본 결과 한두군데가 아니었습니다.

6) 관리권한 페이지의 액세스 오류
php의 내장 web auth등의 여러가지 method를 통해 인증을 요구하거나 세션체크를 할 수 있습니다.
그러나 일부 쇼핑몰 소스들은 관리자 주요 페이지들만 달랑 인증을 걸어놓은채
include되는 파일들은 전혀 인증이 되어있지 않아 로그인없이도 매출자료가 열람가능한 경우가 있었습니다.
상용으로 꽤나 많이 팔아먹은것 같은데..
해당업체의 홍보자료에 의하면 쇼핑몰 솔루션을 구매한 약 300여개가 넘는 업체들은 지금 매출자료가 공개된거나 마찬가지인 상태가 되었습니다.
통합적인 파일을 인클루드하거나, 헤더에 넣거나, 체계적인 클래싱의 오토클래스 스타팅 선언등. 방법은 여러가지가 있었을텐데말입니다.
일부 쇼핑몰 소스는 관리자 페이지 뿐만아니라 주문/배송처리 쪽의 일부 페이지도 로그인없이 액세스가 되었습니다.
쇼핑몰에 있어서 파일이 업/다운로드 되는 유일한 페이지들인데 보안이 허술하면 서버가 뚤립니다.
저는 친절하게 버그 찾아다 밥떠먹여주는 사람이 아닙니다. 상용으로 파는건 파는사람들이 해결해야지요..
단지 보안결함이 있다고 경고만 할뿐입니다.

7) 기타 쿼테이션 오류들
mysql에 집어넣을 데이터스트링에.. 사용자로부터 받은 변수에다가 stripslashes를 먹이는 막장코딩이 있습니다.
이건 서버단에서 차단해도 소용없습니다.. 직빵입니다..
sql injection으로 가는 지름길이라고 할수있습니다. 하는게 아니라당하는겁니다..


마무리 ...
이 문서를 지킨다면 노가다가 매우 심해질수 있음을 사전에 알립니다.
이 문서는 빙빙 둘러 말하고 있지만 이제 구현되어가고 있는 클래스 사용의 권고 메시지가 포함되어있습니다.
무단 펌질은 사양합니다. 트랙백, 퍼말링크등은 관계없습니다. (공지사항참고)

업무적 컨택등이 있을때에는 이 페이지의 최소 가이드를 준수하여야 논의가 가능합니다.

크리에이티브 커먼즈 라이센스
Creative Commons License
이올린에 북마크하기(0) 이올린에 추천하기(0)

Posted by LeCieL

2008/01/22 04:12 2008/01/22 04:12
, , , , , , , , , , , ,
Response
No Trackback , 5 Comments
RSS :
http://cl.dgtalx.net/rss/response/142

Trackback URL : http://cl.dgtalx.net/trackback/142

« Previous : 1 : ... 47 : 48 : 49 : 50 : 51 : 52 : 53 : 54 : 55 : ... 189 : Next »

Archives

Calendar

«   2012/02   »
      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