Fogeaters, Light The World.

05

2017-Aug

[mysql] 성능 향상 전략

작성자: title: MoonBlonix IP ADRESS: *.64.228.3 조회 수: 1469

출처 :: http://blog.bloodcat.com/186

*MySQL 쓰면서 하지 말아야 할 것 17가지*

권장사항이다. 이것을 이해하면 당신의 어플리케이션이 더 나은 성능을 발휘할 것이다.

다만 이것이 사람의 실력을 판단하는 척도로 사용되서는 안 될 것이다.

 

작게 생각하기

- 조만간 규모가 커질거라면 MySQL ecosystem을 봐야된다.
- 그리고 캐싱 빡시게 안 하는 메이저 웹사이트는 없다.
- develooper.com의 Hansen PT랑 Ilia 튜토리얼 볼 것
- 처음부터 확장 가능하게 아키텍처 잘 쪼개놔야된다.
- 복제랑 파티셔닝 어떻게 할지 미리 계획 세워놔라.
- 파일 기반 세션 좀 쓰지마 -_-
- 그렇다고 너무 쓸데없이 크게 생각하지도 말 것
- 특히 성능하고 확장성 구분 못 하면 난감함

 

EXPLAIN 안 써보기

- SELECT 앞에 EXPLAIN 이라고 붙이기만 하면 되는 것을 (..)
- 실행 계획 확인
- 타입 컬럼에 index 써있는거랑 Extra 컬럼에 index 써있는거랑 “매우 큰” 차이 있음
* 타입에 있으면 Full 인덱스 스캔 (안 좋다.)
* Extra 컬럼에 있으면 Covering 인덱스 찾았다는 의미임 (좋다!)
- 5.0 이후부터는 index_merge 최적화도 한다.

 

잘못된 데이터 타입 선택

- 한 메모리 블럭 단위에 인덱스 레코드가 많이 들어갈수록 쿼리가 빨리 실행될 것이다. (중요)
- 아.. 정규화 좀 해 -_-… (이거 정말 충격과 공포인 듯)
- 가장 작은 데이터 타입을 써.. (진짜 BIGINT가 필요하냐고..)
- 인덱스 걸리는 필드는 정말 최소한으로 데이터 크기를 써야된다고.
- IP는 INT UNSIGNED로 저장해!! (아주 공감)
* 이럴 때 쓰라고 INET_ATON 함수가 아예 내장되어 있음.

 

PHP에서 pconnect 쓰는 짓

- 아파치에서 좀비 프로세스라도 생기면 그 커넥션은 그냥 증발하는거야..
- 어차피 MySQL 접속 속도는 Oracle이나 PostgreSQL 보다 10~100배 빠르다고.

너무 과도한 DB 추상화 계층을 두는 것
- 어디 포팅 열심히 할 거 아니면 추상화 계층 쓰지마 (ADODB, MDB2, PearDB 등)
- scale out 가능한걸 쓰라고.

 

스토리지 엔진 이해 못 하는 것

- 단일 엔진만으로 전체 아키텍처를 결정했다면 대부분 최적이 아님
- 엔진 별 장단점을 공부할 것
- ARCHIVE : zlib으로 압축해주고 UPDATE 안 되고 로그 Bulk Insert에 유용함.
- MEMORY : 서버 재시작하면 증발. 인덱스가 HASH나 BTREE로 가능함. 임시, 요약 데이터에 사용.
* 주간 top X 테이블 같은 것.
* 하여튼 메모리에 박아넣고 싶은 데이터 있으면..

 

인덱스 레이아웃 이해 못 하는 것

- 제대로 인덱스랑 스토리지 엔진 선택하려면 공부 좀 해
- 엔진은 데이터와 인덱스 레코드를 메모리나 디스크에 레이아웃하는 걸 구현한 것
- clustered 구성은 데이터를 PK 순서에 따라 저장함.
- non-clustered 구성은 인덱스만 순서대로 저장하고 데이터는 순서 가정하지 않음.
- clustered에서는 인덱스만 타면 추가적인 조회 없이 바로 데이터 가져오는 것임.
- 그래서 clustered PK는 작은 놈으로 할 필요가 있다는거
* 다른 인덱스는 각 레코드마다 PK를 앞에 더 붙이게 되니까.
* PK 지정 안 하면 아무렇게나 해버림

 

쿼리 캐시 이해 못 하는 것

