반응형

점검하면서 쿼리를 작성 중 재밌는 일이 생겨서 공유해 본다.


select gender from employees group by gender;

select distinct(gender) from employees;


둘 다 gender의 상태(??갑자기 설명이 안되네;;;) 를 확인하기 위해서 동료가 group by로 작성하는 것을 보았다.


순간...왜 저렇게 해야되는 걸까? 간단하게 distinct 를 하면 되지 않느냐로 내가 질문하면서 궁금해서 찾아보게 되었다.


난 성능적인 차이가 더 궁금해서 찾아본 결과 다음과 같이 확인할 수 있었다.


동일한 로직을 이용하기에 성능의 차이는 없을 거지만,

group by는 정렬를 하기에 distinct가 조금 더 성능적으로 빠르다.


라는 결과를 얻을 수 있었다.

하지만 이거는 어디까지나 사람들의 의견만 보았다.


그래서 간단하게 테스트를 아래와 같이 진행 하였다.

아직 MySQL 의 쿼리 튜닝할 정도의 실력이 아니기에..간단하게 explain 으로 확인 하였다.



보는바와 같이 동일한 건수를 참조하며, 마지막에 Using filesort 라는 것을 확인할 수 있다.


Using filesort


쿼리에 ORDER BY가 포함되어있는 경우 DBMS 내부적으로 sort를 한다.(MySQL의 경우 퀵소트 알고리즘을 사용)

소트를 처리하기 위해서 메모리공간을 사용하며, 메모리가 부족할 경우 임시파일을 사용한다.

(임시파일은 HDD등의 저장장치에 생성되며 PC에서 가장 느린 성능을 보여주는 부분이다. 

-> 결과적으로 EXPLAIN시 Extra필드에 Using filesort가 표시된다면 이는 파일을 사용할 가능성이 크다는 것이며, 

가장 느린성능을 보여줄것이다.)


하지만 인덱스 순으로 행을 fetch할수 있으면 Filesort는 필요없게된다. 

이런경우 당연히 Extra필드에도 Using Filesort가 표시되지 않는다.

ORDER BY 구문에 인덱스가 사용되도록 쿼리나 테이블을 튜닝하자.


여기까지는 하나의 테이블에서 ORDER BY를 사용한 경우이다.


2개 이상의 테이블을 JOIN해야 하며 결과는 정렬되어 있어야 하는경우엔 어떻게 될까?

이경우엔 3가지 유형이 있다.


1. 첫 테이블을 인덱스를 이용하여 정렬하고, JOIN하는 경우

2. 첫 테이블을 FileSort해서 Join하는 경우 -> Using filesort

3. JOIN후 Filesort하는 경우(JOIN한 결과는 인덱스가 없으므로 JOIN후 Sort할때는 항상 Filesort이다.) -> Using Temporary, Using filesort

 

뒤로 갈수록 처리가 무거워 지므로 1,2번의 실행계획이 잡히도록 유도하는 것이 좋다.

1번을 유도하는 방법 -> WHERE절의 검색조건과 ORDER BY절의 소트조건을 하나의 테이블에 집중시킨다.

출처 : http://blog.daum.net/billbo/11


비록 적은 중복값에 대해서는 차이가 없을지 모르겠으나 그 값이 많아 진다면 group by로 해서 확인하는 것은 부하를 줄 수도 있을 것 같다.


자세한 Filesort 내용은 아래의 위치에서 확인해 보자

http://www.mysqlkorea.com/sub.html?mcode=manual&scode=01_1&m_no=22505&cat1=827&cat2=963&cat3=980&lang=k


sort를 하는 부분은 tmep를 사용하기에 가급적 sort할 필요가 없다면 distinct 를 사용해서 확인하는 것이 좋을 듯 싶다.


오랜만에 아키텍처까지 한번 더 볼 수 있는 좋은 기회였다.


반응형
반응형

Bin Log가 쌓이게 되면 디스크가 증가하기에 이에 대해서 방안을 파이썬으로 지우는 스크립트를 분석하면서

이와 관련된 블로그 찾아서 포워딩해 본다


우리 회사에서는 아래와 같은 쿼리를 사용한다


PURGE MASTER LOGS TO '%s'


%s 에는 binlog의 번호를 확인한다.(파일에서 ls 로 확인)


하지만 서버에서도 확인이 가능하다.


show master logs;


알아서 정렬되어 나오며, 특정 번호를 선정하면 그 하위 번호까지 모두 삭제가 된다.


ex) mysql> purge master logs to 'mysql-bin.001000';

지정된 바이너리 로그 이하의 파일이 삭제됨.
mysql-bin.000001~0000999 까지 삭제됨.


참조 : http://www.ischo.net/mysql/2875


  MySQL Binary Log 는 add, delete, insert, update 등의 query 가 저장되어 있는 파일로서 MySQL 을  설치하게 되면 기본적으로 MySQL Binary Log 가 생성됩니다.
 Binary Log 를 쌓지 않아도 되는 MySQL 구동 환경에서는 Binary Log 를 삭제하므로, 디스크 공간
 여유 공간을 확보할 수 있는데, MySQL Binary Log 를 지우는 방법에 대해서 알아보겠습니다.

1. MySQL Replication 환경에서 지우기

  - MySQL Replication MASTER 서버
    shell> mysql -u root -p
    mysql> RESET MASTER;

  - MySQL Replication SLAVE 서버
    shell> mysql -u root -p
    mysql> RESET MASTER;

2. MySQL Binary Log sequence number 또는 특정 일자로 지우기

  shell> mysql -u root -p
  mysql> PURGE BINARY LOGS TO 'mysql-bin.000015';
  shell> mysql -u root -p
  mysql> PURGE BINARY LOGS BEFORE '2009-05-01 00:00:00';

3. mysqladmin flush-logs 명령어를 통해서 MySQL Binary Log 지우기

   shell> mysqladmin -u root -p flush-logs

4. MySQL Binary Log 생성을 방지하는 방법

  /etc/my.cnf 파일에서 아래 라인을 주석 처리
  log-bin

5. MySQL Binary Log 를 특정 1주일까지만 생성 및 보관하기
  /etc/my.cnf 파일에서 아래 라인을 추가

  expire_logs_days = 7


출처 : http://faq.hostway.co.kr/Linux_DB/1307

반응형
반응형
업무를 하고 중간에 내가 하고 싶었던 일을 하다 보니 한번에 모든 것이 정리되는 것이 아니라

띄엄띄엄 정리를 하게 된다.


그러다 보니 어제는 분명히 완벽히 되던 것이 오늘 진행하다 보면 진행이 안되는 경우가 생긴다


어제까지 완벽히 Install 이 진행 되었었는데, MySQL 에 로그인까지 되는 것을 확인 했다.


하지만 다음날 DB를 내렸다가 다시 올리게 되면 로그인은 커녕 MySQL 이 시작도 안되는 현상이 발생 되었다.



맨 처음에 언급했듯이 해당 서버는 많으 사람들이 이용하는 개발 DB이다.

물론 개발자들을 위해서 멀티로 DB를 설치해 주고 또한 나도 여기서 필요시에 다른 버전을 설치한다.


그러다보니 3.x 버전부터 5.0x 버전 지금의 5.6.31까지 몇개의 MySQL 엔진이 설치되어 있다.


당연히 my.cnf 도 몇개가 있다.


참조 : http://jmkjb.tistory.com/entry/MySQL5621SetupSourceCompile


여기서 보면 service를 등록하는 부분이 있다.


나 또한 등록하여 진행을 하는데,

- cp /usr/local/rmysql/support-files/mysql.server /etc/init.d/mysqld


- vi /etc/init.d/mysqld

아래 내용수정

basedir=/usr/local/rmysql

datadir=/usr/local/rmysql/DB/data


- chkconfig mysqld on

- chkconfig --list mysqld

 

6. MySQL 서비스 시작


- service mysqld start


여기까지 따라한 후 service 를 시작하면 다음과 같은 에러를 발견할 수 있다.



ㅎㅎㅎㅎㅎㅎ여기에 보면 이상한 숫자들과 경로들을 볼 수 있다.

이 부분들은 내가 mysqld 파일을 디버깅하면서 적어 놓은 것들이다....ㅠㅠ


다음은 실행하면서 떨어진 devDB.err 이다



전의 포스팅한 내용에는 분명 rmy.cnf 에는 해당 skip-locking 이라는 설정이 전혀 있지 않다.


상사의 말 한마디에 my.cnf (3버전에서 쓰는 것) 을 확인해 보았다.



ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ

나랑 장난치냐 지금 너!!!!!!!

ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ


 vi /etc/init.d/mysqld


그래......여기를 수정하면 가능하겠다 싶어 수정하였다.


 echo $echo_n "Starting MySQL"

    if test -x $bindir/mysqld_safe

    then

      # Give extra arguments to mysqld with the my.cnf file. This script

      # may be overwritten at next upgrade.

      $bindir/mysqld_safe --defaults-file=/etc/rmy.cnf --datadir="$datadir" --pid-file="$mysqld_pid_file_path" $other_args >/dev/null 2>&1 &

      wait_for_pid created "$!" "$mysqld_pid_file_path"; return_value=$?


빨간색으로 수정해 주었다.

이것을 처음부터 알았다면 고생 안했을 일을....


친구야...우리 앞으로 잘 지내자....ㅠ


나처럼 my.cnf 위치를 수정해서 쓰는 분들은 꼭 확인해 보세요....


이로써 나는 또 초급이란 것을 티내고 있다.....


이제는 3버전에서 5.6으로 Migration 하는 부분을 올릴 예정이다.

테스트는 금방 할 것 같지만...테스트 데이터는 3버전에서 사용하는 것들이라 많은 부분을 수정해 줘야 할 듯 싶다.


Oracle에서 심심하면 했던 Migration.....새삼 그립다....ㅎㅎ

여기서도 패러럴이 먹히면 좋겠지만.....

반응형
반응형

DB 를 설치해 보자.


