태터데스크 관리자

도움말
닫기
적용하기   첫페이지 만들기

태터데스크 메시지

저장하였습니다.

MYSQL (7)


2 byte 코드를 사용하는 한글을 데이터베이스에 저장할 때 DB의 잘못되어 있는 캐릭터셋 설정은 많은 개발자와 DBA에게 혼란을 야기한다. 아주 오래전 신입사원시절에 Ingres라는 DB의 기술지원을 할 때도 가끔 한글코드가 잘못 지정되어 있어 DB를 업그레이드하거나 다른 서버로 마이그레이션할 때 불편함을 겪곤 했다.

그리고 15년이 지난 요즘...취미삼아 붙잡고 있는 Apache, PHP, MySql 의 환경에서도 여전히 DB의 한글코드 문제는 이따금씩 발생하곤 한다.

예를 들자면 웹페이지의 한글은 정상적으로 깨지지 않고 표시되는데 웹페이지를 통해 DB에 한글을 저장하고 다시 웹페이지에서 불러오면 DB에 저장한 한글데이터가 깨져보이는 현상이 대표적인 "한글깨짐" 증상이다.

이때 확인해야할 것은

1. 웹브라우저의 캐릭터셋.
2. 웹서버의 캐릭터셋.
3. 데이터베이스 서버의 캐릭터셋.
4. 생성되어 있는 데이터베이스의 캐릭터셋.
5. DB에 접속하는 세션의 캐릭터셋.

크게 4가지라 할 수 있다.

이 네가지 캐릭터셋 설정 중 어느하나가 다른 세개와 다르게 설정되어 있다면 여러가지 증상이 발생할 수 있다. DB에는 정상적으로 저장되는데 웹페이지에서 조회하여 보면 깨져있거나, 웹브라우저 화면에선 정상적으로 DB에 저장되어 있는 것 처럼 보이는데 DB의 쿼리툴에서 조회해보면 깨져서 저장되어 있을 수도 있다. 초심자로서는 잘 이해되지 않는 경우일 수도 있다.

즉, 클라이언트인 웹브라우저의 캐릭터셋부터 DB에 접속하는 세션의 캐릭터셋, 그리고 데이터베이스서버와 데이터베이스 생성시 지정되는 캐릭터셋, 마지막으로 테이블 생성 시 지정된 캐릭터셋이 일관되게 설정되어 있어야만 어떠한 환경에서 저장, 조회하더라도 문제가 발생되지 않는다.

 

먼저 웹브라우저의 캐릭터셋을 보자.

웹브라우저의 캐릭터셋은 서버에 저장되어 있다가 웹브라우저가 호출하는 웹페이지의 소스에서 설정할 수 있다.


meta 태그로 설정되는 charset = "utf-8" 부분이 바로 웹브라우저가 해당 웹페이지를 호출했을 때 설정할 브라우저의 캐릭터셋이다.

다음은 웹서버의 캐릭터셋이다.


아마치의 경우 httpd.conf 파일에서 'AddDefaultCharset' 파라미터에서 캐릭터셋을 설정할 수 있다. 기본적으로 utf-8을 지원하는데 이부분은 특별히 건드리지 않아도 utf-8이나 euckr을 처리해 주는 것 같다. 아직까지는 웹서버의 캐릭터셋으로 인해 DB에 저장되는 데이터의 캐릭터셋 오류는 보지 못했다.
(난 현재 웹이나 DB엔지니는 아니므로 경험부족일지도 모른다.. -.-)

세번째는 데이터베이스 서버의 캐릭터셋 설정이다.

mysql의 경우 이부분이 참 복잡했다. 한글이 깨지는 경우 대부분 서버의 캐릭터셋 환경과 생성된 DB의 캐릭터셋이 틀린 경우인것 같은데 또 꼭 그렇지만도 않은 듯 하다.

먼저 서버의 캐릭터셋 환경을 보면...


왜인지는 모르겠지만 character set client와 character set connection의 Global value가 latin1 으로 되어 있다. 이 의미는 클라이언트 프로그램 즉 브라우저를 통해 DB에 저장할 데이터를 전달받은 아파치 웹서버가 DB에 세션을 맺을 때 특별한 캐릭터셋을 지정하지 않으면 latin1 으로 캐릭터셋을 지정한다는 의미다.
한글은 2바이트 코드이고 latin1은 1바이트 코드다. 따라서 2바이트인 한글을 쪼개서 저장하면 당연히 깨져서 저장이 될 수 밖에 없다. 만약 이런 환경이라면 아무리 데이터베이스를 euckr 이나 utf8로 생성하더라도 클라이언트를 통해 커넥션을 맺고 한글을 저장하면 깨져서 저장될 수 밖에 없다.

