20130613

최적의 성능을 위한 인덱스의 선택

인덱스를 선택하는 것은 많은 SQL Server DBA와 개발자에게 신비로운 대상이다. 물론 우리는 인덱스가 어떤 일을 하며 어떻게 성능을 향상시키는지를 잘 알고 있다. 문제는 이상적인 인덱스 종류와(클러스터드 vs. 넌클러스터드), 인덱스에 포함시킬 컬럼의 개수(다중 컬럼 인덱스가 필요한지 여부), 그리고 어떤 컬럼에 인덱스를 만들 것이냐 하는 점이다.

여 기서는 이 질문에 대한 답변을 간략하게 제공하고자 한다. 불행하게도, 각각의 경우에 대한 절대적인 해답은 존재하지 않는다. 다른 성능 튜닝 및 최적화와 마찬가지로 이상적인 인덱스를 찾으려면 직접 테스트하는 수고를 감수해야 한다. 이제 인덱스 생성에 있어서의 보편적인 지침을 먼저 살펴보고 다음에는 클러스터드 인덱스와 넌클러스터드 인덱스를 선택하는 기준에 대해서 자세히 알아보도록 하겠다.


인덱스가 너무 많아서 문제가 될 수도 있는가?

정 답은 “그렇다” 이다. 어떤 사람들은 모든 컬럼에 인덱스를 걸면 성능 문제가 모두 해결될 것이라고 생각하기도 하는데 실제로는 전혀 그렇지 않다. 인덱스가 데이터 액세스 속도를 증가시키는 것과 마찬가지로 잘못 선택하면 오히려 액세스 속도를 저하시킬 수도 있다. 인덱스가 많음으로 해서 발생하는 문제는 INSERT, UPDATE, DELETE가 발생할 때 마다 SQL Server가 인덱스를 유지하기 위한 작업을 해야만 한다는 것이다. 하나의 테이블에 한 두개 정도의 인덱스를 관리하는 것은 SQL Server가 처리하기에 별 부담이 없지만 네 개, 다섯 개 혹은 그 이상의 인덱스를 만들게 되면 성능에 큰 장애 요인으로 작용하게 된다. 가능하다면 인덱스를 적게 만드는 것이 좋으며 최적의 성능을 얻기 위해서 적절한 개수의 인덱스를 만드는 것이 요구된다.

단 지 인덱스를 추가하는 것이 좋을 것 같다는 생각만으로 무조건 인덱스를 만드는 것은 좋지 않다. 해당 테이블을 이용하는 쿼리에서 사용될 것이 확실한 경우에만 인덱스를 추가해야 한다. 어떤 쿼리가 실행될지 알 수 없다면 확실히 알기 전까지는 아무 인덱스도 만들지 않는 편이 낫다. 어떤 쿼리가 실행될지 추측하고, 거기에 따라서 인덱스를 생성하면 결국 애초의 추측이 틀렸음을 알게 되는 경우가 많기 때문이다. 실행될 쿼리의 유형을 먼저 알아야 하고 그 다음에 가장 적절한 인덱스를 만들기 위한 분석 과정을 거친 후에야 비로소 인덱스를 생성하고 실제로 효과가 있는지 테스트 해야 한다.
OLTP 프로그램은 INSERT, UPDATE, DELETE 작업이 매우 많기 때문에 최적의 인덱스를 선택하기 위한 판단을 내리기가 쉽지 않다. SELECT, UPDATE, DELETE 할 레코드를 신속하게 찾기 위해서는 인덱스가 필요하지만 인덱스가 너무 많으면 INSERT, UPDATE, DELETE 작업이 실행될 때 과도한 오버헤드가 발생하게 된다. 한편, 주로 읽기 작업이 많은 OLAP 프로그램의 경우에는 INSERT, UPDATE, DELETE 작업에 대해서 걱정할 필요가 없기 때문에 필요한 만큼 인덱스를 만들어도 상관 없다. 이와 같이 인덱스 전략을 수립할 때에는 응용프로그램이 어떻게 사용될 것인가가 큰 차이를 가져오게 된다.