- 어플리케이션 read/write 비율은 알고 있어야지
- 쿼리 캐시 설계는 CPU 사용과 읽기 성능 간의 타협
- 쿼리 캐시 크기를 늘린다고 읽기 성능이 좋아지는게 아님. heavy read라도 마찬가지.
- 과도한 CPU 사용을 막기 위해 무효화 할 때는 캐시 항목들을 뭉텅이로 날려버림
- 한마디로 SELECT가 참조하는 테이블 데이터 하나라도 변경되면 그 테이블 캐시는 다 날라간다는 얘기임
- 수직 테이블 파티셔닝으로 처방
* Product와 ProductCount를 쪼갠다든지..
* 자주 변하는 것과 변하지 않는 것을 쪼개는게 중요하다 이 말임.

 

Stored Procedure를 쓰는 것

- 무조건 쓰면 안 된다는게 아니고..
- 컴파일 할 때 무슨 일이 일어나는지 이해 못 하고 쓰면 재앙이 된다 이 말.
- 다른 RDBMS랑 다르게 connection thread에서 실행 계획이 세워짐.
- 이게 뭔 얘기냐 하면 데이터 한 번 가져오고 연결 끊으면 그냥 CPU 낭비 (7~8% 정도)하는 꼴이라는 것.
- 웬만하면 Prepared 구문과 Dynamic SQL을 써라.. 아래 경우를 제외하고
* ETL 타입 프로시저
* 아주아주 복잡하지만 자주 실행되지는 않는 것
* 한 번 요청할 때마다 여러번 실행되는 간단한 것 (연결한 상태로 여러번 써야 된다니까)

 

인덱스 컬럼에 함수 쓰는 것

- 함수에 인덱스 컬럼 넣어 호출하면 당연히 인덱스 못 탄다
- 함수를 먼저 계산해서 상수로 만든 다음에 = 로 연결해야 인덱스 탈 수 있다.
* 여기 실행 계획 보면 LIKE도 range type 인덱스 타는 것 보임

 

인덱스 빼먹거나 쓸모없는 인덱스 만들어 놓는 것