네번째, 데이터베이스의 캐릭터셋 설정


각 데이터베이스마다 사용할 캐릭터셋을 다르게 지정할 수 있다.

마지막으로 문제해결의 키를 가진 DB 접속 세션의 캐릭터셋...

DB에 접속하는 세션의 캐릭터셋을 잘 이용하면 한글깨짐의 문제를 해결할 수 있다. 하지만 그 전에 앞에서 설명한 mysql 서버의 캐릭터셋 환경을 utf8로 통일시켜주는 것이 가장 좋은 방법이라는 점을 명심해야한다. mysql의 소스를 가져다 컴파일하여 설치할 때 컴파일옵션에 캐릭터셋을 utf8로 설정하는 것이 가장 좋은 방법이다.

하지만 오래전부터 운영하던 mysql 서버라면 쉽게 서버의 캐릭터셋을 변경할 수는 없을 수도 있다. 이런 경우 mysql 서버의 캐릭터셋 환경이 어떻든 간에 DB를 용도에 따라 euckr 혹은 utf8로 생성하고 각각의 db를 사용하는 웹페이지 소스에서 meta 태그를 이용해 euckr 혹은 utf8로 페이지의 캐릭터셋을 지정할 수 밖에 없다.

그리고 각 db에 접속하는 mysql_connect() 함수가 포함된 소스페이지에서 db에 접속한 세션의 캐릭터셋을 각각 euckr 혹은 utf8로 설정하여 사용할 수 있다.


위의 mysql_query('set names utf8') 함수를 실행하면 접속한 DB세션의 캐릭터셋이 utf8로 설정되고 MySql 서버의  character set client와 character set connection 환경변수보다 우선하게 되어 한글이 깨지지 않고 저장/조회된다. 만약 euckr로 설정하고자 한다면 다음과 같이 utf8 대신 euckr 을 지정하면 된다.

mysql_query('set names euckr');

물론 웹브라우저에서 한글이 깨지지 않도록 php 파일 자체의 캐릭터셋을 utf8 혹은 ansi 코드 중 하나로 저장하는 것도 중요하다. 만약 php 파일 자체의 코드를 맞추지 못하면 DB에서 조회한 데이터뿐만 아니라 HTML 내의 한글도 모두 깨지게 되므로 주의가 필요하다.

2011년 9월의 조금 흐린 어느날.....






갑자기 운영하던 웹사이트(RedHat Linux, Apache, PHP, MYSQL)에 접근이 안되는 장애가 생겼다.

root 계정으로 로그인하여 확인하니 httpd 대몬이 평소보다 훨씬 많은 숫자가 실행중이었고 mysql 대몬은 정상적으로 실행중인 것 처럼 보였다.

급하게 apache와 mysql을 재구동 하려 하였으나 mysql의 종류(mysql.server stop)가 안되었고 강제로 kill한 뒤 mysql을 구동하려 하였으나 평소와는 달리 프로세스는 한참만에 떴으나 mysql 클라이언트 및 apache를 통한 db 접근이 안되었다.

mysql 로그를 확인하니 Filesystem의 full로 인해 오류가 발생하였음을 확인할 수 있었다.

먼저 mysql db가 위치한 파일시스템의 불필요한 파일... (나의 경우 tomcat의 mod_jk.log(??) 파일이10G이상으로 커져있었다...)을 지우고 ....

mysql과 apache를 재구동하였다.

웹도 정상적으로 접근하였으나 특정 테이블을 접근하는 php 소스 부분에서 mysql_fetch()의 오류가 발생하였다.

mysql 클라이언트로 접근하여 

mysql> repair table 테이블명;

을 수행하여 복구하였다.

그 전에

mysql>analyze table 테이블명;

을 수행하였더니 테이블이 Crach되어 있다는 분석보고가 나왔다.



.. 리눅스에 apache...mysql...php를 우여곡절끝에 설치하고 phpMyAdmin을 설치한다..
MYSQL은 5.0 버전이다.

VMWARE에 설치된 RedHat Enterprise 4 update 4 (i386)에서는 별 문제없이 최신버전의 A.P.M을 설치했지만 이상하게도 회사의 Intel X86 서버에서는 libphp5.so가 생성되지 않는다...