인덱스를 선택할 때 고려해야 할 사항을 하나 더 들자면 선택한 인덱스가 SQL Server 쿼리 옵티마이저에 의해서 사용되지 않을 수도 있다는 점이다. 쿼 리 옵티마이저가 인덱스를 사용하지 않기로 결정하였다면 인덱스는 SQL Server에게 부담이 될 뿐이기 때문에 아예 삭제하는 편이 낫다. 그렇다면 SQL Server 쿼리 옵티마이저에서 인덱스가 존재함에도 항상 인덱스를 사용하지는 않는 이유는 무엇일까?
여기서 자세하게 답변하기에는 너무 광범위한 내용이지만 한마디로 요약하자면 SQL Server가 인덱스를 사용하는 것 보다 테이블 검색을 수행하는 편이 더 빠르다고 판단하는 경우가 있기 때문이라고 할 수 있다. 여기에는 두 가지 경우가 있는데 하나는 테이블의 크기가 작은 경우이고(레코드 수가 적음) 또 다른 하나는 인덱스가 걸린 컬럼의 유일한 값이 95%보다 적기 때문이다. SQL Server가 인덱스를 사용하지 않을지를 어떻게 알 수 있을까? 이에 대한 답변은 SQL Server 쿼리 애널라이저를 사용하는 방법을 살펴본 다음에 알아보기로 하겠다.
클러스터드 인덱스를 선택하는 방법하나의 테이블에는 하나의 클러스터드 인덱스만을 만들 수 있기 때문에 어떻게 사용할 것인가를 신중하게 생각해야 한다. 실행할 쿼리의 유형을 고려해 보고 어떤 쿼리가 가장 중요하며 클러스터드 인덱스를 사용했을 때 도움이 될지를 추정해 보도록 한다.

일반적으로는 클러스터드 인덱스를 만들 컬럼을 선택할 때에는 다음과 같은 기준을 사용한다.

  • 테이블의 프라이머리 키를 항상 클러스터드 인덱스로 만들어서는 안 된다. 프라이머리 키를 만든 다음에 특별히 지정을 하지 않으면 SQL Server는 자동으로 프라이머리 키를 클러스터드 인덱스로 만든다. 프라이머리 키가 다음과 같은 조건에 부합될 경우에만 클러스터드 인덱스로 만들도록 한다.
  • 클러스터드 인덱스는 일정한 범위의 값에 대해서 쿼리를 실행하거나 정렬된 결과를 필요로 할 ‘때 가장 좋은 방법이다. 왜냐하면 데이터가 이미 인덱스 내에서 정렬되어 있기 때문이다. 예를 들면 쿼리에서 BETWEEN, <, >, GROUP BY, ORDER BY, 그리고 MAX, MIN, COUNT와 같은 집합 함수를 사용하는 경우가 여기에 해당된다.
  • 클러스터드 인덱스는 유일한 값을 가지고 레코드를 검색하는 쿼리를 사용하거나(예를 들면 직원 번호) 레코드의 거의 모든 데이터를 가져오고자 할 때 효과적이다. 왜냐하면 이 쿼리는 인덱스만으로 처리가 가능하기 때문이다.
  • 클러스터드 인덱스는 국가 또는 주(state) 데이터와 같이 유일한 값이 제한적인 컬럼을 액세스 하는 쿼리에 효과적이다. 그러나 컬럼의 데이터가 예/아니오, 남성/여성 처럼 매우 적은 유일한 값을 갖고 있으면 이 컬럼에는 인덱스를 걸지 않는 것이 좋다.
  • 클러스터드 인덱스는 JOIN 또는 GROUP BY 절을 사용하는 쿼리에 효과적이다.
  • 클러스터드 인덱스는 매우 많은 레코드를 반환하는 쿼리를 사용할 때 효과적이다. 왜냐하면 데이터가 인덱스에 모두 들어 있으므로 다른 곳에서 찾아올 필요가 없기 때문이다.
  • 테이블에 INSERT 작업이 많다면 아이디, 날짜 등과 같이 증가하는 컬럼에는 클러스터드 인덱스를 만들지 않아야 한다. 왜냐하면 클러스터드 인덱스는 데이터를 물리적으로 정렬되도록 유지하기 때문에 증가하는 컬럼에 생성된 클러스터드 인덱스는 새로운 데이터를 테이블의 동일한 페이지에 삽입하여 테이블이 과도하게 사용됨으로써 I/O 병목현상을 유발할 수 있다. 이 때에는 클러스터드 인덱스로 사용할 다른 컬럼을 찾아보는 것이 좋다.

한 가지 난처한 문제는 클러스터드 인덱스를 만들 필요가 있는 컬럼이 하나 이상인 경우가 있다는 것이다. 그러나 하나의 테이블에는 오직 하나의 클러스터드 인덱스만을 생성할 수 있기 때문에 모든 가능성을 평가한 다음에 가장 효과적일 것으로 판단되는 하나만을 선택해야 한다.