먼저 설치를 하기 전에 현재 개발 서버에는 Mysql 3.2X 가 무수히 멀티로 운영중이며, 또한 MySQL 5.0x 버전 또한 설치가 되어 있다.


그렇기에 나는 기존 설치 방식이 아닌 다른 방식으로 설치를 진행할 수 밖에 없었다.



1. my.cnf 생성


이 부분은 앞으로 수정되어야 할 듯싶다.


#my.cnf 를 읽는 순서

#1. /etc/my.cnf파일

#2. /etc/mysql/my.cnf

#3. /usr/local/mysql/etc/my.cnf

#4. ~/.my.cnf


[client]

default-character-set = euckr

#default-collaction-set = euckr_korean_ci #euckr_bin

[mysqld]

user = mysql

port            = 4958

basedir = /usr/local/rmysql

datadir = /usr/local/rmysql/data

tmpdir = /usr/local/rmysql/tmp

socket          = /tmp/rmysql.sock

default-storage-engine = MyIsam

event-scheduler = ON

sysdate-is-now


#######

back_log = 100

max_connections = 300

max_connect_errors = 999999

thread_cache_size = 50

table_open_cache = 400

wait_timeout = 28800


max_allowed_packet = 32M

max_help_table_size = 32M

tmp_table_size = 512K


sort_buffer_size = 128K

join_buffer_size = 128K

read_buffer_size = 128K

read_rnd_buffer_size = 128K


query_cache_size = 32M

query_cache_limit = 2M


group_concat_max_len = 1024


########

######### MyISAM Option

## InnoDB를 사용하지 않고 MyISAM만 사용한다면 key_buffer_size 를 4GB까지 설정

key_buffer_size = 32M

bulk_insert_buffer_size = 32M

myisam_sort_buffer_size = 1M

myisam_max_sort_file_size = 2G

myisam_repair_threads = 1

myisam_recover

ft_min_word_len = 3


##로깅 옵션

pid-file = /usr/local/rmysql/logs/mysqld.pid

log-warnings = 1

log-error = /usr/local/rmysql/logs/mysqld


##General log 사용

general_log = 0

general_log_file = /usr/local/rmysql/logs/general_query.log


log_slow_admin_statements

slow-query-log = 1

long_query_time = 1

slow_query_log_file = /usr/local/rmysql/logs/slow_query.log


##replication option

##if this DB is Master DB then you need to remove #

log-bin = /usr/local/mysql/logs/binary_log

binlog_cache_size = 128K

max_binlog_size = 512M

expire_logs_days = 14

log-bin-trust-function-creators = 1

sync_binlog = 1


##if this DB is Slave DB then you need to remove #

#relay-log = /usr/local/rmysql/logs/relay_log

#relay_log_purge = TRUE

#read_only


##현재 서버가 슬레이브이면서 마스터 MySQL인 경우

##현재 MySQL 서버로 복제되는 쿼리를 바이너리 로그에 저장하려면 아래 주석을 해제

#log-slave-updates


위와 같이 작성 하였지만, 설치가 되지 않았다.


 - ...../rmysql/scripts 에서 처음 DB생성을 시도하였지만 아래와 같은 에러가 발생하였다.

FATAL ERROR: Could not find .//usr/local/rmysql/bin/my_print_defaults


If you compiled from source, you need to run 'make install' to

copy the software into the correct location ready for operation.


If you are using a binary release, you must either be at the top

level of the extracted archive, or pass the --basedir option

pointing to that location.


검색하였더니 scripts 폴더가 아닌 mysql base 위치에서 진행하라고 나와 있었다.(외국 블로그)


그래서 다시 base위치에서 진행을 시도 하였지만...아래와 같이 진행되고 stop 되었다.


Installing MySQL system tables...2016-06-14 15:11:49 0 [Warning] TIMESTAMP with implicit DEFAULT value is deprecated. Please use --ex                                                                                       plicit_defaults_for_timestamp server option (see documentation for more details).

2016-06-14 15:11:49 0 [Note] ./bin/mysqld (mysqld 5.6.31-log) starting as process 13591 ...


결국 찾지 못하고 base에 my.cnf 를 설정하기로 했다.(최대한 my.cnf 내용은 간소하게..설치가 목적이라..)


# For advice on how to change settings please see

# http://dev.mysql.com/doc/refman/5.6/en/server-configuration-defaults.html


[mysqld]


# Remove leading # and set to the amount of RAM for the most important data

# cache in MySQL. Start at 70% of total RAM for dedicated server, else 10%.

# innodb_buffer_pool_size = 128M


# Remove leading # to turn on a very important data integrity option: logging

# changes to the binary log between backups.

# log_bin


# These are commonly set, remove the # and set as required.

# basedir = .....

# datadir = .....

# port = .....

# server_id = .....

# socket = .....


# Remove leading # to set options mainly useful for reporting servers.

# The server defaults are faster for transactions and fast SELECTs.

# Adjust sizes as needed, experiment to find the optimal values.

# join_buffer_size = 128M

# sort_buffer_size = 2M

# read_rnd_buffer_size = 2M


sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES

#default-character-set = euckr  이게 문제였다...

character-set-server = euckr   이렇게 작성해 주었다.


user            = mysql

port            = 4958

socket          = /tmp/rmysql.sock

default-storage-engine  = myisam

event-scheduler = ON

sysdate-is-now


key_buffer_size                 = 32M

bulk_insert_buffer_size = 32M

myisam_sort_buffer_size = 1M

myisam_max_sort_file_size       =       2G

myisam_repair_threads                   =       1

myisam_recover

ft_min_word_len                                         = 3



[root@devDB:/usr/local/rmysql]# ./scripts/mysql_install_db --defaults-file=/usr/local/rmysql/my.cnf --basedir=/usr/local/rmysql --datadir=/usr/local/rmysql/data --explicit_defaults_for_timestamp=on


또 에러 발생...



...

2016-06-14 15:40:33 13684 [Note] InnoDB: 5.6.31 started; log sequence number 0

2016-06-14 15:40:33 13684 [ERROR] /usr/local/rmysql/bin/mysqld: unknown variable 'default-character-set=euckr'

2016-06-14 15:40:33 13684 [ERROR] Aborting


2016-06-14 15:40:33 13684 [Note] Binlog end

2016-06-14 15:40:33 13684 [Note] InnoDB: FTS optimize thread exiting.

2016-06-14 15:40:33 13684 [Note] InnoDB: Starting shutdown...

2016-06-14 15:40:34 13684 [Note] InnoDB: Shutdown completed; log sequence number 1600607

2016-06-14 15:40:34 13684 [Note] /usr/local/rmysql/bin/mysqld: Shutdown complete

OK


To start mysqld at boot time you have to copy

support-files/mysql.server to the right place for your system


PLEASE REMEMBER TO SET A PASSWORD FOR THE MySQL root USER !

To do so, start the server, then issue the following commands:


  /usr/local/rmysql/bin/mysqladmin -u root password 'new-password'

  /usr/local/rmysql/bin/mysqladmin -u root -h devDB password 'new-password'


Alternatively you can run:


  /usr/local/rmysql/bin/mysql_secure_installation


which will also give you the option of removing the test

databases and anonymous user created by default.  This is

strongly recommended for production servers.


See the manual for more instructions.


You can start the MySQL daemon with:


  cd . ; /usr/local/rmysql/bin/mysqld_safe &


You can test the MySQL daemon with mysql-test-run.pl


  cd mysql-test ; perl mysql-test-run.pl


Please report any problems at http://bugs.mysql.com/


The latest information about MySQL is available on the web at


  http://www.mysql.com


Support MySQL by buying support/licenses at http://shop.mysql.com


WARNING: Found existing config file /usr/local/rmysql/my.cnf on the system.

Because this file might be in use, it was not replaced,

but was used in bootstrap (unless you used --defaults-file)

and when you later start the server.

The new default config file was created as /usr/local/rmysql/my-new.cnf,

please compare it with your file and take the changes you need.


WARNING: Default config file /etc/my.cnf exists on the system

This file will be read by default by the MySQL server

If you do not want to use this, either remove it, or use the

--defaults-file argument to mysqld_safe when starting the server



- 이 부분은 위에서 수정해 주었다. #default-character-set = euckr  이게 문제였다...


이 후 다시 진행을 했더니 제대로 설치가 된 것 같았다.

이제 MySQL 를 실행해 보자


여기서는 아래 사이트를 참고하면서 진행 하였다.

참조 : http://jmkjb.tistory.com/entry/MySQL5621SetupSourceCompile

반응형

'MySQL' 카테고리의 다른 글

[MySQL] 빈로그 지우기 [펌]  (0) 2016.06.16
[MySQL]5.6.31 source 설치 (3) - 완료  (0) 2016.06.15
[MySQL]5.6.31 source 설치  (0) 2016.06.13
[MySQL] Federated Engine (Table) (2) - 마무리  (0) 2016.06.09
[MySQL] Federated Engine (Table)  (1) 2016.06.09
반응형

MySQL 설치를 보면 대부분 간단하게 yum 을 이용한 설치를 알려준다.


MySQL 엔지니어는 안해봤지만, Oracle Engineer를 한 경험상, UI를 통해 설치하는 경우도 대부분이긴 하지만,

단순히 Typicall 이 아닌 Manual 대로 진행하는 경우가 보통이다.

즉, 설정값 들을 수동으로 진행하는 경우가 대부분 이었다.


그래서 나는 단순 설치가 아닌 CMake를 이용한 source 설치를 

물론 책에도 이렇게 진행하는 방식을 알려준다...




MySQL 5.6.31 로 진행하였으며, mysql 홈페이지 가면 찾기가 조금 까다롭다.

해당 페이지를 참조하면서 설치 하였다.

http://jmkjb.tistory.com/entry/MySQL5621SetupSourceCompile


먼저 souce 파일을 다운받아 보자.


현재 테스트 서버는 CentOS 5.6 이다

[root@devDB:/root]#  rpm -qa | grep release

centos-release-5-6.el5.centos.1


http://dev.mysql.com/downloads/mysql/5.6.html#downloads 에서 다운받아 진행했다.