결국 apache 5.0의 안정버전과 mysql 5.2.. 그리고 php 4.4.4를 설치했다. 이 조합에서는 잘 된다... -.-

하지만 phpMyAdmin 최신버전을 설치하여 로그인하려는데...

#1251 - Client does not support authentication protocol requested by server; consider upgrading MySQL client

요런 에러가 난다...

찾아보니...

mysql> set password for
    -> root@localhost = OLD_PASSWORD('비밀번호');

Query OK, 0 rows affected (0.02 sec)

mysql> flush privileges;

Query OK, 0 rows affected (0.00 sec)

요렇게 해줘야...로그인이 되더라는 말씀... mysql5와 mysql4 버전의 패스워드 암복호화 방식의 변경으로 인한 문제인것 같다.

넘 힘들다..증말...



금일 (2007년 1월25일) 기준 최신버전의 MYSQL인 5.0.33 설치 중 다음과 같은 에러가 발생한다.
네이버...구글 등 한글 검색사이트 검색 결과 관련 정보가 하나도 -.- 없었다.

./configure -- .....

sql_class.cc: In constructor 'THD::THD()':
sql_class.cc:265: error: 'query_cache_init_query' was not declared in this
scope
make[5]: *** [sql_class.o] Error 1

이 에러는 configure 옵션 중

./configure --without-query-cache 

위의 옵션을 줄 경우 발생한다.

--- 해결방법---

소스디렉토리 아래의 sql 디렉토리에 sql_class.cc 파일을 열고 다음과 같이 두줄을 추가해 준 뒤 다시 configure와 make, make install을 실행한다.

   client_capabilities= 0;                       // minimalistic client
   net.last_error[0]=0;                          // If error on boot
   #ifdef HAVE_QUERY_CACHE       <--- 추가....
   query_cache_init_query(&net);                 // If error on boot
   #endif                                       <--- 추가....
   ull=0;
   system_thread= NON_SYSTEM_THREAD;
   cleanup_done= abort_on_warning= no_warnings_for_error= 0;



mysql 5.0 이상버전의 소스로 컴파일하여 설치할 때에는 client library를 별도로 한번 더 컴파일해주어야 했다.

mysql 설치 후

./configure \
--enable-thread-safe-client \
--with-charset=euckr \
--without-server \
--without-query-cache \
--without-docs \
--without-man \
--without-bench \
--without-readline \
--without-libedit
make && make install


위와 같이 prefix 를 지정하지 않고 MySQL 클라이언트만을 설치할 경우에는 /usr/local/lib/mysql 디렉토리에 library들이 설치된다.

그리고 /usr/local/bin/ 디렉토리에 실행스크립트를 비롯한 MySQL 관련 파일들이 자리잡게 된다.



MySQL을 설치할 때는 Daemon 들이 root 소유자로 실행되지 않도록 하는 것이 좋다. 보안상 MySQL의 취약성이 발결되어 공격을 당하게 되면 곧바로 root shell을 획득하는 것이 가능하기 때문이다. 따라서 MySQL을 mysql과 같이 일반 계정으로 실행되도록 설치하라.

그리고 부팅 시 자동으로 실행되도록 하기 위해서는 다음과 같은 작업 절차를 거친다.

사용자 삽입 이미지

1. 설치된 디렉토리 (여기서는 /app/mysql5 로 가정한다.) 아래의 support-files 디렉토리로  이동하고...

2. mysql.server 라는 파일을 /etc/init.d 로 적당한 이름을 주어 복사한다.   (여기서는 mysql5022 로 복사했다.)

3. chkconfig --add mysql5022 명령을 실행하여 서비스로 등록한다.

4. chkconfig --level 2345 mysql5022 on 명령을 실행하여 mysql을 runlevel 2,3,4,5에서 실행하도록 설정한다.

5. 서버를 리부팅하여 정상적으로 서비스가 기동되는지 확인한다.



1. mysql 을 다음과 같이 --skip_grant 옵션으로 실행한다.

     mysqld_safe --skip_grant &

    패스워드 없이 접근이 가능합니다.

2.  mysql 만 치고 접속합니다.

3. connect mysql;    (root의 패스워드 수정할 때)

4. update user set password=password('new-password') where user='root';

5. flush privileges;

root 패스워드를 잊어 버렸을 경우 root 패스워드 재설정해 주는 과정임