넌클러스터드 인덱스를 선택하는 방법

넌클러스터드 인덱스는 테이블에 원하는 만큼 생성할 수 있기 때문에 클러스터드 인덱스를 선택하는 것보다 비교적 쉽다. 다음은 넌클러스터드 인덱스를 생성할 컬럼을 선택하는 방법이다.
  • 넌클러스터드 인덱스는 반환되는 레코드의 개수가 적은 쿼리(하나의 레코드 포함)와 인덱스의 선택도가 높은 경우에(95% 이상) 사용하면 좋다.
  • 컬럼의 데이터가 95% 이상의 유일한 값을 갖지 않는다면 SQL Server 쿼리 옵티마이저는 해당 컬럼에 생성되어 있는 넌클러스터드 인덱스를 사용하지 않을 가능성이 높다. 따라서 적어도 95% 이상 유일한 값을 갖지 않는 컬럼에는 넌클러스터드 인덱스를 만들지 않는 것이 좋다. 예를 들어 “예”, “아니오”를 데이터로 갖는 컬럼은 95% 이상 유일해야 한다는 조건에 부합되지 않는다.
  • 인덱스의 폭(width)을 가능한 좁게 유지한다. 특히 복합(다중 컬럼) 인덱스를 만들 때는 더욱 그러하다. 이렇게 하면 인덱스의 크기가 감소하기 때문에 인덱스를 읽을 때 필요한 읽기 횟수도 줄어들므로 성능 향상을 얻을 수 있다.
  • 가능하다면 문자보다는 정수 값을 가진 컬럼에 인덱스를 만들어야 한다. 정수 값은 문자보다 처리하는데 필요한 오버헤드가 작다.
  • 응용프로그램이 같은 테이블에 대해서 동일한 쿼리를 반복해서 실행한다면 해당 테이블에 커버링 인덱스를 만들어 보도록 한다. 커버링 인덱스는 쿼리에서 사용하는 모든 컬럼을 포괄하는 인덱스이다. 인 덱스에 찾고자 하는 데이터가 모두 들어 있기 때문에 SQL Server는 데이터를 찾기 위해서 테이블을 참조할 필요가 없으며 결과적으로 논리적 및 물리적 I/O가 감소하는 장점이 있다. 그러나 인덱스가 과도하게 커지면(컬럼이 너무 많아서) I/O가 증가하여 성능이 떨어질 수도 있다.
  • 인덱스는 쿼리의 WHERE 절이 인덱스의 가장 왼쪽의 컬럼과 일치할 때에만 사용된다. 따라서 “City, State”와 같은 복합 인덱스를 만들었다면 WHERE City=’Houston’과 같은 쿼리는 인덱스를 사용하겠지만 WHERE STATE=’TX’는 인덱스를 사용하지 않는다.

일반적으로 테이블에 하나의 인덱스만 필요하다면 클러스터드 인덱스로 만드는 것이 좋다. 그러나 테이블에 하나 이상의 인덱스가 필요한 경우에는 넌클러스터드 인덱스를 만들 수 밖에 없다. 위의 내용을 충실하게 따른다면 최적의 인덱스를 선택할 수 있을 것이다.




원문 : http://blog.naver.com/webman21/17890936

mysql replication 을 활용한 분산처리

아.. 솔직히 돈있으면 이런 고민하지 말고 클러스터링 쓰자... 그게 답이다.


돈 없어서 이렇게 쓰는 건데... 영 힘들다...


원리는 이렇다.


mysql master 와 slave 여러개 로 구축을 하고....

php에서 memcache 서버에 현 접속 디비 slave를 기록해두고 순차적으로 slave를 갈아타는 아주 아주 단순한 방법이다.

insert/update/delete 등 데이터 조작이 일어날 경우에만 master로 연결하고 나머지 경우에는 slave로만 가는 형태이다.


이렇게 서버가 있다고 가정하다.

php-fpm


memcached
 mysql master

mysql slave 1

mysql slave 2...


php-fpm에서는 mysql master의 접속이 아닌 경우 데이터베이스 접근 전에 memcached 에다가 바로 전에 slave 몇 번 서버에 접근했는지 정보를 가져와서 순차적으로 다음번 slave 접속해서 질의하는 형태이다.


이 경우 좃되는 경우가 하나 있다.