여기서 select platform 을 눌러보면 Source Code가 있는데 선택해서 맞는 OS 에 대해 다운 받으면 된다.

이지 참고를 하면 금방 찾을 수 있을 것이다.

이 후, 책을 참고 하여 진행 하였다.


다운 받은 파일을 압축을 풀어 압축 푼 폴더 내에서 build_target이라는 폴더를 생성한다.

mkdir -p /home/Setup/mysql-5.6.31/build_target


MySQL 5.5부터는 CMake를 이용한 Makefile를 생성 해야 한다.


다만 여기서 책과 다른 점은 SSL 이다. SSL 에 대한 정보는 다음과 같은데,

책에서는 0으로 설정했는데, bundled 로 나는 선택했다.


만약 여기서 에러가 생기면 해당 폴더(여기서는 /home/Setup/mysql-5.6.31/build_target) 를 다 삭제 후에 다시 진행해야한다.

또한 에러 찾기 위해서는 CMakeLists.txt 파일을 열어 찾아 보는 것을 추천한다. 나도 에런 코드 위치 찾아 보니 SSL 관련 된 내용이 있길레 변경을 한 후 진행하였다..


맨 마지막에 done 으로 뜨면 완료 된 것이다.

먼저, 자신이 설치할 위치에 디렉터리 권한 설정 및 가급적 폴더까지 생성하자.

mkdir -p /usr/local/rmysql/bin

mkdir -p /usr/local/rmysql/etc

mkdir -p /usr/local/rmysql/data

...


chown -R root /usr/local/rmysql

chown -R mysql /usr/local/rmysql/data

chown -R mysql /usr/local/rmysql/bin

chown -R mysql /usr/local/rmysql/etc

chgrp -R dba /usr/local/rmysql

...


*CMake 파일 생성 정보


$ cmake .. \

 '-DCMAKE_INSTALL_PREFIX=/usr/local/rmysql' \                //자기에 맞는 경로를 설정하자(mysql 엔진설치 위치)

 '-DWITH_COMMENT=Toto mysql standard x86_64' \

 '-DINSTALL_SBINDIR=/usr/local/rmysql/bin' \

 '-DINSTALL_BINDIR=/usr/local/rmysql/bin' \

 '-DINSTALL_LAYOUT=STANDALONE' \

 '-DMYSQL_DATADIR=/usr/local/rmysql/data' \               //데이터 위치

 '-DSYSCONFDIR=/usr/local/rmysql/etc' \

 '-DINSTALL_SCRIPDIR=/usr/local/rmysql/bin' \

 '-DWITH_INNOBASE_STORAGE_ENGINE=1' \

 '-DWITH_ARCHIVE_STORAGE_ENGINE=1' \

 '-DWITH_BLACKHOE_STORAGE_ENGINE=1' \

 '-DWITH_PERFSCHEMA_STORAGE_ENGIN=1' \

 '-DWITH_PARTITION_STORAGE_ENGINE=1' \

 '-DWITH_FEDERATED_STORAGE_ENGINE=1' \

 '-DENABLE_DEBUG_SYNC=0' \

 '-DENABLED_LOCAL_INFILE=1' \

 '-DENABLED_PROFILING=1' \

 '-DWITH_DEBUG=0' \

 '-DWITH_LIBWRAP=0' \

 '-DWITH_READLINE=1' \

 '-DWITH_SSL=bundled' \

 '-DCMAKE_BUILD_TYPE=RelWithDebInfo' \

 '-DCMAKE_C_FLAGS=-02' \

 '-DCMAKE_CXX_FLAGS=-02'



해당 속성 정보들은 아래와 같다

[펌] http://www.programkr.com/blog/MkjMxADMwYTz.html


  • 지정 설치 파일 설치 경로 때 자주 쓰는 옵션

-DCMAKE_INSTALL_PREFIX=/usr/local/mysql   # 지정 설치 경로 (기본 바로/usr/local/mysql)
-DMYSQL_DATADIR=/data/mysql               # mysql 데이터 파일 경로
-DSYSCONFDIR=/etc                         # 프로필 경로
  • 컴파일 과정 중에 다른 저장소 엔진 사용하기

-DWITH_INNOBASE_STORAGE_ENGINE=1          # INNOBASE 저장소 엔진 사용하기
-DWITH_ARCHIVE_STORAGE_ENGINE=1           # 자주 사용하는 로그 및 집계 분석 한다, 지원하지 않는 인덱스
-DWITH_BLACKHOLE_STORAGE_ENGINE=1         # 블랙홀 저장소 엔진
  • 컴파일 과정에서 일부 저장소 엔진, 취소

-DWITHOUT_<ENGINE>_STORAGE_ENGINE=1
예제 다음과 같다: 
-DWITHOUT_EXAMPLE_STORAGE_ENGINE=1
-DWITHOUT_FEDERATED_STORAGE_ENGINE=1
-DWITHOUT_PARTITION_STORAGE_ENGINE=1
  • 컴파일 들어갈 과정에서 기능 활성화 명령 소개

-DWITH_READLINE=1       # 지원 대량 가져오기 mysql 데이터
-DWITH_SSL=system       # mysql 지원 ssl 회화 구현 기반 ssl 데이터 회답하다
-DWITH_ZLIB=system      # 압축 라이브러리
-DWITH_LIBWRAP=0        # WRAP 실현할 수 있는지 기반 접근 제어
  • 기타 기능 명령

-DMYSQL_TCP_PORT=3306                   # 기본 포트
-DMYSQL_UNIX_ADDR=/tmp/mysql.sock       # 기본 소켓 파일 경로
-DENABLED_LOCAL_INFILE=1                # LOCAL_INFILE 기능 사용 여부
-DEXTRA_CHARSETS=all                    # 혹시 지원 추가 문자 집합
-DDEFAULT_CHARSET=utf8                  # 기본 인코딩 메커니즘
-DDEFAULT_COLLATION=utf8_general_ci     # 기본 언어 데이터 정렬
-DWITH_DEBUG=0                          # DEBUG 기능 설정
-DENABLE_PROFILING=1                    # 성능 처리 기능 사용 여부


이것까지 완료 되었다면, 이제 Install를 진행하자.


$ make && make install


이렇게 치면 다음과 같이 설치가 진행 되는 것을 확인할 수 있다.



옆에 %가 나와서 보기가 좋다.

에러가 난 적은 없지만..에러가 어떻게 하면 에러가 찾을 수 있을지는 나중으로........나도 찾아봐야 하기에;;;ㅠ


이제 DB를 생성해 보자.


vi /etc/RMy.cnf 라는 이름으로 생성해 보자.



반응형
반응형


추가로 확인된 부분이 있어서 정리해 보았다.


Federated 테이블 생성 시 마지막에 remote DB의 접속 정보를 작성하게 된다.


간단하게 앞에서 만들어 본 federated_table 에 대해서 script 를 확인해 보자

(보기 편하게 \G 를 붙여 세로로 보자)

mysql > show create table fedrated_table\G


아무리 회사 테스트DB정보이지만 모자이크...-_-



다른 부분은 전부 필요없고, 맨 하단에 이렇게 IP 정보와 포트 정보, 계정 정보 등이 모두 표기가 된다......(모자이크지만....)


그래서 분명 보안상에 문제가 될 수 있다.


그렇기에 다음과 같이 Server를 Syntax(synonym??) 으로 만들어 진행하면 보안적인 부분을 해결할 수 있다.


Syntax 만드는 구문이다.


CREATE SERVER server_name
    FOREIGN DATA WRAPPER wrapper_name
    OPTIONS (option [, option] ...)

option:
    { HOST character-literal
     | DATABASE character-literal
     | USER character-literal
     | PASSWORD character-literal
     | SOCKET character-literal
     | OWNER character-literal

     | PORT numeric-literal }

출처 : http://m.blog.naver.com/kwoncharlie/10151416351


이제 Syntax를 이용하여 만들어 보자.

CREATE SERVER tlogin

FOREIGN DATA WRAPPER mysql

OPTIONS (

USER '접속ID',

PASSWORD '비밀번호',

HOST '접속DB IP',

PORT 3306, 

#SOCKET '/usr/local/mysql3/mysql.sock',

DATABASE 'DB명');


여기서 port는 반드시 integer 로 작성


이후 Federated 테이블 생성


CREATE TABLE `federated_server` (

  `nID` int(11) NOT NULL auto_increment,

  `PlayerID` varchar(36) NOT NULL default '',

  `Password` varchar(32) NOT NULL default '',

...

) ENGINE=FEDERATED DEFAULT CHARSET=utf8 CONNECTION='tlogin/Player'

;


확인해 보쟈.


mysql > show create table fedrated_server\G



위와 같이 Connection 에서 tlogin/Player 으로 확인 가능하며, 정상적으로 select 도 될 수 있는 것을 확인할 수 있다.

보안적인 측면에서 효과를 볼 수 있다.




결론


이리 저리 DB에서 다른 원격지 DB의 정보를 가져 왔어 데이터를 쉽고, 프로그램 개발이 아닌 DB에서 해결하고자 하는 목적으로 진행을 해 보았다.

하지만 이 생각은 어디까지나 나의 욕심이고(DBA로써??아님 개발을 싫어하는 나로써??) 무식하면 용감하다고...

용감도 아닌 그냥 무식하고 무지해서 진행한 것이다.


여러 전문 DBA 고수분들은 비록 이런 기능이 있으나 사용하지 말라고 권고를 한다.

밑에 내용들만 조금만 깊게 생각해 보면 좋은 것 만은 아니라는 것을 알 수 있다.


1. 네트워크 성능에 따라 쿼리의 성능이 좌지우지.

2. 원격지의 Table에 DDL 작업이 발생 시 이것을 참조하는 Federated 테이블 에 대해서 재 생성

 - 혹시라도 federated 테이블을 모르는 경우 낭패..

3. 데이터 참조 시 해당 원격 DB 테이블을 모두 가져와야 하기에 처리량 증가 및 이에 따른 비용 증가