- 인덱스 분포도(selectivity)가 허접하면 안 쓴다.
- S = d/n
* d = 서로 다른 값의 수 (# of distinct values)
* n = 테이블의 전체 레코드 수
- 쓸모없는 인덱스는 INSERT/UPDATE/DELETE를 느리게 할 뿐..
- FK는 무조건 인덱스 걸어라. (물론 FK 제약 걸면 인덱스 자동으로 생긴다.)
- WHERE나 GROUP BY 표현식에서 쓰이는 컬럼은 인덱스 추가를 고려할 것
- covering index 사용을 고려할 것
- 인덱스 컬럼 순서에 유의할 것!

 

join 안 쓰는 짓

- 서브쿼리는 join으로 재작성해라
- 커서 제거해라
- 좋은 Mysql 성능을 내려면 기본
- 집합 기반으로 생각해야지 루프 돌리는거 생각하면 안 된다.

 

Deep Scan 고려하지 않는 것

- 검색엔진 크러울러가 쓸고 지나갈 수 있다.
- 이 경우 계속해서 전체 집합을 정렬한 다음 LIMIT로 가져와야 하니 무진장 느려진다.
- 어떻게든 집합을 작게 줄인 다음 거기서 LIMIT 걸어 가져올 것

 

InnoDB 테이블에서 WHERE 조건절 없이 SELECT COUNT(*) 하는 짓

- InnoDB 테이블에서는 조건절 없이 COUNT(*) 하는게 느리다.
- 각 레코드의 transaction isolation을 유지하는 MVCC 구현이 복잡해서 그렇다는..
- 트리거 걸어서 메모리 스토리지 엔진 쓰는 테이블에 통계를 별도로 유지하면 된다.

 

프로파일링이나 벤치마킹 안 하는 것

- 프로파일링 : 병목 찾아내기
- 벤치마킹 : 시간에 따른 성능 변화 추이 평가, 부하 견딜 수 있는지 테스트
- 프로파일링 할 때는 실제 데이터를 옮겨와서 할 것
- 어디가 병목이냐~ Memory? Disk I/O? CPU? Network I/O? OS?
- 느린 쿼리 로그로 남기기
* log_slow_queries=/path/to/log
* log_queries_not_using_indexes
- 벤치마킹 시에는 다 고정시키고 변수 하나만 바꿔가면서 해야 함. (쿼리 캐시는 끌 것.)
- 도구를 써라~~
* EXPLAIN
* SHOW PROFILE
* MyTop/innotop
* mysqlslap
* MyBench
* ApacheBench (ab)
* super-smack
* SysBench
* JMeter/Ant
* Slow Query Log

 

AUTO_INCREMENT 안 쓰는 것

- PK를 AUTO_INCREMENT로 쓰는건 무진장 최적화 되어 있음
* 고속 병행 INSERT 가능
* 잠금 안 걸리고 읽으면서 계속 할 수 있다는!
- 새 레코드를 근처에 놓음으로써 디스크와 페이지 단편화를 줄임
- 메모리와 디스크에 핫 스팟을 생성하고 스와핑을 줄임

 

ON DUPLICATE KEY UPDATE를 안 쓰는 것

- 레코드가 있으면 업데이트하고 없으면 인서트하고 이런 코드 필요없다!! 다 날려버려라!!
- 서버에 불필요하게 왔다갔다 할 필요가 없어짐
- 5-6% 정도 빠름
- 데이터 입력이 많다면 더 커질 수 있음
하지 말아야 할 것 총정리
Thinking too small
Not using EXPLAIN
Choosing the wrong data types
Using persistent connections in PHP
Using a heavy DB abstraction layer
Not understanding storage engines
Not understanding index layouts
Not understanding how the query cache works
Using stored procedures improperly
Operating on an indexed column with a function
Having missing or useless indexes
Not being a join-fu master
Not accounting for deep scans
Doing SELECT COUNT(*) without WHERE on an InnoDB table
Not profiling or benchmarking
Not using AUTO_INCREMENT
Not using ON DUPLICATE KEY UPDATEK

profile
profile

title: MoonBlonix

2017.08.05 16:28
*.64.228.3

출처 :: http://blog.bloodcat.com/186


★ mysql 총 게시물 수 세기

<?php 
    $numresults=mysql_query("select code from $board"); 
    $numrows=mysql_num_rows($numresults); 
    //2초 이상 ...게시물 10만개 
?> 
VS 
<?php 
    $numresults=mysql_query("select count(code) as code from $board"); 
    $row_num=mysql_fetch_array($numresults); 
    $numrows=$row_num[code]; 
// 0.2  ...게시물 10만개 
?> 
아래것이 훠얼씬 빠름다.. 
도대체 mysql_num_row() 는 왜 만들어 놓은 것일까용??? 

★ mysql_fetch_row  > mysql_fetch_array >>> mysql_result 입니다.

row 가 array 보다 약간 빠르고, result 보다는 훠얼씬 빠름당 ^^ 
row 는 불편한게 숫자로 칼럼을 불러와서 불편합니다. 
array 가 약간 느리지만, 문자 칼럼을 불러 올 수 있으니 array 쓰는 것도 양호 ^^ 
아직도 mysql_result() 를 사용하시나요? 


★print , echo ,printf 함수 속도 비교

HTML 출력 >> echo > print >> printf

print 와 printf 함수는 복잡한곳에 적격이고, 약간 느리다. 
echo 는 단순한곳에 적격이고...빠르다. 
printf 는 형식화된 출력을 해주므로 그래도 좋죠 ^^ 

★  인라인

a.php 
<?php 
    $aa="sdsdsdsd"; 
    echo (" 
      <table> 
      <td> $aa </td> 
      </table> 
      "); 
?> 
위 랑..

b.php 
<?php $aa="sdsdsdsd"; ?> 
<table> 
<td><?=$aa;?></td> 
</table>

랑 도대체 어케 코딩 하느냐죠 ? 
솔직히 코딩은 a.php 가 편합니다. 
<?php ?> 를 한번 만 쓰니까 효율적일 수 있습니당.. 
그러나 아무리 아무리 PHP가 빠르다고 해도 HTML 이 더 빠릅니당.. !!! 
쿠쿠쿠 
b.php 처럼 코딩 하는 습관을 가지세용 ^^ 
변수가 많아 질 경우 b.php 파일이 느립니다. 
그러나 소스 코드 분리 측면에서는 b.php 파일 형태가 좋습니다. 
이 부분은 여러분들이나 저나 각종소스를 가지고 연구해 봐야만 할검당 ^^ 

★ zend cache , APC ,Bware 캐쉬 비교

Zend cache > APC > Bware 
zend cache 는 캐쉬 입니다. 울나라에서 포탈에서도 몇군데 사용하죠. 
PHP 프로그램 변환 없이 속도를 빠르게 해 줍니다. ^^ 
zend cache 를 살 돈이 없으시다구용? 
그러면 APC 나 zend optimizer 를 반드쉬 설치 하세용 !! 
■ 그런디 Zend Cache 만한 무료 캐쉬가 나왔다고 합니당...^^ 
http://www.php-accelerator.co.uk/index.php 
도대체 캐쉬의 작동 원리는 어케 되는가? 
캐쉬는 여러가지로 작동 할 수 있습니다. 
가장 많이 아는 것이 메모리에 페이지를 띄워 놓고 읽어 오는 것입니다. 
하드에서 읽는 것 보다 빠르기 때문이죠. 
그리고 HTML 로 만드는 것도 캐쉬 입니다.조금 더 빠르다면 캐쉬 라고 할 수 있습니다. 
게시판 목록 보기 페이지는 디비 연결이 많습니다. 쓰기 보다 , 읽기가 20배 정도 많습니다. 
게시판을 마니 운영해 봤다면 아시겟죠 ^^? 
그래서 게시판 목록을 HTML 로 저장 시키고, 쓰기, 삭제 가 있을때 마다 HTML 로 만들어 주는 것 입니다. 
그러면 디비 부하는 상당히 줄어 듭니다. 
또는 코딩을 할때 enter 이나 tab 키를 많이 사용해서 합니다. 
저번에 본 캐쉬는 enter 이나 tab 키를 없에 주는 캐쉬 였습니다. 
enter 이나 tab 를 없엘 경우 최대 1-2k 정도 절약이 되죵 ^^ 

★ ereg_replace <<<  preg_replace 정규표현식

ereg_replace () 가 장난 아니게 느림다. 
40개 정도 변환 하는데 1초나 걸려요.preg_replace 는 0.3초 정도요. 
되도록이면 preg_replace() 를 사용하세요 !!! 
www.php.net/preg_replace 
tood.net preg_replace 강좌 
http://www.tood.net/tood/toodboard/toodread.php?board=tootech&tcode=907 

★ foreach , list 함수 속도 차이 35%

foreach($string as $a);  >>>  while(list(,$a) = each($string)); 
foreach() 를 사용하는 것이 35% 정도 빠릅니다...!! 

★ explode() Vs split() Vs preg_split

explode() 와  split() 와 preg_split 는 문자열을 자르는 함수 입니다. 
테스뚜 해보시면 아시겟지만 explode() 가 훨 빠릅니당..!!! 
70% 정도 빠릅니다. 


★ 큰따옴표(") , 작은 따옴표(')

큰따옴표는 PHP가 파싱을 합니다. 그러나 작은 따옴표는 파싱을 하지 않습니다. 
작은 따옴표를 사용하시는 것이 빠릅니다. 
코딩시 echo ' test '.$aaa.' tood '; 
요런식으로 하시는 게 가장 빠름당 !! 

★ mysql 데이터 저장 공간 크기 ?

id int(11) unsigned NOT NULL auto_increment, 
   bbs smallint(5) unsigned NOT NULL, 
   lens mediumint(8) unsigned NOT NULL, 
   
int 도 무자게 많슴당.. 
각각의 크기에 맞게 하는 것이 속도가 빨라 집니당 ^^ 
not null 를 주는 것도 속도가 빨라 집니당.. 
작은 사이트 조회수 경우 1만을 넘기도 힘듭니다. 그럴 경우 smallint 를 사용하시면 됩니다. 

★ where 절에 모든 것은 인덱스를 걸어라 !!!

mysql 에서 쿼리시에 where 절에 사용되는 비교의 칼럼은 반드쉬 인덱스를 거세용 !!! 
mysql 인덱스 가 여러 분들의 게시판 속도를 업 시켜 줍니당 !! 

★메인페이지에서 속도를 빠르게 하려면, .htm 으로 만드는 것이 좋습니다.

그러니까..제 사이트 페이지 오른쪽 에는 모두 최신 게시물로 디비를 불러오는 부분 
입니다. 
디비 연결은 부하를 줄 수 있습니다. 
그러니까..이예는 사이트가 상당히 활성화된 사이트일 경우 입니다. 
하루에 2번 정도 .htm 으로 만들어 페이지를 업시키는 것이 좋습니다. 
아무리 PHP가 빨라도 HTML 보다는 빠르지 않습니다.. !!! 
HTML 로 만드는 것은 생각 할것이 게시판 목록수 만큼 파일이 생성이 됩니다. 1만개 게시물이면 1만개 HTML 
이 생기죠. 그런데 조회수가 보통 1000-3000 을 넘는 게시판 경우는 효율성이 중대 됩니다. 
그러나 조회수가 100 비스므리 하다면...생각해봐야 합니다. 
그러기 위해서는 ★cron 를 알아야 합니다 PHP강좌 게시판에서 cron 으로 검색하세용 !! 
윈도우 사용자는 멀 쓰는지 잘 모르겠슴당...아시면 답글 부탁  ^^ 


★ 페이지 네비게이션- 페이지 분활

페이지 분활도 속도 차이가 납니다. 각각의 페이지 분활을 테스트 해보시면 아시겟지만, 
10만개,20만개의 데이터를 넣어 보세요. 빠른것은 0.03초 정도, 느린것은 3초나 걸립니다. 
인덱스를 걸어서 빨리 뜨는데, 머가 늦는 걸까 했는데, 페이지 네비게이션 알고리즘이 문제더군요. ^^ 

★ 속도 테스트는 어케 하는가 ? microtime() 사용하면 됩니다.

<?php 
function pageTime($page) { 
$mstart = explode(" ", $m); 
$mend = explode(" ", microtime()); 
$mtime = ($mend[1] - $mstart[1]) + ($mend[0] - $mstart[0]); 
echo " $m[1]  $mend[1]<P>"; 
echo " $m[0]  $mend[0]<P>"; 
print("tood.net 페이지 로딩 시간 : " . $mtime . " microseconds"); 

pageTime(basename($PHP_SELF)); 
?>


조금 더 빠른 쿼리

$sql = "SELECT table.column FROM table WHERE criteria LIKE $myrow[variable]"; 
아래 처럼 하면 파싱 되지 않기 때문에 아주 아주 쪼금 빨라 짐다..0.0000x 초; 
$sql = 'SELECT table.column FROM table WHERE criteria LIKE ' . $myrow['variable']; 

profile

title: MoonBlonix

2017.08.05 16:31
*.64.228.3

Beginner : echo "$var";   
Experienced : echo $var;  


Beginner:echo "<a href=\"http://www.php.net\">PHP</a>";  ; 
Experienced : ?><a href="http://www.php.net">PHP</a> color="#0000CC"><?  ... ?>   



<?php if($asdf){ ?> 여기에 html 조건 출력하는 이런것도 됨. 굳이 echo 길게 쓸 것 없음 <?php } ?>

profile

title: MoonBlonix

2017.08.05 17:22
*.64.228.3

http://dev.mysql.com/doc/refman/5.5/en/optimization.html

profile

title: MoonBlonix

2017.10.12 23:13
*.64.228.3

mysqli_fetch_array 대신 mysqli_fetch_assoc 사용하기.

뭐가 다른지는 print_r 로 출력해볼 것.

List of Articles
번호 제목 글쓴이 날짜 조회 수
공지 [Web] 클라우드 IDE + 2 title: MoonBlonix 2017-06-25 15128
112 [jQuery] Ajax 옵션 설명 title: MoonBlonix 2017-09-15 1441
111 [jQuery] 페이지 부분 새로고침 + 1 title: MoonBlonix 2017-09-13 1128
110 정규표현식 분석 + 1 title: MoonBlonix 2017-09-12 1090
109 [php/mysql] 모든 uft-8 한글 인코딩 문제 title: MoonBlonix 2017-09-12 1498
108 [php] AES / mcrypt 양방향 대칭키 암호화/복호화 + 3 title: MoonBlonix 2017-08-28 1241
107 [php/jQuery/Ajax] 파일 업로드 구현 title: MoonBlonix 2017-08-28 1463
106 [jQuery] ajax 사용 기초 title: MoonBlonix 2017-08-19 1462
105 [php] 서버 용량 구하기 title: MoonBlonix 2017-08-15 1354
104 [jQuery] 레이어 팝업 title: MoonBlonix 2017-08-15 1490
103 [php/jQuery] 선택된 다수의 체크박스 값 넘기기 / 체크박스 제어 + 2 title: MoonBlonix 2017-08-15 1476
102 [mysql] 저장 프로시저 / 저장 함수 / 트리거 title: MoonBlonix 2017-08-15 1511
101 [mysql] 저장엔진 title: MoonBlonix 2017-08-15 1473
100 [mssql / mysql] sql 트랜잭션(Transaction) + 1 title: MoonBlonix 2017-08-14 1508
99 [mysql] mysqli_fetch_row() mysqli_fetch_assoc() mysqli_fetch_array() 차이 title: MoonBlonix 2017-08-08 1484
98 [javascript/jQuery] Ajax의 흐름과 예제 title: MoonBlonix 2017-08-05 1540
97 [web] html 게시글 에디터 title: MoonBlonix 2017-08-05 1459
96 [mysql] JOIN 쿼리 사용하기 title: MoonBlonix 2017-08-05 1459
95 [mysql] 인덱스 (Index) 사용 방법 및 설명 + 2 title: MoonBlonix 2017-08-05 1717
94 [mysql] TEXT 타입, INT 타입 title: MoonBlonix 2017-08-05 1517
» [mysql] 성능 향상 전략 + 4 title: MoonBlonix 2017-08-05 1469