동기화의 문제인데... 그건 검증 로직을 활용하여 프로시저를 만들어 관리해야 하고

문제가 있다고 판단이 될 경우 해당 slave 서버를 서비스 중단을 해야 한다.


이렇게 감시관리하는 데몬을 뛰워 분산처리를 시도하였다.


뭐 그런대로는 쓸만하긴 하나... 돈 있으면 오라클 쓰자..ㅡㅡ;;


이런 !@#!%!@!@#!#@



nginx proxy

음.. 계속 말하겠지만. 난 돈 쓰는걸 많이 싫어하는 편이다.

투자에 인색하다고 하지 말아라.

특별한 투자 없이 처리할 수 있고 비용을 줄일 수 있으면 줄이는 게 당연한 것이다.

비록 난 대표는 아니지만 내 회사라는 마음이 있고 회사 돈을 아낀다면.. 그게 맞다는 것이다.

하지만 고민할 수 있는 위치가 아니거나 고민하다가 시간을 많이 소비한다면 오히려 그건 회사에게 더 실이 될 수 있으니 조심하자. (인건비가 요즘 장난 아닌지라...)


자. 최근 테라급 데이터를 다루다보니 당연히 눈길이 스토리지 서버에 갈 수 밖에 없는 상황이 생기고 스토리지가 생기더라도 아무리비싸고 좋고 훌륭한 raid를 써서 사용하더라도 결국 사고터지만 백업에 의존하는 상황이 생기기 마련이다.


그러다보니 스토리지를 과감히 포기하고 다른 방법을 택해보았다.


역시 돈이 없기 때문에 L4를 사용할 생각 조차 하지 않았다.

그리고  LVS는 몇 번 구축했지만 내가 원하는 성능을 내지를 못 했다.

내 욕구에 충족하는 수준이 nginx proxy이다.

이 놈 멋지다....

단순 i/o라면 발란스 관리하는 놈만 만들면 아주 아주 퍼팩트한 운영이 가능하다.


방법은 아래와 같다.



 
 192.168.0.x
Proxy 1  --monit-> Proxy 2   -monit->  Proxy 3  -monit -> Proxy 1 (cycle 모니터링)


Web 192.168.101.x 대역        
Web 192.168.101.1(10TB)  <-- sync --> Web 192.168.101.2(10TB) (cycle 모니터링)


Web 192.168.102.x 대역        
Web 192.168.102.1(10TB)  <-- sync --> Web 192.168.102.2(10TB) (cycle 모니터링)


Web 192.168.103.x 대역        
Web 192.168.103.1(10TB)  <-- sync --> Web 192.168.103.2(10TB) (cycle 모니터링)




1) Proxy 들은 각자 1명씩의 Proxy를 감시하여 Proxy 장애를 모니터링 하게 된다.
2) 각 Web의 대역은 서로 service 및 모니터링을 관활하게 된다. (실 운영은 2대보다 많아야지?)
3)  외부에서 접속이 할 경우 proxy 에서 rewrite 를 활용하여 서버에서의 응답을 return 처리
4) Proxy 에서는 Web 대역을 모니터링하여 upstream 을 조정 및 발란스 처리


1-4번까지의 기능의 데몬을 만들면... 오픈 소스를 통하여 대용량 스토리지처럼 운영할 수 있게 된다.


음...

좀 이해가 안되나?..

그냥 고민해보면... 디게 쉽다.

이런식으로 현재 운영을 하고 있는데... 물론 100% 동일하지는 않고 부수적인 것들이 좀 있기는 하지만... proxy 상단에 cdn이 있고 뭐 등등...

암튼 기본적인 이런 형태면 트래픽에 따른 proxy 추가와 용량에 따른 Web 대역을 늘리는 것으로 100 * 10TB정도의 용량은 가능해지게 된다. ㅡㅡ;;

물론 현재 초기 시범 서비스라... 이게 정답인지는 모르겠다...



어떤이는 이렇게 말한다.. 아 복잡해 걍 스토리지 몇대 넣고 L4넣죠... 라고 하는...


누군 그게 좋은지 몰라서 그러냐... 돈이 없다. 돈이 없어.

알림형 sync 만들어 보기...

서버간의 실시간 동기화를 해야 하는데....

무작정 cron으로 rsync를 하기 보다는 inotify를 사용하여 동기화를 시도해보았다.

물론 이건 엄청난 단점을 가지고 있다.. ㅡㅡ;;