이 부분들은 여러 블로그를 조사하던 중에 참고한 내용을 기억나는 순으로 정리한 것이다.


가급적 사용하지 말자고 하는 것에서는 전부 이유가 있으니 가급적 사용안하는 방안으로 하고, 필요시 사용은 하되 엄격한 제한을 두고 사용하도록 해야겠다.


참조 : http://gywn.net/2014/12/let-me-introduce-federatedx/


반응형
반응형

Oracle 엔지니어겸 DBA 롤을 진행하다 보니 DB Link로 인해 종종 문제가 생긴 적이 많았다.


scn 이 Max치가 되어서 장애가 생긴적, 동일 DB 내에서 DB Link를 사용해서 불필요한 컨넥션 증가 등등...

어쩔때에는 db link 갯수가 한 DB내에 80개나 되는 경우도 있어서 당황한 적도 많았다.


그래서 DB Link만 생각해도 짜증나는 경우가 더 많았다.


하지만, MySQL DBA로 업무를 진행하다 보니 오히려 여기서는 DB Link 기능 없이 

Python 으로 일일이 개발해서 진행하는 경우가 허다하게 많았다.


처음부터 Python 개발을 했었다면 모르겠지만 Python을 익히면서 DBA 업무를 하다보니 괜히 개발이 더더더 싫어졌다.

(컴퓨터과에서 공부하며 DBA 가 되고 싶었지만 개발이 싫어서 DBA가 하고 싶은 부분도 상당히 많았기에...)


어쨋든 MySQL 에서도 Federated storage engine 이라는 것이 있어서 이 기술을 사용하면 db link 역활을 할 수 있다는 것을 찾아서 

이에 대해 간략한 설명과 테스트한 부분을 소개하고자 한다.



Federated Storage Engine 이란?


FEDERATED 스토리지 엔진을 사용하면 리플리케이션이나 클러스터 기술을 이용하지 않고도 원격의 MySQL 데이터베이스에 접근할 수 있다. 서비스 운영을 위해 여러 데이터베이스를 사용하는데, 한 데이터베이스의 데이터를 다른 데이터베이스들이 참조하여 사용할 때 활용할 수 있다. Oracle의 DBLink와 유사한 기능이라고 한다.


FEDERATED 엔진은 MySQL에 기본으로 설정되어 있지 않기 때문에 이를 사용하기 위해서는 별도의 작업이 필요하다. 검색을 해보면 처음에 설치할 때 부터 –with-federated-storage-engine 옵션을 주어야 한다는 등의 이야기 등이 있지만, 실제로는 재설치를 하거나 할 필요는 없다.

참조 : http://blog.weirdx.io/post/3503


그럼 나의 테스트 환경이다.

현재 회사 테스트 DB 장비와 나의 Local DB 를 연결하였다.


회사 테스트 DB는 5.0.37 이다



나의 local DB 는 5.7.12 이다

테스트는 개발장비의 데이터를 내 pc 에서 select 하고자 한다.


1. Engine 이 설치되어 있는지 확인해 본다. (Local DB)

 - 보통은 아래와 같이 Federated engine 이 NO 라고 되어 있는 것을 확인할 수 있다.



이 부분을 YES 로 변경해 보자.


my.cnf 또는 my.ini 파일에서 아래와 같이 추가해 보자


FEDERATED    만 추가한 후 저장하자.


(내 PC는 윈도우 이기에, my.ini 파일을 수정하였다.)



이 후, 적용을 위해서 DB를 재 기동 한다.


다시 Engine을 확인해 본다.




Federated가 YES 로 되어 있는 것을 확인 할 수 있다.


2. Engine을 이용하여 테이블을 추가해 보자.

 - Federated 엔진을 이용한 테이블은 쉽게 생각해 보면 원격 테이블을 가져와서 보여주는 것을 의미하며, 

   DML 작업을 진행하게 되면 원격지의 테이블 또한 변경이 되기에 굉장히 조심하자!!


* 원격지의 Table과 동일한 형태로 만들자

* 기본적으로 원격지 DB로 접속이 되는지 확인하자.(IP / Port 허용 여부)

* Oracle DB Link와 같이 원격지로 접근할 수 있도록 ID와 패스워드를 알아야 한다.


기본 문구는 아래와 같다.


CREATE TABLE `테이블명` (

  `컬럼1` 속성, 

  `컬럼2` 속성,

  `컬럼3` 속성

) ENGINE=FEDERATED DEFAULT CHARSET=utf8 CONNECTION='mysql://원격지접속ID:패스워드@원격지DB의IP:포트/DB명/테이블명'

;


ex) 

CREATE TABLE `federated_table` (
  `nID` int(11) NOT NULL auto_increment,
  `PlayerID` varchar(36) NOT NULL default '',
  `Password` varchar(32) NOT NULL default '',
  `Name` varchar(20) NOT NULL default ''
) ENGINE=FEDERATED DEFAULT CHARSET=utf8 CONNECTION='mysql://root:password@192.168.1.70:3306/TestDB/Player'
;


3. 이렇게 추가가 정상적으로 되면 테스트 진행해 보자.


local 에서 federated_table 를 select 해 보자.



워낙 데이터가 많아 이렇게 먼저 비교를 해 보았다.


이번에는 데이터 한건에 대해서 DML 작업을 진행해 보자.



이 한건을 이용하여 Remote에서 지워지면 local 에서는 어떻게 되는지 보자.



지워져서 검색이 안되는 것을 확인할 수 있다.


다시 Insert를 진행해 보자.


local 에서 보여 지는 것을 확인할 수 있다.


이번에는 반대로 테스트 해 보자.

Local 에서 작업을 진행하면 원격지 DB에서는 어떻게 진행되는지 Delete 작업 하나만 해 보자.



Local 에서 삭제 작업을 진행 하였더니, 원격지에서 삭제 된 것을 확인할 수 있다.


DB Link를 대신 할 수 있는 분명 좋은 기능인 것을 확인할 수 있다.


이 작업을 통해서 파이썬을 이용한 작업을 대체할 수 있지 않을까 생각해 본다.

원격지의 필요한 것에 대해서 미리 테이블을 생성한 후 프로시저 등으로 작업을 진행하고, 스케줄러를 통해 주기적으로 한다면,

crontab 에 걸어서 사용을 하고 등의 필요성이 줄어들고, 동시에 보안도 해결되지 않을까 고민해 본다.



추가로 아래 사이트를 참조해도 좋을 듯 싶다.


http://blackbull.tistory.com/31


[HELP FEDERATED ENGINE]
길다.. 생략..... -0-;;;;;;;

[FEDERATED ENGINE 설명]
Federated Engine은,
실제적으로 물리적인 저장공간을 확보하여 데이터를 복사해서 갖는 것이 아니다.
무슨 말이냐 하면... 흠...

마치 C에서의 포인터나, C++, Java에서의 참조형 처럼,
단순히 틀에 대한 정보만을 지니고, 
그것을 원거리에서 MySQL의 API를 이용하여 접근하는 방식이다.


그리하여, 물리적으로 격리되어 있는 다른 MySQL 서버의 테이블로의 접근이 
로컬 내에서 가능하게 만들어준다.
(5.0 에서는 MySQL 서버간에만 사용가능하며, 추후에 이기종 DB 와의 연계도 개발할꺼라는데...
언제나 될런지 -0-;;;)

물론, 
DBLink와 마찬가지로 물리적 거리감은 실행 속도의 하락과
DDL(Data Definition Language) 같은 쿼리는 적용 되지 않는 부효과를 발생시킨다.

그런 이유로, 
만약 로컬에서 DROP TABLE 명령어를 사용하여 테이블을 DROP 시킨다면,
로컬만 DROP되고, 원거리의 TABLE에는 영향을 미치지 않는다.



[FEDERATED ENGINE 사용방법]
먼저, 원본 테이블이 어딘가 존재해야 한다.

# 원본 테이블
USE testdb;
CREATE TABLE testtbl (
 id INT AUTO_INCREMENT PRIMARY KEY,
 col1 VARCHAR(100) DEFAULT NULL,
 col2 VARCHAR(200) DEFAULT NULL
) ENGINE=INNODB DEFAULT CHARSET=UTF8;


그리고 난 후에야, 로컬에 동일한 구조로 테이블을 생성한다.
단, 이번에는, 
****************************************************************************************
1. ENIGINE :  ENGINE=FEDERATED
2. CONNECTION 정보 : 'mysql://아이디:패스워드@IP또는DOMAIN:PORT/DB명/TABLE명'
****************************************************************************************

# FEDERATED 된 테이블
CREATE TABLE testtbl (
 id INT AUTO_INCREMENT PRIMARY KEY,
 col1 VARCHAR(100) DEFAULT NULL,
 col2 VARCHAR(200) DEFAULT NULL
) ENGINE=FEDERATED 
  DEFAULT CHARSET=UTF8
  CONNECTION='mysql://scott:tiger@192.168.10.2:3306/testdb/testtbl;


라고 만들면,
그 때부터 INSERT, DELETE, UPDATE, SELECT 가 가능해지며, 
INDEX를 사용할 수 있게 된다.

[FEDERATED 주의할 점]
1. 반드시 #원본테이블 처럼, 대상이 되는 테이블이 먼저 존재해야 한다.
2. Transaction은 지원되지 않는다.
3. #원본테이블 데이터를 제외한 필드명/타입/구조 등의 변경 사항은 
   #FEDERATED 된 테이블 적용이 되지 않기 때문에,
   수동으로 변경해 주어야 한다.
4. Query Cache는 이용할 수 없다.
5. !!!!! 중요 !!!!! SHOW CREATE TABLE #FEDERATED된 테이블
   입력시 #원본테이블로의 접속정보가 고스란히 노출 되므로, 주의가 요구된다.


반응형
반응형

[출처] http://blackbull.tistory.com/43

쿼리를 날리다 보면, 필요에 따라 그룹별로 순위를 매겨야 할 때가 있다.