그건 알아서들 분산하거나 보완하기 바란다.


간단한 예제를 만들어 보았다.

소스는 간단하니... 뭐.. 설명할 게 없다.
 

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/inotify.h>
#define EVENT_SIZE  ( sizeof (struct inotify_event) )
#define BUF_LEN     ( 1024 * ( EVENT_SIZE + 16 ) )
int main( int argc, char **argv ) {
    char target[20]; /* monitoring directory name */
    int fd;
    int wd; /* watch desc */
    char buffer[BUF_LEN];
    fd = inotify_init();
    if (fd < 0) {
        perror("inotify_init");
    }
    if (argc < 2) {
        fprintf (stderr, "Watching\n");
        strcpy (target, ".");
    } else {
        fprintf (stderr, "Watching '%s' directory\n", argv[1]);
        strcpy (target, argv[1]);
    }
    wd = inotify_add_watch(fd, "/root/inotify", IN_MODIFY | IN_CREATE | IN_DELETE | IN_MOVED_FROM | IN_MOVED_TO | IN_MOVE_SELF | IN_CLOSE_WRITE);
    while(1) {
        int length, i = 0;
        length = read(fd, buffer, BUF_LEN);
        if (length < 0) {
            perror("read");
        }
        while (i < length) {
            struct inotify_event *event = (struct inotify_event *) &buffer[i];
            printf ("[debug] wd=%d mask=%d cookie=%d len=%d dir=%s\n", event->wd, event->mask, event->cookie, event->len, (event->mask & IN_ISDIR)?"yes":"no");
            if (event->len) {
                switch (event->mask) {
                    case IN_CLOSE_WRITE:
                        if (event->mask & IN_ISDIR) {
                            printf("The directory %s was created[IN_CLOSE_WRITE].\n", event->name);
                        } else {
                            printf("The file %s was created[IN_CLOSE_WRITE].\n", event->name);
                        }
                        break;
                   
                    case IN_CREATE:
                        if (event->mask & IN_ISDIR) {
                            printf("The directory %s was created[IN_ISDIR]\n", event->name);
                        } else {
                            printf("The file %s was created[IN_ISDIR].\n", event->name);
                        }
                        break;
                    case IN_DELETE:
                        if (event->mask & IN_ISDIR) {
                            printf("The directory %s was deleted[IN_DELETE].\n", event->name);
                        } else {
                            printf("The file %s was deleted[IN_DELETE].\n", event->name);
                        }
                        break;
                    case IN_MODIFY:
                        if (event->mask & IN_ISDIR) {
                            printf("The directory %s was modify[IN_MODIFY].\n", event->name);
                        } else {
                            printf("The file %s was modify[IN_MODIFY].\n", event->name);
                        }
                        break;
               
                    case IN_MOVED_FROM || IN_MOVED_TO || IN_MOVE_SELF:
                        if (event->mask & IN_ISDIR) {
                            printf("The directory %s was moved[MOVE].\n", event->name);
                        } else {
                            printf("The file %s was moved[MOVE].\n", event->name);
                        }
                        break;
                }
            }
            i += EVENT_SIZE + event->len;
        }
    }
  /*
  inotify_rm_watch(fd, wd);
  close(fd);
  */
  return 0;
}

tune 1

php를 pmap으로 본 결과 locate의 용량이 허더더 하더라....



기본적으로 utf-8만 사용하기 때문에 뭐 특별한 고민없이 아래와 같이 처리하였다.

# rm -rf /usr/lib/locale/locale-archive
# localedef -i en_US -f UTF-8 en_US.UTF-8

pmap으로 확인해 본 결과 메모리가 엄청나게 줄었다... ㅡㅡ;;
 

20130612

CentOS5.x에 nginx, php-fpm, mariadb, memcache, barcode 등 프로그램 설치

현재 업무 중 많은 부분이 쇼핑몰과 SCM, ERP 등이다..

평균 동접은 약 1,000명 수준이다보니 단일 서버는 어렵고 분산 처리 방식으로 개발이 된다.


그에 맞는 서버 설정을 구성하였고 기록을 남긴다.

울 회사에서 내 포지션은 개발 관련 총책임자로 있어 돈이 들어가는 걸 기본적으로 싫어하는 나는 무료/오픈 소프트웨어 로 주로 사용하며 중국에서 근무하는 상황에서 개발인력 확보가 어렵고 인건비가 상당히 비싼 점을 고려한 세팅이다.

초기 개발 시에는 JAVA도 사용하였지만 현재는 다 걷어낸 상황이다.


대략 정리하면 아래와 같다.

Language : php-fpm(web), python & shell(server inside)

OS : CentOS5.x 64bit

Software : nginx(web or proxy), php,  python, memcache, mmonit, munin, sphinx, mysql or mariadb, imagemagick, genbarcode(barcode), ioncube, goaccess


설치 과정.

1. 우선 CentOS 설치시에 모든 패키지를 제거하고 최소화로 하여 설치를 진행한다.


2. 64bit를 기준으로 설치하였기 때문에 32bit 소프트웨어는 제거.
 # yum remove \*.i\?86

3. yum 으로 설치되는 부분이 있기 때문에 yum.conf 에다가 64bit 소프트웨어만 설치하도록 32bit 소프트웨어는 exclude 처리한다.
# vi /etc/yum.conf

exclude = *.i?86  #add exclude


4. yum으로 기본 패키지 정리.
#  yum -y remove php httpd mysql bind dhclient tftp inetd xinetd ypserv telnet-server rsh-server vsftpd tcsh nfs* samba tftp-server telnet system-config-printer
#  yum -y install system-config-date subversion gcc gcc-c++ g++ cpp make ncurses-devel automake autoconf tcl-devel rdate rsync pcre-devel gd-devel zlib zlib-devel curl curl-devel libcurl-devel openssl openssl-devel libopenssl-devel libtermcap-devel libc-client-devel bzip2-devel

5.ulmiit 설정
# ulimit -SHn 10240
# echo "ulimit -SHn 10240" >> /etc/rc.local

6. imagemagick 설치
# yum -y install ImageMagick*

7. iconv 설치 (source)
# tar zxvf libiconv-1.14.tar.gz
# cd libiconv-1.14
# ./configure --prefix=/usr/local
# gmake
# gmake install
# export LD_PRELOAD=/usr/local/lib/preloadable_libiconv.so

8. mysql or mariadb 설치 (2013년 이전에는 주변에서 데이터베이스 컨설팅 시에 돈 없으면 mysql을 사용하고 아주 아주 잘 설계하면 아주 아주 잘 버텨준다고 했다. 2013년 이후엔 mariadb를 추천해주고 있다. 이유는 explain 을 해보라. 그러면 알 것이다. 어차피 실 서비스는 정규화 설계로는 답없는 경우가 대부분이다.)


# groupadd mysql
# useradd -s /bin/false -g mysql mysql


8.1 mysql 설치 시... (5.6.x 기준)
# tar xvfz mysql-5.6.x.tar.gz
# cd mysql-5.6.x
# cmake -DCMAKE_INSTALL_PREFIX=/usr/local/mysql -DWITH_EXTRA_CHARSETS=all -DMYSQL_DATADIR=/usr/local/mysql/data -DENABLED_LOCAL_INFILE=1 -DWITH_INNOBASE_STORAGE_ENGINE=1 -DMYSQL_UNIX_ADDR=/tmp/mysql.sock -DSYSCONFDIR=/usr/local/mysql -DDEFAULT_CHARSET=utf8 -DDEFAULT_COLLATION=utf8_general_ci -DWITH_EXTRA_CHARSETS=all -DMYSQL_TCP_PORT=3306 -DENABLE_DTRACE=0


 8.2 mariadb 설치 시 (10.x.x 기준)
# tar xvfz mariadb-10.x.x.tar.gz
# cd mariadb-10.x.x
# cmake -DCMAKE_INSTALL_PREFIX=/usr/local/mysql -DWITH_EXTRA_CHARSETS=all -DMYSQL_DATADIR=/usr/local/mysql/data -DENABLED_LOCAL_INFILE=1 -DWITH_INNOBASE_STORAGE_ENGINE=1 -DWITH_ARCHIVE_STORAGE_ENGINE=1 -DWITH_PARTITION_STORAGE_ENGINE=1 -DWITH_ARIA_STORAGE_ENGINE=1 -DWITH_READLINE=1 -DMYSQL__ADDR=/tmp/mysql.sock -DSYSCONFDIR=/usr/local/mysql -DDEFAULT_CHARSET=utf8 -DDEFAULT_COLLATION=utf8_general_ci -DWITH_EXTRA_CHARSETS=all -DMYSQL_TORT=3306 -DENABLE_DTRACE=0

mysql과 mariadb는 cmake 까지만 다르다..