이에 대해 오라클에서는 그러한 기능을 제공하는데,

아래가 바로 그 예이다.


[Oracle] 


SELECT empno, ename, job, sal, 

            ROW_NUMBER() OVER(PARTITION BY job ORDER BY sal) AS rnum 

FROM scott.emp;

<<결과>>

     EMPNO ENAME                JOB                       SAL       RNUM

---------- -------------------- ------------------ ---------- ----------

      7902 FORD                  ANALYST                    3000          1

      7788 SCOTT                ANALYST                   3000          2

      7369 SMITH                 CLERK                         800          1

      7900 JAMES                CLERK                         950          2

      7876 ADAMS                CLERK                       1100          3

      7934 MILLER                CLERK                       1300          4

      7782 CLARK                 MANAGER                  2450          1

      7698 BLAKE                 MANAGER                  2850          2

      7566 JONES                 MANAGER                  2975          3

      7839 KING                    PRESIDENT                5000          1

      7654 MARTIN               SALESMAN                 1250          1

      7521 WARD                  SALESMAN                 1250          2

      7844 TURNER               SALESMAN                 1500          3

      7499 ALLEN                 SALESMAN                 1600          4


14 개의 행이 선택되었습니다.




상기 쿼리는,

emp 테이블의 JOB을 기준으로 하여 그룹을 정하고 (PARTITION BY job),  -- 1

sal을 기준으로 하여 순위를 매겨(ORDER BY sal),

각각의 행에 ROW_NUMBER를 부여하겠다는 의미이다.                         -- 2


여기서 'PARTITION BY job'은 job별 정렬을 발생시킨다.

즉, 최종 결과물의 넘버링은 ORDER BY job, sal의 순으로 결과가 나오는 것이다.



[MySQL]

그런데, 불행하게도..... MySQL에는 저 기능이 없다.

그렇기 때문에 우리의 친구 꼼수(?)를 이용하여 저것을 구현해 내야 하는데.....


SELECT empno, ename, job, sal, rnum

FROM (

   SELECT a.*, 

           (CASE @vjob WHEN a.job THEN @rownum:=@rownum+1 ELSE @rownum:=1 END) rnum,

           (@vjob:=a.job) vjob

   FROM emp a, (SELECT @vjob:='', @rownum:=0 FROM DUAL) b

   ORDER BY a.job, a.sal                  

) c;

<<결과>>

+-------+--------+-----------+------+------+

| empno | ename  | job       | sal  | rnum |

+-------+--------+-----------+------+------+

|  7902 | FORD   | ANALYST   | 3000 |    1 |

|  7788 | SCOTT  | ANALYST   | 3000 |    2 |

|  7369 | SMITH  | CLERK     |  800 |    1 |

|  7900 | JAMES  | CLERK     |  950 |    2 |

|  7876 | ADAMS  | CLERK     | 1100 |    3 |

|  7934 | MILLER | CLERK     | 1300 |    4 |

|  7782 | CLARK  | MANAGER   | 2450 |    1 |

|  7698 | BLAKE  | MANAGER   | 2850 |    2 |

|  7566 | JONES  | MANAGER   | 2975 |    3 |

|  7839 | KING   | PRESIDENT | 5000 |    1 |

|  7654 | MARTIN | SALESMAN  | 1250 |    1 |

|  7521 | WARD   | SALESMAN  | 1250 |    2 |

|  7844 | TURNER | SALESMAN  | 1500 |    3 |

|  7499 | ALLEN  | SALESMAN  | 1600 |    4 |

+-------+--------+-----------+------+------+

14 rows in set (0.00 sec)


어때... 결과가 같아 보이는가? 


자, 그럼 쿼리를 뜯어보자.

여기서 궁금하게 생각되는 부분은 아래 3개의 쿼리라고 예상 된다.


1. (CASE @vjob WHEN a.job THEN @rownum:=@rownum+1 ELSE @rownum:=1 END) rnum,

--> 이전 job 필드와 동일한 그룹인가를 판별하고, 그룹에 따라 순번을 부여하기 위함이며,

      테이블에서 각각의 행을 읽을 때마다,

      변수 @vjob 값이 지금 새로 읽은 job 과 같다면 변수 @rownum을 1증가 시키고, 

      그렇지 않은 경우(@vjob이 현재 읽은 job값과 같지 않다면) @rownum을 1로 초기화 시킨다.


2. (@vjob:=a.job) as vjob

--> 테이블에서 각각의 행을 읽을 때마다,

      그룹 판별을 위해 현재 읽고 있는 행의 job값을 변수 @vjob에 입력


3. (SELECT @vjob:='', @rownum:=0 FROM DUAL) b

--> 원래는 쿼리를 수행하기 이전에, 

      SET @vjob:=0, @rownum:=0;  을 수행하여 변수를 초기화 해야 한다. 

      만약 해주지 않으면, NULL 값이 들어가게 된다.


      하지만 그럴 경우 쿼리가 2번 수행되어야 하기 때문에,

      하나의 쿼리로 만들기 위해서 이런 식의 서브 쿼리를 이용한 것이다.

      이 서브쿼리는 초기 테이블 확인시 1회만 수행되고,

      이후부터는 열람되지 않는다.

    

      !! 주의 !! 

      서브쿼리 안에서의 결과값만 가지고 현재의 결과값을 얻고자 할 때,

      변수가 되는 항목의 값을 동일한 자료형으로 맞춰주지 않으면, 

      정상적인 결과값이 나오지 않는다.

      가령 위의 예를 이용하자면, @vjob의 초기값을 @vjob:=0 으로 수행 하고

      서브쿼리만을 수행하면 정상적인 결과값이 나오지 않게 된다. 

      한 번 해보자~

       

이 3가지를 이해한다면 아마 이해할 수 있을 것이라 생각되지만,

한 가지 짚고 넘어가야 할 것이 있다. 


Q. 우리가 흔히 쓰는 SELECT 문장의 수행순서는 어떻게 될까?

무슨의미냐 하면..

위에서 사용한 것처럼 변수를 이용한 SELECT 내 연속적인 값의 할당은, 

수행결과에 영향을 미치게 되지 않을까? 

라는 질문이다.


흠.. 내가 말을 써놓고 난해하군..

예제를 보도록 하자.


<<예제>>

SET @val1=0, @val2=0;    #아까도 말했듯이 변수 초기화는 먼저 선행되어야 한다.

SELECT @val1:=@val1+1, @val2:=@val1+1, @val2:=0, @val1=@val2+1 

FROM DUAL;


자.... 당신이 예상하는 결과는?.....




<<쿼리 수행 결과>>

 +----------------+----------------+----------+---------------+

| @val1:=@val1+1 | @val2:=@val1+1 | @val2:=0 | @val1=@val2+1 |

+----------------+----------------+----------+---------------+

|                      1 |                     2 |             0 |                    1 |

+----------------+----------------+----------+---------------+

1 row in set (0.00 sec)


상기와 같이 SELECT 내 수행 결과는,

왼쪽에서 오른쪽으로 순차적인 수행이 이루어짐을 알 수 있다.


즉, @val1:=@val1+1 → @val2:=@val1+1 → @val2:=0  →  @val1=@val2+1 

로 수행 순서가 정해진다는 의미.


그러므로, 

변수를 이용한 SELECT를 이용할 때는 반드시 수행순서를 염두해 두고 쿼리를 작성하도록 하자.




PS : 오라클에는 예제 테이블이 있지만 MySQL 에는 없으니

       혹시 테스트 해보고 싶은 사람은 아래 쿼리를 수행해서 테스트 해보도록...


CREATE TABLE emp (

   empno INT,

   ename VARCHAR(30),

   job VARCHAR(30),

   sal INT

)ENGINE=INNODB DEFAULT CHAR SET=UTF8;


INSERT INTO emp

VALUES

(7902,'FORD','ANALYST',3000),

(7788,'SCOTT','ANALYST',3000),

(7369,'SMITH','CLERK',800),

(7900,'JAMES','CLERK',950),

(7876,'ADAMS','CLERK',1100),

(7934,'MILLER','CLERK',1300),

(7782,'CLARK','MANAGER',2450),

(7698,'BLAKE','MANAGER',2850),

(7566,'JONES','MANAGER',2975),

(7839,'KING','PRESIDENT',5000),

(7654,'MARTIN','SALESMAN',1250),

(7521,'WARD','SALESMAN',1250),

(7844,'TURNER','SALESMAN',1500),

(7499,'ALLEN','SALESMAN',1600);


반응형
반응형

공부하다가 오라클의 Trace 의 개념으로 예상되는 부분을 찾았다.