# gmake
# gmake install
# echo /usr/local/mysql/lib >> /etc/ld.so.conf && ldconfig

설정 파일은 알아서 잘...(여기에 올려진 설정은 메모리 16GB기준의 서버의 설정이다)

# cp example.cnf /usr/local/mysql/my.cnf
# cp support-files/mysql.server /usr/local/mysql/bin/


# chmod 700 /usr/local/mysql/bin/mysql.server
# chown mysql. -R /usr/local/mysql
# /usr/local/mysql/scripts/mysql_install_db --user=mysql --basedir=/usr/local/mysql --datadir=/usr/local/mysql/data

관리할 폴더 생성
# mkdir /usr/local/mysql/logs
# chmod 755 /usr/local/mysql/tmp
# chmod 755 /usr/local/mysql/logs
# chown -R mysql. /usr/local/mysql

링크 생성
# ln -s /usr/local/mysql/bin/mysql /usr/bin/


# echo "/usr/local/mysql/bin/mysql.server start" >> /etc/rc.local

# /usr/local/mysql/bin/mysql.server start
# /usr/local/mysql/bin/mysql_secure_installation


mysql이나 mariadb의 설치는 동일하다고 보면 된다.



replication (필요시...)
master 에서 실행
# show master status;

slave 에서 실행.
# CHANGE MASTER TO
 MASTER_HOST='master ip',
 MASTER_USER='마스터에 접속할 계정',
 MASTER_PASSWORD='마스터에 접속할 비밀번호',
 MASTER_LOG_FILE='마스터에서 정보 조회 후 설정',
 MASTER_LOG_POS=마스터에서 정보 조회 후 설정;

# START SLAVE;
Slave에서 err를 확인하여 아래의 메시지가 있으면 접속 성공
Slave I/O thread: connected to master 'useid@hostname:3306',replicat
ion started in log 'mysql-bin.00000x' at position xxx




9. php 설치

# tar zxvf php-5.x.x.tar.gz
# cd php-5.x.x
# yum -y install libxml2-devel libpng-devel freetype-devel freetype2-devel gmp-devel libmcrypt libmcrypt-devel gd libjpeg-devel glibc glibc-devel glib2 glib2-devel GeoIP*
# ./configure --prefix=/usr/local/php --with-config-file-path=/usr/local/php/etc \
--with-mysql=/usr/local/mysql --with-mysqli=/usr/local/mysql/bin/mysql_config \
--with-iconv=/usr/local \
--with-freetype-dir --with-jpeg-dir --with-png-dir --with-gd --enable-gd-native-ttf \
--with-mcrypt --with-curl --with-gmp --with-zlib --with-openssl --with-pear --with-bz2 \
--enable-mbstring --enable-mbregex --enable-bcmath --enable-inline-optimization -enable-sockets \
--enable-fpm

# gmake
# gmake install

# mkdir /usr/local/php/logs

# cp php.ini-production /usr/local/php/etc/php.ini
# rm -rf /usr/bin/php
# ln -s /usr/local/php/bin/php /usr/bin/

# cp /usr/local/php/etc/php-fpm.conf.default /usr/local/php/etc/php-fpm.conf
# vim /usr/local/php/etc/php-fpm.conf (알아서설정)

# echo "/usr/local/php/sbin/php-fpm" >> /etc/rc.local

# vim /usr/local/php/etc/php.ini (알아서설정)
extension_dir = "/usr/local/php/lib/php/extensions/no-debug-non-zts-20090626/"

# /usr/local/php/sbin/php-fpm



10. ioncube loader 설치 (소스 중에 주요부분은 사용하고 있어서 사용. 근데 이거 은근 메모리 많이 먹는다.)

# wget http://downloads2.ioncube.com/loader_downloads/ioncube_loaders_lin_x86-64.tar.gz
# tar xfz ioncube_loaders_lin_x86-64.tar.gz
# mv ioncube /usr/local/

# vim /usr/local/php/etc/php.ini
zend_extension = /usr/local/ioncube/ioncube_loader_lin_5.3.so (추가)
date.timezone = Asia/Shanghai (수정)





php 재시작
# killall php-fpm
# /usr/local/php/sbin/php-fpm


11. Zend 설치 (근데 요즘 사용안 하기 때문에 열외)

#  tar xvfpz ZendGuardLoader-php-5.3-linux-glibc23-x86_64.tar.gz
# mkdir /usr/local/zend/
# cp ZendGuardLoader-php-5.3-linux-glibc23-x86_64/php-5.3.x/ZendGuardLoader.so /usr/local/zend/