show engine innodb status

 -> InnoDB 에 대한 현재 상태를 확인할 수 있는 명령어

  • Semaphores
  • Transactions
  • File I/O
  • Insert Buffer and Adaptive Hash Index
  • Buffer pool and memory
  • Row operations
  • Lastest Detected deadlock
    • Deadlock 발생 시 해당 항목 확인
    • Transaction 정보를 표시
    • Waiting for this lock to be Granted (트랜잭션이 실행하기 위해 lock 을 걸어야 하는 데이터에 대한 정보, row에 대한 정보를 표시
    • Holds the lock(s) (현재 잡고있는 lock 의 정보)
  • Lastest Foreign key error

이론도 중요하지만 당장 테스트 해 보았다.


2개의 Session 에 대해서 lock 을 걸어 보았다.


※ MySQL 5.7 InnoDB on Windows 이며, 전부 Default 로 설정 하였다.

 

Lock 을 발생하기 위해서는 autocommit 을 반드시 off 인지 확인해 보자


mysql> show variables like '%commit%';

+-----------------------------------------+-------+

| Variable_name                           | Value |

+-----------------------------------------+-------+

| autocommit                              | OFF   |

| binlog_group_commit_sync_delay          | 0     |

| binlog_group_commit_sync_no_delay_count | 0     |

| binlog_order_commits                    | ON    |

| innodb_api_bk_commit_interval           | 5     |

| innodb_commit_concurrency               | 0     |

| innodb_flush_log_at_trx_commit          | 1     |

| slave_preserve_commit_order             | OFF   |

+-----------------------------------------+-------+

8 rows in set, 1 warning (0.00 sec)


양쪽 session 에서 동일한 데이터에 update 를 하여 Lock 을 발생 후 확인해 보자


1번 session

mysql> update test1 set txt='4' where id =1;

Query OK, 1 row affected (0.00 sec)

Rows matched: 1  Changed: 1  Warnings: 0


mysql> select * from employees.test1;

+----+-----+

| id | txt |

+----+-----+

|  1 | 4   |

|  2 | 2   |

|  3 | 3   |

|  5 | 5   |

|  6 | 7   |

+----+-----+

5 rows in set (0.00 sec)



2번 session

mysql> select * from test1;

+----+-----+

| id | txt |

+----+-----+

|  1 | 1   |

|  2 | 2   |

|  3 | 3   |

|  5 | 5   |

|  6 | 7   |

+----+-----+

5 rows in set (0.00 sec)


mysql> update test1 set txt='3' where id=1;

mysql> show engine innodb status \G;

*************************** 1. row ***************************

  Type: InnoDB

  Name:

Status:

=====================================

2016-06-02 15:41:56 0x2b84 INNODB MONITOR OUTPUT

=====================================

Per second averages calculated from the last 24 seconds

-----------------

BACKGROUND THREAD

-----------------

srv_master_thread loops: 2 srv_active, 0 srv_shutdown, 421 srv_idle

srv_master_thread log flush and writes: 423

----------

SEMAPHORES

----------

OS WAIT ARRAY INFO: reservation count 15

OS WAIT ARRAY INFO: signal count 14

RW-shared spins 0, rounds 54, OS waits 5

RW-excl spins 0, rounds 0, OS waits 0

RW-sx spins 0, rounds 0, OS waits 0

Spin rounds per wait: 54.00 RW-shared, 0.00 RW-excl, 0.00 RW-sx

------------

TRANSACTIONS

------------

Trx id counter 17161

Purge done for trx's n:o < 16713 undo n:o < 0 state: running but idle

History list length 60

LIST OF TRANSACTIONS FOR EACH SESSION:

---TRANSACTION 17160, ACTIVE 32 sec starting index read

mysql tables in use 1, locked 1

LOCK WAIT 2 lock struct(s), heap size 1136, 1 row lock(s)

MySQL thread id 2, OS thread handle 11152, query id 23 localhost ::1 root updating

update test1 set txt='3' where id=1

------- TRX HAS BEEN WAITING 32 SEC FOR THIS LOCK TO BE GRANTED:

RECORD LOCKS space id 80 page no 3 n bits 72 index PRIMARY of table `employees`.`test1` trx id 17160 lock_mode X locks rec but not gap waiting

Record lock, heap no 2 PHYSICAL RECORD: n_fields 4; compact format; info bits 0

 0: len 4; hex 80000001; asc     ;;

 1: len 6; hex 000000004307; asc     C ;;

 2: len 7; hex 270000022302e9; asc '   #  ;;

 3: len 1; hex 34; asc 4;;


------------------

---TRANSACTION 17159, ACTIVE 94 sec

2 lock struct(s), heap size 1136, 1 row lock(s), undo log entries 1

MySQL thread id 3, OS thread handle 11140, query id 24 localhost ::1 root starting

show engine innodb status

Trx read view will not see trx with id >= 17159, sees < 17159

--------

FILE I/O

--------

I/O thread 0 state: wait Windows aio (insert buffer thread)

I/O thread 1 state: wait Windows aio (log thread)

I/O thread 2 state: wait Windows aio (read thread)

I/O thread 3 state: wait Windows aio (read thread)

I/O thread 4 state: wait Windows aio (read thread)

I/O thread 5 state: wait Windows aio (read thread)

I/O thread 6 state: wait Windows aio (write thread)

I/O thread 7 state: wait Windows aio (write thread)

I/O thread 8 state: wait Windows aio (write thread)

I/O thread 9 state: wait Windows aio (write thread)

Pending normal aio reads: [0, 0, 0, 0] , aio writes: [0, 0, 0, 0] ,

 ibuf aio reads:, log i/o's:, sync i/o's:

Pending flushes (fsync) log: 0; buffer pool: 0

507 OS file reads, 64 OS file writes, 15 OS fsyncs

0.00 reads/s, 0 avg bytes/read, 0.00 writes/s, 0.00 fsyncs/s

-------------------------------------

INSERT BUFFER AND ADAPTIVE HASH INDEX

-------------------------------------

Ibuf: size 1, free list len 0, seg size 2, 0 merges

merged operations:

 insert 0, delete mark 0, delete 0

discarded operations:

 insert 0, delete mark 0, delete 0

Hash table size 34679, node heap has 0 buffer(s)

Hash table size 34679, node heap has 0 buffer(s)

Hash table size 34679, node heap has 0 buffer(s)

Hash table size 34679, node heap has 0 buffer(s)

Hash table size 34679, node heap has 0 buffer(s)

Hash table size 34679, node heap has 0 buffer(s)

Hash table size 34679, node heap has 0 buffer(s)

Hash table size 34679, node heap has 0 buffer(s)

0.00 hash searches/s, 0.00 non-hash searches/s

---

LOG

---

Log sequence number 345901173

Log flushed up to   345901173

Pages flushed up to 345901173

Last checkpoint at  345901164

0 pending log flushes, 0 pending chkp writes

15 log i/o's done, 0.00 log i/o's/second

----------------------

BUFFER POOL AND MEMORY

----------------------

Total large memory allocated 137297920

Dictionary memory allocated 673260

Buffer pool size   8192

Free buffers       7678

Database pages     514

Old database pages 209

Modified db pages  0

Pending reads      0

Pending writes: LRU 0, flush list 0, single page 0

Pages made young 0, not young 0

0.00 youngs/s, 0.00 non-youngs/s

Pages read 479, created 35, written 41

0.00 reads/s, 0.00 creates/s, 0.00 writes/s

Buffer pool hit rate 1000 / 1000, young-making rate 0 / 1000 not 0 / 1000

Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s

LRU len: 514, unzip_LRU len: 0

I/O sum[0]:cur[0], unzip sum[0]:cur[0]

--------------

ROW OPERATIONS

--------------

0 queries inside InnoDB, 0 queries in queue

1 read views open inside InnoDB

Process ID=4620, Main thread ID=10324, state: sleeping

Number of rows inserted 2, updated 1, deleted 0, read 26

0.00 inserts/s, 0.00 updates/s, 0.00 deletes/s, 0.00 reads/s

----------------------------

END OF INNODB MONITOR OUTPUT

============================


1 row in set (0.00 sec)


ERROR:

No query specified



이렇게 Lock 이 발생한 것을 확인 할 수 있고, 어떤 쿼리가 Lock 이 걸려 있는지 확인이 가능하다.


해당 쿼리에 대한 해석은 제대로 해야 될 듯 싶다.

그러면 튜닝 또는 장애 발생 시 정확한 원일을 알 수 있는 척도가 될 듯 싶다.


mysql> show engine innodb status \G;



[출처] http://dba-jadelee.blogspot.kr/2014/10/mysql-innodb-status-monitoring.html

MySQL INNODB Status Monitoring

-  해당 STATUS출력내역은 각 상세내역 설명을 위해 특정장비를 기준으로 특정시점의 결과를 샘플링  

세부적으로 설명한 부분도 있지만 직관적으로 상태정보를 보여주는 부분은 상세한 설명은 생략함. (이 부분들에 대한 추가적인 질문은 개인별로 문의하세요)

각 부분에 대한 간략한 기능적 설명이며 모니터링상 중요한 영역에 대한 코멘터리는 각 영역에 대한 설명 마지막 부분에 기재하였음.

-----------------------------------------------------------------


SQL> SHOW ENGINE INNODB STATUS\G


=====================================
131129 12:58:12 INNODB MONITOR OUTPUT      <--- 분석시점에 대한 출력시간
=====================================
Per second averages calculated from the last 30 seconds   <--- 분석에 대한 대상시간범위 (출력시간으로부터 *초 전까지의 범위)


----------------------------
BACKGROUND THREAD      
----------------------------               
InnoDB 메인 백그라운드 스레드의 작업실행 통계 각 숫자는 Innodb Engine이 시작된 이후부터의 카운트 값

srv_master_thread loops: 18613866 1_second, 18613864 sleeps, 1861347 10_second, 1755 background, 1739 flush
<--- 순서대로 초당 루프횟수 / 초당 슬립횟수 / 10초당 루프횟수 / User Transaction이 없는 유휴시간대 백그라운드 연산의 루프횟수플러시 루프횟수를 의미

srv_master_thread log flush and writes: 18683584
<--- 로그 메시지를 기록하고 플러시한 횟수를 의미 (Redo + Undo)


-----------------
SEMAPHORES              
-----------------          
InnoDB 내부 세마포어(뮤텍스나 리드/라이트 락 세마포어를 기다리는 스레드에 대한 정보)  통계 : 각 숫자는 Innodb Engine이 시작된 이후부터의 카운트 값이며 각 카운트 값이 갑자기 높은 시간대에 대해서는 DISK I/O 성능 및 InnoDB 엔진경합을 의심

OS WAIT ARRAY INFO: reservation count 1206724952, signal count 3304987791
<--- 전역 대기 배열정보 : 배열이 생성된 후에 셀을 예약한 횟수 / 객체가 시그널을 받은 횟수를 의미

Mutex spin waits 39567225498, rounds 173270862365, OS waits 330572230
<--- 뮤텍스 대기 스핀정보 : 뮤텍스를 기다리는 스핀락의 호출횟수 / 스핀루프의 반복횟수 / OS의 호출을 기다린 횟수를 의미

RW-shared spins 2945944901, rounds 37722913141, OS waits 601963335
<--- 공유 리드락 스핀정보 : 공유 리드락이 걸린 시간동안 스핀락이 기다린 횟수 / 스핀루프의 반복횟수 / OS의 호출을 기다린 횟수를 의미                       

RW-excl spins 684786004, rounds 12265810889, OS waits 163680647
<--- 배타적 라이트락 스핀정보 : 배타적 라이트락이 걸린 시간동안 스핀락이 기다린 횟수 / 스핀루프의 반복횟수 / OS의 호출을 기다린 횟수를 의미

Spin rounds per wait: 4.38 mutex, 12.81 RW-shared, 17.91 RW-excl
<--- 각 뮤텍스에 대해 OS의 호출을 기다리며 스핀루프가 기다린 횟수를 의미


------------------------------------
LATEST FOREIGN KEY ERROR 
------------------------------------            
최근 발생한 트랜잭션별 참조키 에러내역 히스토리 : 제약조건에 위배되는 내역에 대한 트랜잭션 및 쿼리 정보 확인

세부 에러 내역은 생략


------------------------------------
LATEST DETECTED DEADLOCK
------------------------------------           
최근 발생한 트랜잭션별 데드락내역 히스토리 : 가장 최근 발생한 트랜잭션간 데드락 이슈에 대한 처리내역 확인 (경합된 트랜잭션간의 내역 및 데드락 해결을 위한 트랜잭션 처리내역)

세부 데드락 내역은 생략


-------------------
TRANSACTIONS  
-------------------           
현재 트랜잭션별 락모니터링 정보 : 실제 수행되고 있는 트랜잭션별 작업내역에 대한 락 핸들링 정보 및 락 사용 세부쿼리 내역을 확인

세부 트랜잭션 내역은 생략


----------
FILE I/O    
----------         
다양한 I/O 연산에 대한 Innodb 스레드 실행내역 및 통계 : 내부적인 I/O 스레드의 현재 상태 및 전체 통계값 제공하며 만약 I/O상의 부하나 상태이상이 의심될 때 참조될 수 있음.

I/O thread 0 state: waiting for i/o request (insert buffer thread)
I/O thread 1 state: waiting for i/o request (log thread)
I/O thread 2 state: waiting for i/o request (read thread)
I/O thread 3 state: waiting for i/o request (read thread)
I/O thread 4 state: waiting for i/o request (read thread)
I/O thread 5 state: waiting for i/o request (read thread)
I/O thread 6 state: waiting for i/o request (write thread)
I/O thread 7 state: waiting for i/o request (write thread)
I/O thread 8 state: waiting for i/o request (write thread)
I/O thread 9 state: waiting for i/o request (write thread)
---> 현재 Innodb엔진이 사용중인 I/O 스레드들의 현재 상태 (각 스레드의 역할에 대한 명칭은 각 줄 끝 괄호내역 확인)

Pending normal aio reads: 0 [0, 0, 0, 0] , aio writes: 0 [0, 0, 0, 0] ,
 ibuf aio reads: 0, log i/o's: 0, sync i/o's: 0
Pending flushes (fsync) log: 0; buffer pool: 0
---> 대기하는 연산에 대한 정보  (aio 는 비동기 입출력을 의미)

21113936456 OS file reads, 7012874230 OS file writes, 5248276317 OS fsyncs
---> Innodb 엔진이 시작된 이후 전체통계 카운트

670.33 reads/s, 16384 avg bytes/read, 387.61 writes/s, 196.80 fsyncs/s
---> 마지막으로 통계를 출력한 이후의 전체통계 값에 대한 초단위 처리 카운트


------------------------------------------------------
INSERT BUFFER AND ADAPTIVE HASH INDEX      
------------------------------------------------------        
인서트 버퍼와 적응형 해시에 대한 정보 : 버퍼와 해시의 효율에 대한 통계정보 확인

Ibuf: size 38747, free list len 48247, seg size 86995, 183927359 merges
---> 페이지에 있는 삽입버퍼의 인덱스트리 사이즈 / 사용가능한 프리리스트의 길이 / 삽입버퍼트리와 헤더를 갖고 있는 파일에 할당된 페이지수 / 합병된 페이지수 를 의미

merged operations:
 insert 1037160756, delete mark 902047899, delete 260585507
---> 인덱스 페이지에 합병한 각 DML연산의 횟수를 의미

discarded operations:
 insert 110253, delete mark 5399, delete 2043
---> 테이블스페이스나 인덱스가 삭제되어 합병하지 않고 버려진 각 DML연산의 횟수를 의미

Hash table size 4425293, node heap has 1037 buffer(s)
---> 적응형 해시 인덱스 테이블에 있는 셀의 개수와 예약된 버퍼프레임의 개수를 의미

1057.94 hash searches/s, 1352.65 non-hash searches/s
---> 검색한 적응형 해시 인덱스 검색에 성공한 횟수 / 적응형 해시 인덱스를 사용할 수 없을때 B-tree 인덱스를 검색한 횟수를 의미


-------
LOG
-------
Innodb Log 활동에 대한 통계

Log sequence number 7804454235313     
---> 현재 로그 일련번호 (lsn)

Log flushed up to   7804454234099
---> 로그파일에서 플러시된 항목의 개수

Last checkpoint at  7804369829116
---> 가장 최근의 체크포인트된 lsn 정보

0 pending log writes, 0 pending chkp writes
4882215617 log i/o's done, 153.85 log i/o's/second

log switch 이슈와 관련
VIEW POINT : (Log flushed up to - Last checkpoint at) 값은 체크포인트가 얼마나 오래되었는지를 확인할 수 있음.
체크 포인트값이 (innodb_log_file_size * innodb_log_files_in_group) 77% 이상이 되지 않게 모니터링하고 유지되어야 함. (권장된 모니터링수치)
만약 이 비율에 가깝거나 큰 비율 값이 나온다면 Innodb엔진은 공격적으로 플러싱을 시도하게 되며 이로 인한 DB연산이 멈출 수 있음.


------------------------------------
BUFFER POOL AND MEMORY
------------------------------------            
Innodb 버퍼풀과 메모리 사용량에 대한 통계정보 : Innodb 버퍼에 대한 LRU 관리방법에 의한 할당내역 및 사용내역(byte단위)  버퍼사용율에 대한 세부통계수치 확인

Total memory allocated 2197815296; in additional pool allocated 0
---> 버퍼로 할당된 메모리 전체사이즈 / 추가풀에 할당된 메모리사이즈를 의미

Dictionary memory allocated 8409400
---> 데이터딕셔너리 테이블과 인덱스에 할당된 메모리사이즈를 의미

Buffer pool size   131072
Free buffers       21
---> 버퍼풀 사이즈를 페이지 단위로 알려주고 / 버퍼풀에 있는 해재된(free) 버퍼의 개수를 의미 : 프리버퍼가 없는 경우가 잦으면 버퍼사이즈를 늘려주는 것을 권장.

Database pages     130014
Old database pages 47973
Modified db pages  5660
---> 버퍼의 현재 LRU큐의 길이 / 과거 LRU큐의 길이 / 플러시해야할 페이지의 개수를 의미 : 기본적으로 Innodb는 버퍼풀을 LRU로 관리하며 할당과 해제에 대해서 페이지단위로 관리됨.

Pending reads 0
Pending writes: LRU 0, flush list 0, single page 0
---> 대기하고 있는 읽기연산의 개수 / LRU 관리방식에 따른 플러시하기 위해 대기하고 있는 페이지의 개수 / Buf_flush_list에서 플러시하기위해 기다리고 있는 페이지의 개수 / Buf_flush_single_page 리스트에서 플러시하기 위해 기다리고 있는 페이지의 개수를 의미

Pages made young 47722254403, not young 0
3258.74 youngs/s, 0.00 non-youngs/s
---> 최근에 접근한 페이지의 개수 / 최근에 접근하지 않은 페이지의 개수를 의미

Pages read 25295158134, created 95903681, written 2392506665
670.33 reads/s, 2.00 creates/s, 235.76 writes/s
---> 페이지의 읽기 연산 카운트 버퍼풀에 생성했지만 읽지않은 페이지의 개수 / 쓰기 연산 카운트를 의미

Buffer pool hit rate 997 / 1000, young-making rate 19 / 1000 not 0 / 1000
---> 획득한 버퍼풀 페이지 수와 비교하여 읽은 페이지 수의 비율 / 획득한 버퍼풀 페이지 수와 비교 및 접근한 페이지 수의 비율 / 획득한 버퍼풀 페이지 수와 비교 및 접근하지 않은 페이지 수의 비율을 의미

Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s
---> 미리 읽기 비율 / 접근하지 않아서 제거된 미리 읽기 페이지수의 비율 / 랜덤 미리 읽기 비율

LRU len: 130014, unzip_LRU len: 0
I/O sum[108645]:cur[907], unzip sum[0]:cur[0]


----------------------
ROW OPERATIONS
----------------------
메인 스레드의 행 연산에 대한 통계 정보

2 queries inside InnoDB, 0 queries in queue
5 read views open inside InnoDB
---> 현재 실행중인 쿼리 / innodb_thread_concurrency 큐에 있는 쿼리의 개수 / 읽은 뷰의 개수를 의미

Main thread process no. 16530, id 140226923562752, state: sleeping
---> 메인 스레드의 ID 및 상태정보를 의미 (첫번째 no. OS의 프로세스ID)

Number of rows inserted 3941662945, updated 2611269253, deleted 1052306048, read 11030869627676
39.96 inserts/s, 57.94 updates/s, 2.00 deletes/s, 908015.98 reads/s
---> innodb 엔진 시작된 이후의 각 DML연산의 총합계 카운트 / 초당 각 DML연산의 횟수 카운트를 의미

--------------------------------------------------------
END OF INNODB MONITOR OUTPUT

========================================================


[출처] http://intomysql.blogspot.kr/2010/12/innodb-lock.html

  • 우선 각 트랜잭션은 “TRANSACTION 999999 999999”으로 시작된다. (TRANSACTION ID는 8바이트 숫자 값인데, 이러한 형태의 8바이트 숫자 값은 모두 상위 4바이트와 하위 4바이트 두 영역으로 나뉘어서 출력된다.)
  • 그러므로, 여기 예제에서는 현재 3개의 트랜잭션 (09번 라인, 13번 라인, 23번 라인)이 존재한다는 것을 확인할 수 있다.
  • 각 TRANSACTION 라인에는 현재 해당 트랜잭션이 어떤 작업을 하고 있는지 어떤 상태인지를 알려 주는 키워드가 표시되며, 해당 트랜잭션이 몇 초 동안 진행 중인지도 보여 준다.- 09번 라인 : not started è 현재 트랜잭션이 진행 중이지 않음을 의미- 13번 라인 : ACTIVE è 현재 트랜잭션이 4초 동안 활성화된 상태임 (경우에 따라 문장 끝에 어떤 작업 중인지를 표시해 줌 : starting index read)- 23번 라인 : ACTIVE è 현재 트랜잭션이 17251초 동안 활성화된 상태임
  • 각각의 트랜잭션들은 “MySQL thread id”라는 항목(10, 16, 24번 라인)을 가지는데, 이 thread id값은 MySQL에서 “SHOW PROCESSLIST” 명령의 결과에 보여지는 “id”와 동일한 값을 가지게 된다. 즉, 현재 트랜잭션 목록에서 제일 밑 트랜잭션 (TRANSACTION 0 1809458)을 종료하고자 하면, “kill 4”명령으로 트랜잭션을 종료시킬 수 있다.
  • 13번 라인의 트랜잭션은 “TRX HAS BEEN WAITING 4 SEC FOR THIS LOCK TO BE GRANTED:”이라는 항목을 가지고 있는데, 이는 현재 트랜잭션 (TRANSACTION 0 1809460)이 다른 트랜잭션이 이미 점유한 Lock 때문에 Blocking(대기) 상태임을 표시하며, 어떤 Lock을 기다리고 있는지 상세히 보여 준다. 하지만, 안타깝게도 이 결과로는 어떤 트랜잭션이 그 Lock을 가지고 있는지는 알아낼 수 없다. 대략 짐작해 보건데 그 밑에 있는 트랜잭션이 지금 17251초 동안 트랜잭션이 ACTIVE 상태인 것으로 보아서 문제를 유발하고 있을 것이라는 것 정도는 짐작해 볼 수 있게 된다. 조금 더 자세히 확인하기 위해서는 “innodb_lock_monitor” 가 필요한데, 이 내용은 하단의 “InnoDB Lock monitoring” 을 참조 바란다.
  • 19번 라인을 보면, 레코드 Lock에 관련된 정보가 나오는데, 이것은 현재 트랜잭션이 대기 중인 레코드(인덱스)를 의미하며, 그 인덱스의 정보를 20, 21번 라인에 걸쳐서 보여 주고 있다. 19번 라인에는 지금 대기하고 있는 Lock이 Shared-lock인지 Exclusive-lock인지를 그리고 Gap까지 잠그고 있는지 아닌지를 보여 준다. 일반적으로 Gap lock이 아니라고 표현되는 경우를 제외하고는 거의 모두 Gap까지 잠그고 있다고 생각하면 될 듯 하다. (Gap을 잠그고 있는지 아닌지 판단하는 또 다른 방법은 “undo entries” 항목의 수가 “row lock(s)”의 수보다 작으면 대부분 Gap lock 으로 판단할 수 있다. 하지만 이 방법도 정확한 것은 아니다. 예를 들어서 한 트랜잭션에서 하나의 레코드만 계속 업데이트하게 되면 undo entries가 row locks보다 커질 수 있기 때문이다.)
  • 21번 라인을 보면, 드디어 이해 불가능의 단어들이 출력되는데, 사실 이 부분이 의외로 문제 해결에 도움이 될 수 있다. 우선 이 라인에 표시되는 내용은 인덱스 레코드의 필드 값들을 출력해서 보여 주는데, HEX값과 ASCII값을 동시에 보여 준다. 21번 라인의 내용을 정리해 보면 아래와 같이 잘라서 생각해 볼 수 있다. (구분자는 ;; 임)- 0: len 1; hex 70; asc p;;- 1: len 1; hex 02; asc  ;;- 2: len 4; hex 00000009; asc     ;;- 3: len 4; hex 00000000; asc     ;;각 라인은 [인덱스상에서의 번호 : 필드 길이 : 16진수 필드 값(HEX) : ASCII 필드 값(ASC) ] 포맷으로 구성되어 있다. 그리고 이 필드들의 개수는 20번 라인의 n_fields와 동일한 값을 가지게 된다.이 값들과 19번 라인의 테이블 및 인덱스 명을 이용하면 어느 테이블의 어떤 레코드를 지금 기다리고 있는지를 알아낼 수 있다.
  • "SHOW ENGINE INNODB STATUS"의 "DEADLOCK" 섹션에 출력되는 내용도 지금까지의 설명한 내용과 동일한 패턴으로 출력되므로, DEADLOCK 정보를 확인할 때에도 이와 같은 방식으로 해석할 수 있다.

지금까지 간단히 트랜잭션의 내용을 읽는 방법을 확인해 보았다.


InnoDB Lock monitoring
우선 InnoDB의 Lock을 모니터링 하기 위해서는 아래와 같은 테이블을 생성해야 한다.

Create table innodb_lock_monitor (fd1 int) engine=innodb;

  • 이 테이블을 생성하면, 매 몇 초 단위로 SHOW ENGINE INNODB STATUS 결과를 MySQL 에러 로그 파일에 기록하므로 모니터링이완료되면 테이블을 삭제 해주는 것이 좋다.
  • 위의 테이블을 생성 후, “SHOW ENGINE INNODB STATUS” 명령을 실행하면 아래와 같은 좀 더 상세한 트랜잭션 정보를 확인할 수 있다.
  • 어떤 트랜잭션이 다른 트랜잭션의 처리를 Blocking(막고) 하고 있는지를 판단하기 위해서는, 각 트랜잭션에서 가지고 있거나 또는 기다리고 있는 테이블 및 인덱스 그리고 그 인덱스 페이지 번호로 추적해 볼 수 있다. (물론 이 방법도 정확하게 찾을 수 있는 방법은 아니다. 하지만 더 자세히는 지금의 InnoDB 에서는 무리일지도 모른다)
  • 아래 빨간색으로 표기된 부분을 비교해 봄으로써 두 개의 트랜잭션이 서로 Racing 상태임을 확인할 수 있다. (동일 테이블의 동일 인덱스에서 같은 인덱스 페이지 번호 4를 참조하고 있음)

------------
TRANSACTIONS
------------
01 Trx id counter 0 1809465
02 Purge done for trx's n:o < 0 1809462 undo n:o < 0 0
03 History list length 22
04 LIST OF TRANSACTIONS FOR EACH SESSION:
05 ---TRANSACTION 0 0, not started, process no 5975, OS thread id 1099274560
06 MySQL thread id 10, query id 442 localhost root
07 show engine innodb status
08
09
10 ---TRANSACTION 0 1809464, ACTIVE 12 sec, process no 5975, OS thread id 1281038656 starting index read
11 mysql tables in use 1, locked 1
12 LOCK WAIT 2 lock struct(s), heap size 368, 1 row lock(s)
13 MySQL thread id 9, query id 400 localhost root Updating
14 update article set article_title='xx' where article_category=112 and article_type='general'
15 ------- TRX HAS BEEN WAITING 12 SEC FOR THIS LOCK TO BE GRANTED:
16 RECORD LOCKS space id 88 page no 4 n bits 88 index ix_category_type_id_user of table test.article trx id 0 1809464 lock_mode X locks rec but not gap waiting
17 Record lock, heap no 10 PHYSICAL RECORD: n_fields 4; compact format; info bits 0
18  0: len 1; hex 70; asc p;; 1: len 1; hex 02; asc  ;; 2: len 4; hex 00000009; asc     ;; 3: len 4; hex 00000000; asc     ;;
19
20 TABLE LOCK table test.article trx id 0 1809464 lock mode IX
21 RECORD LOCKS space id 88 page no 4 n bits 88 index ix_category_type_id_user of table test.article trx id 0 1809464 lock_mode X locks rec but not gap waiting
22 Record lock, heap no 10 PHYSICAL RECORD: n_fields 4; compact format; info bits 0
23  0: len 1; hex 70; asc p;; 1: len 1; hex 02; asc  ;; 2: len 4; hex 00000009; asc     ;; 3: len 4; hex 00000000; asc     ;;
24
25
26 ---TRANSACTION 0 1809463, ACTIVE 21 sec, process no 5975, OS thread id 1099008320
27 3 lock struct(s), heap size 368, 2 row lock(s), undo log entries 1
28 MySQL thread id 8, query id 355 localhost root
29 TABLE LOCK table test.article trx id 0 1809463 lock mode IX
20 RECORD LOCKS space id 88 page no 4 n bits 88 index ix_category_type_id_user of table test.article trx id 0 1809463 lock_mode X locks rec but not gap
21 Record lock, heap no 10 PHYSICAL RECORD: n_fields 4; compact format; info bits 0
22  0: len 1; hex 70; asc p;; 1: len 1; hex 02; asc  ;; 2: len 4; hex 00000009; asc     ;; 3: len 4; hex 00000000; asc     ;;
23
24 RECORD LOCKS space id 88 page no 6 n bits 80 index PRIMARY of table test.article trx id 0 1809463 lock_mode X locks rec but not gap
25 Record lock, heap no 11 PHYSICAL RECORD: n_fields 10; compact format; info bits 0

26  0: len 4; hex 00000009; asc     ;; 1: len 6; hex 0000001b9c37; asc      7;; 2: len 7; hex 000000002d1f51; asc     - Q;; 3: len 1; hex 70; asc p;; 4: len 2; hex 7878; asc xx;; 5: len 30; hex 3c703e3c7370616e207374796c653d22666f6e742d73697a653a20313470; asc

반응형
반응형

조언으로 항상 체크해 봐야겠다.

[출처] https://blog.lael.be/post/370#comments

*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

반응형

+ Recent posts