# vi /usr/local/php/etc/php.ini

[Zend]
zend_loader.enable=1
zend_loader.disable_licensing=0
zend_extension=/usr/local/zend/ZendGuardLoader.so

php 재시작
# killall php-fpm
# /usr/local/php/sbin/php-fpm







12. nginx 설치
# tar xzvf nginx-1.x.x.tar.gz
# cd nginx-1.x.x/

# ./configure --user=www --group=www --prefix=/usr/local/nginx --with-http_stub_status_module --with-http_ssl_module --with-http_gzip_static_module --with-http_flv_module --with-http_mp4_module --with-http_geoip_module --with-http_realip_module --with-http_image_filter_module

# gmake
# gmake install
# echo "/usr/local/nginx/sbin/nginx" >> /etc/rc.local
# vim /usr/local/nginx/conf/nginx.conf (알아서 설정)

# /usr/local/nginx/sbin/nginx


13. GepIP
# yum -y install GeoIP GeoIP-devel GeoIP-data perl-Geo-IP
   
# vim /usr/local/nginx/conf/nginx.conf 에 추가
geoip_country /var/lib/GeoIP/GeoIP.dat;

해당 가상호스트 설정 부분의 php 설정에 추가
fastcgi_param  COUNTRY $geoip_country_code;


14. 바코드 설치

# wget http://mirror.yongbok.net/gnu/barcode/barcode-0.98.tar.gz
# tar xfvz barcode-0.98.tar.gz
# cd barcode-0.98
# ./configure
# gmake
# gmake install
# ldconfig

# wget http://www.ashberg.de/php-barcode/download/files/genbarcode-0.4.tar.gz
# tar xvfz genbarcode-0.4.tar.gz
# cd genbarcode-0.4
# gmake
# gmake install

php 예제....
# wget http://www.ashberg.de/php-barcode/download/files/php-barcode-0.4.tar.gz
# tar xvfpz php-barcode-0.4.tar.gz
# cd php-barcode-0.4


15. munin 설치 (nginx, php, mysql, mariadb 등의 plugin은 알아서들 설치)
# rpm -Uhv http://apt.sw.be/redhat/el5/en/x86_64/rpmforge/RPMS/rpmforge-release-0.3.6-1.el5.rf.x86_64.rpm
# yum install munin-node -y
# vim /etc/munin/munin-node.conf
# chkconfig munin-node on
# /etc/init.d/munin-node start


16. monit 설치 (munin 이 정상적으로 잘 돌고 있는 지 감시의 목적)
# tar xvfz monit-x.y.z.tar.gz
# cd monit-x.y.z
# ./configure --prefix=/usr/local/monit
# gmake
# gmake install


17. goaccess 설치
# wget http://sourceforge.net/projects/goaccess/files/0.4.2/goaccess-0.4.2.tar.gz/download
# ./configure
# make
# make install

18. sphinx 설치... (이건 워낙 확장들이 많아서... 난 우선 중국에 있어 sphinx-chinese 를 사용)

참조 http://www.sphinx-search.com/ 여기 가면 친절히 설명이 되어있다.




tune 하는 부분은 따로 포스트 할 생각이다.


위의 내용은 아주아주 기본적인 설치이며... pmap으로 각 process를 모니터링 & 튠해야 한다.




logrotate를 이용한 mysql log 관리

mysql 의 logs 관리


나는 기본적으로 /usr/local/ 에 프로그램을 설치를 한다.

그래서 mysql 의 경우에는 /usr/local/mysql 로 설치를 한다.

그리고 로그는 해당 프로그램의 하위 폴더에 두어 관리를 한다.

mysql의 logs 관리 폴더는 /usr/local/mysql/logs/... 로 관리한다.


log 관리를 logrotate를 활용하여 해보자.

/etc/logrotate.d/ 안에 mysql 파일을 만들어 아래 내용을 작성.

이렇게 하면 /etc/cron.daily/logrotate 알아서 관리

/var/log/mysql-slow.log {
ifempty
        daily
        rotate 3
        missingok
        size=10M
        compress
        postrotate

        if test -x /usr/local/mysql/bin/mysqladmin && \
                /usr/
local/mysql/bin/mysqladmin ping &>/dev/null
        then
                /usr/
local/mysql/bin/mysqladmin flush-logs
        fi
                endscript
}

Articles