반응형


요즘 3.2 에서 5.6.32 로 Data Migration 이 가능하도록 개발 중이다.


그 중에 잠시 이슈를 정리하고 간다.


MySQL 5.6.32 를 설치한 곳에서 python 으로 


import MySQLdb;


를 하게 되면 아래와 같은 에러가 발생한다.


Traceback (most recent call last):

  File "<stdin>", line 1, in ?

  File "build/bdist.linux-x86_64/egg/MySQLdb/__init__.py", line 19, in ?

  File "build/bdist.linux-x86_64/egg/_mysql.py", line 7, in ?

  File "build/bdist.linux-x86_64/egg/_mysql.py", line 6, in __bootstrap__

ImportError: libmysqlclient.so.10: cannot open shared object file: No such file or directory


확인한 결과 

MySQL 설치된 곳 lib 폴더 내에 해당 libmysqlclient.so.10 파일이 없는 것을 확인 하였다.


검색하다 보니 여기저기 설정하라고 하는데 정작 설치가 되어 있지 않은 것 같아 rpm 으로 설치를 하였다.


아래 내용들은 잘못 되었다...하지만 참고하신 분들이 있을 것 같아서 삭제는 안하고 선을 그었다.



아래는 설정하라는 위치들이다.

/etc/profile

/etc/ld.so.conf


하지만 설치가 우선이라..


아래와 같이 다운 받은 후 설치 해 보자


[root@DBTEST02:/home/backup]# wget ftp://195.220.108.108/linux/Mandriva/official/9.1/i586/Mandrake/RPMS/libmysql10-3.23.55-1mdk.i586.rpm

[root@DBTEST02:/home/backup]# rpm -ivh libmysql10-3.23.55-1mdk.i586.rpm


이러면 설치는 되지만 여전히 import 할 수 없다.


그래서 해당 설치된 위치를 찾아보면 아래와 같다.


[root@DBTEST02:/home/backup]# find / -name libmysqlclient.so.10

/root/src/mysql-3.23.58/libmysql/.libs/libmysqlclient.so.10

/usr/lib/libmysqlclient.so.10


이제 /etc/profile 에 설정을 해 보자.

[root@DBTEST02:/home/backup]# vi /etc/profile


아래 내용을 추가 후 적용해 보자


LD_LIBRARY_PATH=/root/src/mysql-3.23.58/libmysql/.libs/

export LD_LIBRARY_PATH


[root@DBTEST02:/home/backup]# source /etc/profile


이러고 나면 제대로 되는 것을 확인할 수 있다.

하지만.......5.6에 접속이 되지 않는다.


Traceback (most recent call last):

  File "<stdin>", line 1, in ?

  File "build/bdist.linux-x86_64/egg/MySQLdb/__init__.py", line 74, in Connect

  File "build/bdist.linux-x86_64/egg/MySQLdb/connections.py", line 170, in __init__

_mysql_exceptions.OperationalError: (1251, 'Client does not support authentication protocol requested by server; consider upgrading MySQL client')



그렇구나.....다시 검색이 필요하다...



의외로 간단하게 풀었다.


/usr/local/mysql/lib 폴더 내의 libmysqlclient.so.18 을 soft link를 걸어서 생성해 주었다.


[root@DBTEST02:/usr/local/mysql/lib]# ln -s libmysqlclient.so.18.1.0 libmysqlclient.so.10

[root@DBTEST02:/usr/local/mysql/lib]# chown mysql:mysql libmysqlclient.so.10



이후에 session 을 끊은 후 다시 접속하여 확인해 보니 쉽게 통과 했다.


하지만.....다른 에러를 지금 풀고있다.

위의 내용은 쓸데없이 다른 환경설정 건들지 말고 soft link(symbolic link) 로 생성해 주면 된다.


해당 내용은 일단 끝!




추가로 환경설정이 필요해서 추가합니다.


1. cd /etc/ld.so.conf


2. vi로 아무 파일명 생성하여 아래 내용 추가

/usr/local/mysql/lib


ldconfig 를 쳐서 적용


3. vi ~/.bash_profile 

LD_LIBRARY_PATH=/usr/local/mysql/lib

export LD_LIBRARY_PATH


이후 source ~/.bash_profile


한 후 혹시나 하는 마음에 해당 세션 종료 후 다시 접속 후 확인


반응형
반응형
SP에 대해서 아직 무지하다.

[펌] http://cafe.naver.com/mysqlpg (MySQL Power Group - 네이버까페)



MySQL 스토어드 프로그램은

MySQL의 스토어드 프로그램(이 글에서 스토어드 프로그램은 Stored procedure와 Stored Function에 한함)은 MySQL 5.0버전부터 지원되기 시작했다. MySQL 5.0의 첫번째 릴리즈 버전이 2005년도 10월에 출시되었으니, MySQL에 프로시져가 도입된지 벌써 10년 정도의 시간이 지나가고 있지만 실제로 MySQL에서 프로시져의 인기는 그다지 높지 않다.
요즘은 MSSQL이나 Oracle에 익숙한 사용자(개발자와 DBA 모두)들이 MySQL을 배우거나 사용하고자 하는 경우가 많이 늘어나고 있는데, 많은 사용자들이 MySQL 만의 특징에 익숙치 않아서 혼란스러워하는 경우를 많이 보았다.
특히나 MySQL은 주로 Web 기반의 서비스에서 사용되다 보니, MSSQL이나 Oracle과 같은 RDBMS에서 효율적으로 제공하는 기능들이 MySQL에서는 그렇지 못한 것들이 자주 있다. 물론 때로는 그 반대인 경우도 흔히 볼 수 있다.
그중에서 가장 많은 이슈가 되고 있는 스토어드 프로그램의 특징을 간단히 살펴보고, 왜 MySQL에서는 Oracle이나 MSSQL에서와 같이 스토어드 프로그램을 활용할 수 없는지를 소개해보고자 한다.


스토어드 프로그램의 컴파일

다른 상용의 RDBMS에서와 같이 MySQL 서버에서도 스토어드 프로그램은 컴파일 과정을 거치게 된다. 물론 C/C++과 같이 물리적인 CPU가 직접 해석할 수 있는 이진 코드가 만들어지는 것은 아니지만, Java와 같이 어떤 형태의 목적 코드(Java의 바이트 코드와 같은)가 만들어지고
이 목적 코드는 메모리상에 저장되어서 나중에 재실행 요청시에는 준비된 바이트 코드가 실행된다. 즉 스토어드 프로그램의 소스 코드가 매번 실행될 때마다 파싱되고 분석되어서 실행되는 것이 아니란 것을 의미한다.

간단히 아래와 같은 프로시져를 생각해보자.

CREATE PROCEDURE sp_test(p CHAR(16))
BEGIN
    DECLARE x INT;
    SET x = 3;
    WHILE x > 0 DO
        SET x = x-1;
        INSERT INTO tab_test VALUES (x, p);
    END WHILE;
END

위의 프로시져가 컴파일되면, 아래와 같은 목적 코드가 만들어지게 된다.
목적 코드에서는 단순히 스토어드 프로그램의 코드에서 SET 이나 WHILE과 같은 문장들을 sp_instr_set이나 sp_instr_jump 등과 같은 인스트럭션으로 변환된 형태로 관리하게 된다.
여기에서 한 가지 기억해야 할 것은 컴파일된 스토어드 프로그램의 목적 코드에서 SQL 문장은 그대로 문자열로 남아있게 된다는 것이다. 즉 MySQL의 스토어드 프로그램은 컴파일이 되어도 내부에 사용된 SQL 문장들을 바로 실행할 수 있는 실행 계획이나 Parsed-Tree 형태로 관리하는 것이 아니란 것을 의미한다.

---------+-----------------------------------------------------
Position | Instruction
---------+-----------------------------------------------------
 0       | sp_instr_set(1, '3')
 1       | sp_instr_jump_if_not(5, 'x>0')
 2       | sp_instr_set(1, 'x-1')
 3       | sp_instr_stmt('INSERT INTO tab_test VALUES (x, p)')
 4       | sp_instr_jump(1)
 5       | <end>
---------+-----------------------------------------------------

스토어드 프로그램 캐시

Oracle이나 MSSQL의 스토어드 프로그램은 전역의 스토어드 프로그램 캐시 공간(Memory)에 관리된다. 물론 MySQL 서버의 스토어드 프로그램도 컴파일되면 스토어드 프로그램 캐시(소스 코드에서는 이를 sp_cache라고 함)에 관리한다.
하지만 MySQL의 스토어드 프로그램 캐시는 전역이 아니라 Thread 단위로 관리된다. 여기서 Thread라 함은 사실은 Connection 기반으로 관리됨을 의미한다. 만약 Thread pool을 사용한다 하더라도, 실제 Linux의 Thread 단위가 아니라 Connection 단위의 메모리 공간(THD)에 관리되는 것이다.

큰 차이가 아닌 것 같지만, 사실 스토어드 프로그램 캐시가 전역이나 세션(로컬) 단위냐에 따라서 장단점은 크게 달라진다.


  • 전역 스토어드 프로그램 캐시
    •  장점 : 메모리 절약, 스토어드 프로그램의 컴파일과 최적화 회수가 적음
    •  단점 : 여러 클라이언트가 동시에 컴파일된 스토어드 프로그램을 참조하므로 동기화 비용이 필요하며, Re-Enterant와 Thread-safe한 데이터 구조체 및 구현 필요(뒷 부분은 사실 운영이 아니라 구현상의 이슈이므로, 사용자인 우리에게는 별로 중요하지 않음)
  • 로컬 스토어드 프로그램 캐시
    • 장점 : 클라이언트간의 공유 이슈가 없으므로 잠금이 없고 빠른 처리 가능, 구현이 쉬움
    • 단점 : 많은 메모리 공간이 필요하고, 클라이언트 컨넥션 단위로 스토어드 프로그램의 컴파일 필요



MySQL의 스토어드 프로그램 캐시 공간은 Connection 단위로 관리된다는 것은 컨넥션이 새로 생성되면 필요한 모든 프로시져의 컴파일이 필요하다는 것을 의미한다.
만약 Connection pool이나 PHP의 Persistent-connection을 사용하지 못하고 매번 Connection을 생성해야 하는 경우라면, 매번 스토어드 프로그램이 실행될 때마다 스토어드 프로그램을 (mysql.proc 테이블에서) 읽어서 컴파일을 해야 하므로 최악의 성능을 내게 될 것이다.
그렇다고 Connection pool이나 Persistent-Connection 환경이라고 안전한 것은 아니다. 많은 스토어드 프로그램이 사용되는 서비스에서 MySQL 서버에 연결된 컨넥션이 10000개라고 가정하면 엄청난 메모리 공간이 필요하게 될 것이다.
하지만 성능 향상을 고려한다면, 스토어드 프로그램 캐시 메모리 공간을 적게 설정할 수도 없는 진퇴양난의 상황에 빠지게 될 수도 있다.

스토어드 프로그램의 무효화

MySQL 서버의 스토어드 프로그램 캐시 공간은 컨넥션간 서로 공유되는 전역 공간이 아니라, 컨넥션 단위로 관리된다는 것을 앞에서 살펴보았다.
사실 스토어드 프로그램 캐시가 컨넥션 단위로 관리되기 때문에 발생하는 문제점이 또 있는데, ALTER나 CRETE 등과 같은 DDL을 이용해서 스토어드 프로그램의 코드를 변경하는 경우이다.
만약 컨넥션이 10000개가 만들어져서 각각의 컨넥션에서 sp_test라는 프로시져를 사용하고 있다고 가정해보자. 이때 DBA가 ALTER PROCEDURE나 DROP PROCEDURE + CREATE PROCEDURE를 실행했다고 가정해보자.
그럼 어떤 현상이 발생하게 될까 ?

프로시져를 변경하는 컨넥션에서는 단순히 해당 프로시져의 정보를 mysql DB에 있는 proc 테이블에 변경 저장하고, 해당 프로시져의 버전을 1 증가시키고 완료된다. 이때 해당 프로시져의 버전은 글로벌하게 전역 메모리 공간에 관리된다.
그리고 모든 서비스 컨넥션에서는 프로시져를 실행하기 전에 항상 로컬 스토어드 프로그램 캐시에 괸리되는 프로시져의 버전과 전역 공간의 프로시져 버전을 확인해서, 로컬 스토어드 프로그램 캐시의 버전이 낮으면 로컬 스토어드 프로그램 캐시에 저장되어 있던 컴파일된 목적 코드를 버리고 다시 컴파일을 수행한다.
이렇게 컴파일이 완료되면, 비로소 해당 프로시져를 실행할 수 있게 되는 것이다.

그나마 다행인 것은, 변경된 프로시져가 자주 실행되지 않는다면 모든 컨넥션이 한번에 동일 스토어드 프로그램을 컴파일하기 위해서 상당한 시간을 소모하지 않을 것이다. 하지만 스토어드 프로그램이 아주 빈번하게 모든 컨넥션에서 활용된다면 어떤 상황이 발생하게 될까 ?
이런 경우라면 일부러 사용량이 별로 없는 새벽 시간에 스토어드 프로그램을 배포해야 할 지도 모르겠다.

(참고로, Oracle의 MySQL 개발팀에서는 Production MySQL 서버에서 스토어드 프로그램을 갱신하는 것은 상당히 드문 케이스이며, 별로 심각하게 고려되지 않는 상황이라고 소개하고 있다. ㅠㅠ)있다

메모리 부족 예방

MySQL 서버의 스토어드 프로그램은 컨넥션 단위로 로컬 캐시 영역에 관리되기 때문에, 컨넥션이 많고 사용되는 스토어드 프로그램이 많다면 많은 메모리 공간이 필요할 것이다. 때로는 메모리 부족 현상으로 운영 체제가 MySQL 서버를 강제 종료시킬 수도 있다.
여기에서 스토어드 프로그램의 개수가 많고 적음은 상대적이며, Production MySQL 서버에 장착된 메모리 크기와 여러가지 상황에 따라서 의존적이므로 각 DBA가 적절하게 판단해야 할 것으로 보인다.

MySQL 서버에서는 이런 메모리 과다 사용을 막기 위해서 MySQL 5.5부터 stored_program_cache라는 시스템 변수를 제공하고 있다. 이 변수는 기본 값이 256이며, 설정하는 값의 의미는 스토어드 프로그램 캐시에 저장할 스토어드 프로그램의 개수이다.
스토어드 프로그램 하나 하나의 크기에 의해서도 메모리 사용량이 많이 좌우될 것으로 보이므로, 사실 256이라는 수치가 적절한지 큰 값인지는 판단하기 쉽지 않아 보인다.

만약 스토어드 프로그램 캐시에 저장된 스토어드 프로그램의 개수가 256을 넘게 되면, MySQL 서버는 현재 컨넥션의 스토어드 프로그램 캐시 내용을 모두 무효화시키고 다시 스토어드 프로그램을 하나씩 컴파일해서 저장하게 된다.
물론 스토어드 프로그램이 256개 이상이고 순서대로 하나씩 사용된다면, 위의 무효화 -> 컴파일 과정을 계속 반복하게 될 것이다.

정리하면...

MySQL 스토어드 프로그램의 내부적인 처리 방식을 간단히 살펴보았는데, MySQL의 스토어드 프로그램을 Oracle이나 MSSQL의 그것과 동일하게 생각해서는 안되는 이유를 간략히 정리해보면...
1) 스토어드 프로그램 자체는 컴파일되어서 목적 코드로 관리되지만, 내부의 SQL문장을 파스된 형태(실행계획이나 Parsed-Tree 형태)로 관리하지 않는다.
2) 컴파일된 스토어드 프로그램 목적 코드는 각 컨넥션 단위로 관리되기 때문에 Oracle이나 MSSQL보다 많은 메모리 공간이 필요하다.
3) 스토어드 프로그램이 변경될 때마다, 모든 컨넥션에서 기존 목적 코드의 무효화 및 신규 프로시져의 컴파일 과정일 필요하다.

또한 MySQL은 Web 기반의 단순 쿼리를 고속으로 처리해주는 용도로 많이 활용된다. 그래서 Facebook이나 Twitter 등의 SNS 회사들은 WebScaleSQL이라는 목표로 MySQL 코드 패치를 수행하고 있기도 하다.
이런 방향성으로 본다면, 스토어드 프로그램과 같은 복잡한 절차적 코드(Compound-statement block)를 확장이 어려운 MySQL 서버에 둔다는 것은 적절치 않을 수 있다.
Oracle이나 MSSQL에서는 모든 처리를 DBMS 서버로 집중화하고 서버를 통합(Consolidation) 것이 목표였다면, MySQL의 목표는 그 반대로 볼 수 있다. MySQL은 라이센스 비용이 없으니깐 말이다.
물론 라이센스 비용 이야기는 어떤 형태의 기술 지원을 받는냐에 따라 이야기가 달라지겠지만, 그래도 Oracle이나 MSSQL의 라이센스 비용에 비할바는 아닐 것이다.


<<그렇다고 MySQL의 스토어드 프로그램은 사용해서는 안될 물건이라고 생각하지는 말자. 어디까지나 목적에 맞게 기능들을 잘 활용하자는 수준으로 해석할 것을 당부드린다.>>

블로그에 올렸던 글인데, 못 보신분들이 있을까봐

카페에 재탕합니다. (원본 URL은 http://kakaodbe.blogspot.kr/2014/10/mysql.html)



반응형
반응형

꾸준히 공부한 것에 다시 정리하면서 보니 이해 했던 부분은 더 쉽게 머리에 남고..

헷갈렸고 몰랐던 부분에 대해서는 정리가 되는 것 같다.


오늘은 Transaction 에 대한 isolation 정리다.



※ isolation level (Transaction 격리 수준)

- 동시에 여러 트랜잭션이 처리될 때, 특정 트랜잭션이 다른 트랜잭션에서 변경하거나 조회하는 데이터를 볼 수 있도록 허용할지 말지를 결정

- 격리 수준 :  " Read Uncommitted", "Read Committed", "Repeatable Read", "Serializable"

- 격리 수준이 높아질수록 동시성도 떨어지는 것이 일반적이라고 볼 수 있다.


  1. Read Uncommitted (Dirty Read)
    • 일반적으로 거의 사용하지 않음
    • 변경 내용이 Commit이나 Rollback 여부에 상관 없이 다른 트랜잭션에서 보여짐
    • 어떤 트랜잭션에서 처리한 작업이 완료되지 않았는데도 다른 트랜잭션에서 볼 수 있게 되는 현상
    • Dirty Read를 유발하는 Read Uncommitted는 RDBMS 표준에서는 트랜잭션의 격리 수준으로 인정하지 않을 정도로 정합성에 문제가 많은 격리 수준
    • Dirty read, Non-repeatable read, Phantom read 현상 발생
  2. Read Committed
    • Oracle DBMS에서 기본적으로 사용되고 있는 격리 수준(Shared Lock을 사용)
    • 온라인 서비스에서 가장 많이 선택되는 격리 수준으로 데이터를 변경했더라도 Commit이 완료된 데이터만 다른 트랜잭션에서 조회할 수 있음
    • 어떤 트랜잭션에서 처리한 작업이 commit 이 안되어 있다면 다른 트랜잭션은  undo 영역에 있는 기존 값을 참고하여 보여 주게 됨
    • Read Committed 격리 수준에서도 "Non-Repeatable Read"("Repeatable Read" 가 불가능) 라는 부정합 문제 발생 (Phantom read 현상 발생)
※ Repeatable Read : 하나의 트랜잭션 내에서 동일 select 쿼리를 실행했을 때는 항상 같은 결과를 가져와야 한다는 정합성 정의 (select를 한 후 재차 하는 사이에 데이터 변경이 있어서 동일 select 했을 때 다른 결과가 나오는 것을 뜻함)
이것이 문제될 수 있는 상황은 하나의 트랜잭션에서 동일 데이터를 여러 번 읽고 변경하는 작업이 금전적인 처리와 연결된다면 문제가 발생할 수 있음.(ex-돈을 인출하는 과정에서 돈이 있어서 인출하려는 사이에 금액에 변경이 생겼(다른 곳에서 돈이 인출됨)는데 인출이 가능한 경우 문제가 발생)

 3. Repeatable Read

      • MySQL InnoDB의 기본적으로 사용되는 격리 수준(모든 데이터에 shared lock 이 걸리므로 다른 사용자는 그 영역에 해당되는 데이터에 대한 수정이 불가능)
      • Binary Log를 가진 MySQL 의 장비에서는 최소 Repeatable read 격리 수준 이상을 사용해야 함
      • Non-Repeatable Read 부정합이 발생하지 않음
      • InnoDB Storage Engine은 트랜잭션이 Rollback 될 가능성에 대비해 변경되기 전 레코드를 언두(Undo) 공간에 백업해두고 실제 레코드 값을 변경(MVCC-Multi Version Concurrency Control)
      • MVCC를 위해 언두 영역에 백업된 이전 데이터를 이용해 동일 Transaction 내에서는 동일한 결과를 보여줄 수 있도록 보장(Read committed 도 commit 되기 전 데이터를 보여줌)
      • Repeatable Read 와 Read Committed 의 차이는 언두 영역에 백업된 레코드의 여러 버전 가운데 몇 번째 이전 버전까지 찾아 들어가야 하는지에 있음. -> 언두영역에서 특정 트랜잭션 번호의 구간 내에서 백업된 데이터를 보여 줌, 하지만 트랜직션을 종료하지 않으면 무한정으로 언두 영역이 커질 수 있으며 이로 인해 MySQL 성능이 떨어질 수 있음
      • select .. for update 쿼리의 경우 다른 트랜잭션에서 수행한 변경 작업에 의해 레코드가 보였다가 안보였다가 할 수 있다. 이것을 Phantom Read(Phantom row)라고 표현 -> Undo 영역을 lock 할 수 없기 때문에 변경 전 데이터가 아닌 현재 변경된 레코드를 표현 

※ MVCC(Multi Version Concurrency Control)

: 잠금을 사용하지 않는 일관된 읽기를 제공하는 것이 목적


이해를 하기 위해 다시 정리

- Read Uncommitted : 변경되었거나 안된 데이터의 값을 읽음  

- Read Committed(그 이상의 경리 수준 - Repeatable_Read, Serializable) : 변경 전 Undo에 있는 값을 읽음 


-> 이러한 과정을 MVCC라고 표현


 4. Serializable

      • 동시성이 중요한 DB에서는 거의 사용하지 않음
      • 읽기 작업도 공유 잠금(읽기 잠금)을 획득해야만 하며, 동시에 다른 트랜잭션은 그러한 레코드를 변경하지 못하게 됨
      • 한 트랜잭션에서 읽고 쓰는 레코드를 다른 트랜잭션에서는 절대 접근할 수 없음
      • 일반적 DBMS에서 일어나는 "Phantom Read" 라는 문제가 발생하지 않지만 InnoDB 스토리지 엔진에서는 Repeatable Read 격리 수준에서도 이미 Phantom Read가 발생하지 않으므로 굳이 Serializable를 사용할 필요성 없음


 5. Repeatable Read 격리 수준과 Read Committed 격리 수준의 성능 비교

      • Repeatable Read가 Transaction 을 열어 해당 Transaction 에서 모든 테이블의 데이터를 Select 를 한 후 그대로 놔두면 InnoDB의 Undo 영역이 계속 커져서 시스템 테이블스페이스 I/O가 유발되는 경우가 대표적인 예..(성능을 떨어트리는 이유)
      • 해당 문제를 일으키지 않으면 성능상 차이가 거의 없음. Binary log가 활성화된 MySQL 서버에서는 Read Committed 격리 수준을 사용할 수 없음



사실 이해하기 위해서는 Transaction 이 일어나는 현상을 이미지로 설명한 것을 읽으면 빠를 듯 싶다.


또한 MySQL(Repeatable Read) 과 Oracle(Read Committed) 은 격리수준이 다르다.

반응형
반응형

현재 MySQL 3.28 로 구성된 Slave를 MySQL 5.6.31 로 업그레이드 작업을 진행 중이다.


Master의 경우 작업 당일날 진행할 예정이고,

Slave의 경우 백업 데이터가 일 단위로 구성되고 있기에 이 부분에 대해 먼저 다른 장비로 Migration 을 진행 하기로 했다.


매일 Dump를 이용해서 작업 하기에는 지루한 작업이 될 것이다.


그래서 매일 백업 데이터를 dump 받아 scp 로 전송 후 다른 장비에서 해당 dump를 이용한 load 및 데이터 건수 비교하는 스크립트를 개발 중이다.


그 중 scp 로 전송하는 방법이 잠시 막혀 공유하고자 한다.





scp 를 이용하는 방법으로 검색하면 twisted ? paramiko_scp ? pexpect ? 등을 설치해서 간편하게 사용하는 방법이 있지만..

가급적이면 설치 등은 피하는 방법을 사용하다 보니

어쩔 수 없이 os.system 이라는 명령문을 사용할 수 밖에 없었다.


os.system은 os에서 사용하는 shell 명령문을 사용 가능하도록 하는 문장인데...

scp 를 하게 되면 먹통이 되는 현상을 확인할 수 있었다.


또한 간단하게 테스트하기 위해서 python에서 직접 os.system 사용하여 scp 를 날리면 비밀번호를 묻는 것을 확인할 수 있었다.

(비밀 번호 묻는게 당연한 이야기 이겠지만....)


여러가지 확인중 ssh 를 이용하여 비밀번호 묻지 않고 바로 전송 하는 방법으로 진행 했더니 정상적으로 전송이 되는 것을 확인할 수 있었다.


아래는 간단하게 ssh 암호 묻지 않는 방법에 대해서 공유해 본다.

(물론 암호 관련하여서는 가급적이면 하지 않는 방법이 최선이며, 꼭 해야 된다면 내부망에서만 가능하도록 하자)



- SSH 암호 없이 접속하는 방법


접속시도하는 (source OS) 에서 

1. ssh key 생성 

 $ ssh-keygen -t rsa

 - /root/.ssh/ 아래에 암호 관련된 파일이 생성


2. ssh 복사

 - /root/.ssh/id_rsa.pub 파일을 cat 으로 열어 내용을 복사


3. Target OS에서 authorized_keys 생성

 - Target (접속하는 서버) 의 /root/.ssh/authorized_keys 파일을 만들어 내용 복사 진행


4. 테스트 진행

 - source OS에서 target OS 으로 접속 진행 테스트

 - port는 1004 라고 가정

 $ ssh -p 1004 192.168.0.2  


암호없이 접속 되면 성공





어쩌면 이것도 ETL 의 개념???과 비슷하지 않을까 싶다. ㅎㅎㅎㅎㅎㅎㅎㅎ


ETL 이란 ? [출처] 위키백과 https://ko.wikipedia.org/wiki/%EC%B6%94%EC%B6%9C,_%EB%B3%80%ED%99%98,_%EC%A0%81%EC%9E%AC

 추출, 변환, 적재(Extract, transform, load, ETL)는 컴퓨팅에서 데이터베이스 이용의 한 과정으로 특히 데이터 웨어하우스에서 다음을 아우른다:


동일 기종 또는 타기종의 데이터 소스로부터 데이터를 추출한다.

조회 또는 분석을 목적으로 적절한 포맷이나 구조로 데이터를 저장하기 위해 데이터를 변환한다.

최종 대상(데이터베이스, 특히 운영 데이터 스토어, 데이터 마트, 데이터 웨어하우스)으로 변환 데이터를 적재한다.


반응형

'Python' 카테고리의 다른 글

[Python Script] Maria DB Table Sync 맞추기  (0) 2017.02.13
[Python] libmysqlclient.so.10 Error  (2) 2016.11.02
[펌][Python] OS 관련 명령어  (0) 2016.10.21
[펌] [python] pass, continue 차이  (0) 2016.03.08
[펌] [python] FTPlib  (1) 2016.03.08
반응형

 운영체제(OS)에서 제공되는 기본적인 기능들을 제공.

 

os.getcwd(), os.chdir(path)

 

chdir() 함수는 현재 작업 디렉터리 위치를 변경하며,

getcwd()함수는 현재 작업 디렉터리의 위치를 가져올 때 쓰입니다.

>>> getcwd()

'C:\\Python3'

>>> chdir('Tools')

>>> getcwd()

'C:\\Python3\\Tools'

 

os.access(path, mode)

 

입력받은 <path>에 대해서 <mode>에 해당하는 작업이 가능한지의 여부를 반환합니다.

 

모드(mode)

설명

F_OK

해당 path의 존재 여부를 확인

R_OK

해당 path의 읽기 가능여부 확인

W_OK

해당 path의 쓰기 가능여부 확인

X_OK

해당 path의 실행 가능여부 확인

 

>>> access('.', F_OK)    # existence

True

>>> access('.', W_OK | X_OK | R_OK) # write, exec, read

True

 

os.listdir(path)

 

해당 경로(path)에 존재하는 파일과 디렉터리들의 리스트를 반환합니다.

>>> listdir('.')

['DLLs', 'Doc', 'include', 'Lib', 'libs', 'LICENSE.txt', 'NEWS.txt', 'python.exe', 'pythonw.exe',

'README.txt', 'tcl', 'Tools', 'w9xpopen.exe']

 

os.mkdir(path[, mode])

 

<path>에 해당하는 디렉터리를 생성합니다.

>>> mkdir('test1')

['DLLs', 'Doc', 'include', 'Lib', 'libs', 'LICENSE.txt', 'NEWS.txt', 'python.exe', 'pythonw.exe',

'README.txt', 'tcl', 'test1', 'Tools', 'w9xpopen.exe']

 

os.makedirs(path[, mode])

 

인자로 전달된 디렉터리를 재귀적으로 생성합니다.

이미 디렉터리가 생성되어 있는 경우나 권한이 없어 생성할 수 없는 경우는 예외를 발생합니다.

>>> makedirs('test2/sub1/sub2/leaf')

>>> listdir('test2/sub1/sub2')

['leaf']

>>> makedirs('test2/sub1/sub2/leaf') # 이미 존재하는 디렉터리를 생성하는 경우 예외 발생

Traceback (most recent call last):

File "<pyshell#18>", line 1, in <module>

makedirs('test2/sub1/sub2/leaf')

File "C:\Dev\Python3\lib\os.py", line 152, in makedirs

mkdir(name, mode)

WindowsError: [Error 183] 파일이 이미 있으므로 만들 수 없습니다: 'test2/sub1/sub2/sub3'

 

os.remove(path), os.unlink(path)

 

파일을 삭제 합니다.

>>> remove('test.txt')

>>> unilnk('test.txt')

 

os.rmdir(path)

 

디렉터리를 삭제합니다. 단, 디렉터리는 비어있어야만 합니다.

>>> mkdir('test1')

 

os.removedirs(path)

 

디렉터리를 연달아 삭제합니다.

 

 만약 '윈도우 탐색기'와 같은 애플리케이션으로 'sub1' 디렉터리를 보고 있다면,

removedirs() 함수로 'sub1' 디렉터리를 삭제할 수 없습니다. 탐색기를 종료하고 수행해야 합니다.

# leaf 디렉토리 삭제에 성공하면 차례로 sub2, sub1, test2의 순서로 삭제

>>> removedirs('test2/sub1/sub2/leaf')

 

os.rename(src, dst)

 

src를 dst로 이름을 변경하거나 이동합니다. 파일이나 디렉터리에 대해서 모두 적용 됩니다.

>>> rename('text.txt', 'renamed.txt')

 

os.renames(src, dst)

 

src를 dst로 이름을 변경하거나 이동합니다. rename과 다른점은 이동 시에 필요한 디렉터리들을

자동으로 생성한다는 것입니다.

>>> renames('renamed.txt', 'test_renames/moved.txt')

>>> listdir('test_renames')

['moved.txt']

 

os.stat(path)

 

경로에 해당하는 정보를 얻어옵니다.

아래의 예제와 같이 순차적으로 protection, inode, device, link, user id, group id, size,

last access time, last modified time, last change time 등을 나타냅니다.

(stat() 함수 결과 중 일부는 유닉스/리눅스 시스템에만 해당되는 것도 있습니다)

>>> stat('python.exe')

nt.stat_result(st_mode=33279, st_ino=281474976762468, st_dev=0, st_nlink=1, st_uid=0,

st_gid=0, st_size=26624, st_atime=1321851747, st_mtime=1315097496, st_ctime=1315097496)

 

os.utime(path, times)

 

경로에 해당하는 파일에 대해 액세스 시간(access time)과 수정 시간(modified time)을 <times>로

수정합니다. <times>가 None일 경우는 현재 시간으로 수정합니다. (유닉스의 touch 명령어와 유사)

>>> stat('readme.txt')

nt.stat_result(st_mode=33206, st_ino=281474976762465, st_dev=0, st_nlink=1, st_uid=0,

st_gid=0, st_size=6788, st_atime=1321797957, st_mtime=1315094610, st_ctime=1315094610)

>>> utime('readme.txt', None)

>>> stat('readme.txt')

nt.stat_result(st_mode=33206, st_ino=281474976762465, st_dev=0, st_nlink=1, st_uid=0,

st_gid=0, st_size=6788, st_atime=1321950244, st_mtime=1321950244, st_ctime=1315094610)

 

os.walk(top[, topdown=True[, onerror=None[, followlinks=False]]])

 

top으로 지정된 디렉터리를 순회하며 경로, 디렉터리명을 순차적으로 반환합니다.

다음의 구조로 test_walk, a, b 디렉터리를 만들어 놓고 walk를 실행

 

>>> for path,dirs,files in walk('test_walk'):

    print(path, dirs, files)

    

test_walk ['a', 'b'] []

test_walk\a [] []

test_walk\b [] ['readme.txt']

 

# topdown이 False로 설정된 경우에는 다음과 같이 디렉터리의 끝에서부터 위로 탐색

>>> for path,dirs,files in walk('test_walk', topdown=False):

    print(path, dirs, files)

    

test_walk\a [] []

test_walk\b [] ['readme.txt']

test_walk ['a', 'b'] []

 

os.umask(mask)

 

umask를 설정합니다. 수행하면 이전 mask 값이 반환됩니다. umask가 수행되면

이후 오픈 되는 파일이나 디렉터리에 (mode & ~umask)와 같이 적용됩니다.

 

os.pipe()

 

파이프를 생성합니다. 함수를 실행하면 읽기, 쓰기 전용 파이프의 파일 디스크립터가 반환됩니다.

 

(※ 파이프(pipe)란 프로세스 간 통신을 위한 공유 영역입니다. 여러 프로세스 간에 정보를 주고 받기

위해 만들어지는 공간이며, 하나의 프로세스가 정보를 쓰면 다른 프로세스에서 읽을 수 있습니다)

>>> pipe()

(3, 4)

 

os.fdopen(fd[, mode[, bufsize]])

 

파일 디스크립터를 이용해 파일 객체를 생성합니다.

 

(※ fdopen은 'from os import *'로 import가 안됩니다.

fdopen의 경우 'from os import fdopen'으로 따로 import 해주어야 합니다)

>>> from os import *

>>> from os import fdopen

 

>>> r,w = pipe()

>>> rd = fdopen(r)

>>> rd

<_io.TextIOWrapper name=3 mode='r' encoding='cp949'>

 

os.popen(command[, mode[, bufsize]])

 

인자로 전달된 command를 수행하며 파이프를 엽니다.

(파이썬3 에서는 Popen 클래스의 사용을 권장하지만 그대로 사용할 수도 있습니다)

 

(※ popen은 'from os import *'로 import가 안됩니다. popen의 경우 'from os import popen'으로

import하거나 import os 후 os.popen으로 사용해야 합니다)

>>> from os import popen

 

>>> p = popen('dir', 'r')

>>> p.read()

...<중략>...

2011-09-04 오전 09:51 26,624 python.exe\n

2011-09-04 오전 09:51 27,136 pythonw.exe\n

2011-11-22 오후 05:24 6,788 README.txt\n

2011-11-20 오후 11:06 <DIR> tcl\n

2011-11-20 오후 11:05 <DIR> Tools\n

2011-09-04 오전 09:51 49,664 w9xpopen.exe\n

6개 파일 396,879 바이트\n

9개 디렉터리 72,007,368,704 바이트 남음\n'

 

os.name

 

파이썬이 실행되는 운영체제의 이름을 나타냅니다. (ex: 'nt', 'posix', 'mac'등...)

>>> name

'nt'

 

os.environ

 

환경변수들을 나타내는 사전입니다.

>>> environ

environ({'TMP': 'C:\\DOCUME~1\\ADMINI~1\\LOCALS~1\\Temp',

'COMPUTERNAME': 'WIN2003', 'USERDOMAIN': 'WIN2003',

'COMMONPROGRAMFILES': 'C:\\Program Files\\Common Files', ...<중략>...})

>>> environ['OS']

'Windows_NT'

>>> environ['userprofile']

'C:\\Documents and Settings\\Administrator'

 

os.getpid()

 

현재 프로세스 아이디를 반환합니다.

>>> getpid()

3380

 

os.getenv(varname[, value])

 

환경 변수의 값을 얻어 옵니다. 다만 해당 환경 변수가 없을 경우에는 인자로 전달된 <value>값을

반환합니다. value가 생략되고 해당 환경 변수가 없으면 None을 반환 합니다.

>>> getenv('homepath')

'\\Documents and Settings\\Administrator'

>>> getenv('test', '')

''

 

os.putenv(varname, value)

 

환경변수 <varname>을 <value>로 설정합니다. 자식 프로세스에게 영향을 미칩니다.

>>> putenv('test', '\\tmp\\test')

 

# 자식 프로세스에게 영향을 미치므로, putenv()의 결과 확인.

>>> from os import popen

>>> p = popen('''python -c "import os; print(os.getenv('test'))"''''r')

>>> p.read()

'\\tmp\\test\n'

 

os.strerror(code)

 

에러 코드에 해당하는 에러 메시지를 보여줍니다.

>>> for i in range(0, 44):

print (i, strerror(i))

 

0 No error

1 Operation not permitted

2 No such file or directory

3 No such process

4 Interrupted function call

5 Input/output error

6 No such device or address

7 Arg list too long

8 Exec format error

9 Bad file descriptor

10 No child processes

...<중략>...

 

os.system(command)

 

<command>를 실행하며, 성공한 경우 0을 반환합니다.

[계산기 실행화면]

 

os.startfile(path[, operation])

 

<path>를 os에서 지정된 프로그램으로 실행합니다.

또한 <operation>으로 명시적으로 수행할 프로그램을 지정할 수 있습니다.

 

(※ starfile('LICENSE.txt')의 경우 system("Notepad LICENSE.txt')와 유사하지만 system()을 사용하는

경우에는 파이썬 프로그램의 실행이 잠시 멈추고 system()이 끝나길 기다리게 되고, startfile()은

멈추지 않고 계속 실행됩니다.)

 

os.execl(path, arg0, arg1, ...)

os.execle(path, arg0, arg1, ..., env)

os.execlp(file, arg0, arg1, ...)

os.execlpe(file, arg0, arg1, ..., env)

os.execv(path, args)

os.execve(path, args, env)

os.execvp(file, args)

os.execvpe(file, args, env)

 

위의 함수는 현재 프로세스에서 새로운 프로그램을 수행시키며 리턴은 하지 않습니다.

인자에 따라 여러 가지 exec로 나뉘어지는데, 우선'l'이 붙은 것들은 입력인자들의 수가 정해져

있는 경우이고, 'v'가 붙은 것들은 args라는 튜플로 입력인자를 받습니다. 'e'가 붙은 경우는

환경변수 env를 받느냔 아니냐의 차이이며, 'p'는 환경변수의 path를 이용하는 경우입니다.

>>> execl('C:\\Python3\python''python''-v')

>>> execv('python'('python', '-v'))

>>> execle('C:\Python3\python''python', '-v', {"HOME":"C:\\"})


반응형
반응형

완전 퍼온 글이다. 도움이 될 듯 싶어서 공유한다.



BINLOG FORMAT은 세가지가 있다. 

1) STATEMENT
2) MIXED
3) ROW 

이 중 MIXED는 1),3) 의 혼합 형태로 binary 로그를 남기게 되는데 
몇몇 일관성을 보장하지 못하는 케이스를 제외하고는 1) STATEMENT 형태로 남게 된다. 

그런데 ISOLATION LEVEL 도 binary 로그 포맷에 영향을 주게 되는데 
ISOLATION LEVEL 이 READ-COMMITTED의 경우 binlog_format을 MIXED 이상으로 설정을 해야 한다. 


ISOLATION LEVEL 

1) READ-COMMITTED 

The default isolation level for most database systems ( nut not MySQL ) is READ COMMITTED. It satisfies the simple
definition of isolation used earlier: a transaction will see only those changes made  by transactions that were already committed when it began, and its changes won't be visible to others until it has committed. This level still allows what's 
known as a nonrepeatable read. This means you can run the same statement twice and see different data.

2) REPEATABLE READ

It guarantees that any rows a transaction reads will "look the same" in subsequent reads within the same transaction, but 
in theory it still allows another tricky problem: phantom reads. Simply put, a phantom read can happen when you select some range of rows, another transaction inserts a new row into the range, and then you select the same range again: 
you will then see the new "phantom" row. InnoDB and XtraDB solve the phantom read problem with multiversion comcurrency control, which we explain later in this chapter. REPEATABLE READ is MySQL's default transaction isolation level.


Isolation level을 read committed로 하고 binlog format을 MIXED로 한 후 대부분의 binary 로그가 statement 형태로 남을 것으로 생각했다. 그런데 전부다 row format 으로 남았고, 이것이 정상이라고 한다. isolation level이 repeatable read 에서는 binlog format MIXED 일 때 대부분의 경우 statement 형태로 남고 일관성이 보장이 안되는 특정 경우에만 row 형태로 남았다. 

이건 isolation level의 개념만 다시 확인해 보면 당연한 결과이다. 

테스트를 해보자. 

1. transaction_isolation = repeatable-read


두 개의 세션에서 트랜잭션을 열고 아래 번호 순서대로 DML이 실행한다. 그리고 
 

 

[root@localhost] (test) 12:12> rollback;
Query OK, 0 rows affected (0.00 sec)


####### Session A

[root@localhost] (test) 12:13> start transaction ;
Query OK, 0 rows affected (0.00 sec)

[root@localhost] (test) 12:14>
[root@localhost] (test) 12:14> update trans_test2 set b=999 where a=1;   ----------- 1)
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

[root@localhost] (test) 12:14> update trans_test2 set b=222 where a=10;  ----------- 3) 
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

[root@localhost] (test) 12:14> commit;
Query OK, 0 rows affected (0.00 sec)

 

 

####### Session B

[root@localhost] (test) 12:13> start transaction ;
Query OK, 0 rows affected (0.00 sec)

[root@localhost] (test) 12:14>
[root@localhost] (test) 12:14> update trans_test2 set b=999 where a=20;  ------------ 2)
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

[root@localhost] (test) 12:14> update trans_test2 set b=222 where a=25;  ------------ 4)
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

[root@localhost] (test) 12:14> commit;
Query OK, 0 rows affected (0.00 sec)

 

####### binary log를 확인해 보면 먼저 commit 된 transaction 순서대로 기록된다. ( DML 실행 순서 아님 )


#131001 12:14:08 server id 12  end_log_pos 10509        Query   thread_id=2     exec_time=0     error_code=0
use `test`/*!*/;
SET TIMESTAMP=1380597248/*!*/;
update trans_test2 set b=999 where a=1f<9c>ⓒK
/*!*/;
# at 10509
#131001 12:14:21 server id 12  end_log_pos 10622        Query   thread_id=2     exec_time=0     error_code=0
SET TIMESTAMP=1380597261/*!*/;
update trans_test2 set b=222 where a=10oI^PØ
/*!*/;
# at 10622
#131001 12:14:47 server id 12  end_log_pos 10653        Xid = 127
COMMIT/*!*/;
# at 10653
#131001 12:14:13 server id 12  end_log_pos 10732        Query   thread_id=3     exec_time=0     error_code=0
SET TIMESTAMP=1380597253/*!*/;
BEGINU<90>%²
/*!*/;
# at 10732
#131001 12:14:13 server id 12  end_log_pos 10845        Query   thread_id=3     exec_time=0     error_code=0
use `test`/*!*/;
SET TIMESTAMP=1380597253/*!*/;
update trans_test2 set b=999 where a=20AE§¥
/*!*/;
# at 10845
#131001 12:14:44 server id 12  end_log_pos 10958        Query   thread_id=3     exec_time=0     error_code=0
SET TIMESTAMP=1380597284/*!*/;
update trans_test2 set b=222 where a=25^]GRA
/*!*/;
# at 10958
#131001 12:14:50 server id 12  end_log_pos 10989        Xid = 129
COMMIT/*!*/;
DELIMITER ;
# End of log file
ROLLBACK /* added by mysqlbinlog */;
/*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/;

 


 

2. transaction_isolation = read-committed

 

=== session A

[root@localhost] (test) 13:27> start transaction;
Query OK, 0 rows affected (0.00 sec)

[root@localhost] (test) 13:32>
[root@localhost] (test) 13:32> update trans_test2 set b=444 where a=1;    ---------- 1)
Query OK, 1 row affected (0.16 sec)
Rows matched: 1  Changed: 1  Warnings: 0

[root@localhost] (test) 13:32> select * from trans_test2 where a=20;      ---------- 2) 
+----+------+
| a  | b    |
+----+------+
| 20 |  999 |
+----+------+
1 row in set (0.01 sec)

[root@localhost] (test) 13:33> select * from trans_test2 where a=20;      ----------- 5) 3) DML 결과가 보인다.  
+----+------+
| a  | b    |
+----+------+
| 20 |  777 |
+----+------+
1 row in set (0.00 sec)

[root@localhost] (test) 13:33> update trans_test2 set b=787 where a=20 and b=777;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

[root@localhost] (test) 13:33>
[root@localhost] (test) 13:33> commit;
Query OK, 0 rows affected (0.00 sec)

[root@localhost] (test) 13:33> select * from trans_test2 where a=20;
+----+------+
| a  | b    |
+----+------+
| 20 |  787 |
+----+------+
1 row in set (0.00 sec)

 

=== session B

[root@localhost] ((none)) 13:28> use test
Database changed
[root@localhost] (test) 13:28> start transaction;
Query OK, 0 rows affected (0.00 sec)

[root@localhost] (test) 13:33>
[root@localhost] (test) 13:33> update trans_test2 set b=777 where a=20;    ----------- 3) 
Query OK, 1 row affected (0.02 sec)
Rows matched: 1  Changed: 1  Warnings: 0

[root@localhost] (test) 13:33>
[root@localhost] (test) 13:33> commit;   -------- 4) 
Query OK, 0 rows affected (0.00 sec)

 

위 테스트 처럼 isolation level READ-COMMITTED의 경우  현재의 트랜잭션이 종료 되지 않은 상황에서 다른 트랜잭션이 commit 이 되면 그 트랜잭션 통해 변경된 값이 보이게 된다. 그런데 이런 경우를 statement 방식으로 트랜잭션 단위로 묶어서 순서대로 로깅해서 그 순서대로 쿼리를 실행하게 될 경우 실제 상황을 그대로 재현할 수 없게 되고 다른 결과가 나올 수 있게 된다. 그렇기 때문에 isolation level READ-COMMITTED , binlog_format MIXED 에서 는 binary log가 row 형태로 남게 된다. 

반응형
반응형

utf-8 과 utf8mb4 에 대해서 알아보자.


해당 캐릭터셋은 현재 회사 DB가 euckr 인데...이것을 utf8로 변경하고자 확인하던 도중 많은 도움이 된 블로그가 있어서 정리하면서 같이 소개해 본다.


아래 블로그의 모든 내용을 조금씩 정리하면서 작성하였습니다.

가져온 블로그 : https://blog.lael.be/post/917




utf-8 문자 집한은 1~4 byte까지 저장이 가능하게 설계(가변 바이트)


 -> 다국어가 지원이 되기 때문에 전세계 모든 언어를 저장이 가능하다

 -> MySQL에서 utf8 을 3 Byte 가변 자료형으로 설계


하지만 최근에 나온 4 byte 문자열(Emoji 같은 것)을 utf8 에 저장하면 값이 손실되는 현상이 발생

(charset 은 utf8 / collation 는 utf8_general_ci)


4 Byte UTF-8 문자열

 : MySQL에서 기존 설계대로 가변 4 Byte utf-8 문자열을 저장할 수 있는 자료형을 추가

 -> 이것이 utf8mb4 로 지정 (MySQL 5.5.3 에서부터 추가)


- utf8 : Basic Plane.


- utf8mb4 : Basic Plane + Supplementary Plane.


추가로 utf8mb4 는 Emoji 문자열 지원

 

- 스마트폰에서 지원하는 문자들을 지원 (SMP - Supplementary Multilingual Plane)

  (모바일 어플의 경우의 DB는 utf8mb4 로 설정하는 것이 좋을 듯 싶다) 

 아래 이미지 참고 





즉, utf8 (가변 3바이트) utf8mb4 (가변 4바이트) 저장공간의 크기




Collaction


Collaction은 텍스트 데이터를 정렬 (Order by) 할 때 사용

text 계열 자료형에서만 사용할 수 있는 속성


utf8_bin (utf8mb4_bin)


Binary 저장 값 그대로 정렬

Hex 코드 (16진수)로 되어 있기에 해당 값으로 정렬


utf8_uniconde_ci (utf8mb4_unicode_ci)


한국어, 영어, 중국어, 일본어 사용환경에서는 general_ci 와 unicode_ci 의 결과가 동일

하지만 외국어 및 특수문자가 들어가는 경우 다름




utf8mb4 charset 에, utf8mb4_unicode_ci collation 사용 하는 것이 좋을 듯 싶다.


문제점


1) 다국어를 처리할 수 있는 UTF-8 이라는 저장방식이 있음. 원래 설계는 가변4바이트임.


2) 전세계 모든 언어문자를 다 카운트 해봤는데 3 바이트가 안됨.


3) MYSQL/MariaDB 에서는 공간절약+속도향상 을 위해서 utf8 을 가변3바이트로 설계함.


4) Emoji 같은 새로나온 문자가 UTF-8의 남은 영역을 사용하려함 (4바이트 영역).


5) MYSQL/MariaDB 에서 가변4바이트 자료형인 utf8mb4 를 추가함.  (2010년 3월에).



반응형
반응형

매일 하나씩 공유하고 연구하자는게 하루만에 무너졌다....

다시 시작해보자!!!




mysqlbinlog 관련하여 테스트 중 binlog_format 을 row로 변경하여 테스트 하던 도중 아래와 같은 에러가 발생 하였다.


ERROR: Error in Log_event::read_log_event(): 'Found invalid event in binary log', data_len: 47, event_type: 19



으음.....여기저기 찾아봤는데 원하는 답변을 못 찾았다...

그러다 어느 중국 사이트에서 유사 관련하여 올려 놓은 것을 확인할 수 있었다.


단순 default 로 잡힌 mysqlbinlog 의 version을 확인해 보자



3.2 인것을 확인할 수 있다.

버전차이 때문에 발생할 수 있으니 다음과 같이 엔진에 있는 mysqlbinlog 로 확인해 보자




3.4 인 것을 확인할 수 있다.


그렇다면 다시 binlog가 제대로 되는지 확인해 보자



제대로 import되는 것을 확인할 수 있다.


row 관련한 내용은 좀 더 공부해야겠다...참고로 아래 이미지를 보면 정말 oracle 의 rowid 를 보는 듯한 느낌이 난다...



반응형
반응형
최근에 너무 바빴다..
아픈것도 있었고...심신의 위로가 너무나도 필요했다.

의욕도 생기지 않은것도 한몫 했다...

이런 하소연은 Life에 일기로 써야지....ㅋㅋ쓰다보면 위로가 될꺼라 믿고...


각설하고 오늘부터 하루에 기술 관련 정리하여 올리자고 한다.

작심 3일을 매주 2번..씩 다짐한다.




오늘 Binlog_format에 대한 정리한 것을 공유해 본다.


아래 블로그에서 참고 하였다.

[출처] : http://mysqldba.tistory.com/196



★ binlog_format 종류 및 차이

  • Statement Format : 가장 오래된 Format으로 데이터 변경에서 사용되는 모든 쿼리를 쿼리대로 저장하는 방식을 말함(5.7까지 기본 Format)
  • Row Format : 변경 작업으로 변경된 모든 Row의 정보를 기록하는 방식
  • Mixed Format : Statement 방식과 Row 방식을 혼합한 방식으로 기본은 Statement 방식이고, 몇몇의 경우에는 Row방식으로 동작하는 방식
mysql> set global binlog_format = 'STATEMENT';
mysql> set global binlog_format = 'ROW';
mysql> set global binlog_format = 'MIXED';

 - global 대신 session 으로 변경 가능

* Row 와 Statement 

데이터베이스에 작은 변경을 많이 발생 시키는 쓰레드는 열 기반 로깅을 선호한다. 
WHERE 구문에 있는 많은 열과 매치가 되는 업데이트를 실행하는 쓰레드는 명령문 기반 로깅을 선호하는데, 그 이유는 많은 열을 로깅하는 것 보다는 적은 명령문 로깅이 효과적이기 때문이다.

마스터에서 오랜 실행 시간 동안 실행되지만 비교적 적은 수의 열만을 수정하는 명령문들이 있다. 
이러한 명령문들은 열 기반 로깅을 사용해서 복제하는 것이 보다 효과적이다.


* Defualt 


* STATEMENT 일때 로그 흔적


* row 형태의 format




* Format 변경 시 문제 발생하는 시점
    • Trigger나 Stored Function을 사용하고 있는 경우
    • NDB Cluster Storage Engine을 사용하고 있는 경우
    • 현재 Temporary Table을 사용하고 있는 세션이 있는 경우

  • Mixed Binary Logging Format 을 사용하면 Binary log를 작성하면 MySQL은 자동적으로 Statement방식과 Row 방식을 섞어서 기술
    • Row 방식으로 작성 되는 경우
      • NDB Cluster Table에 DML이 실행된 경우
      • UUID()를 사용한 경우
      • Auto_Increment 칼럼을 가지고 있는 테이블에 연결된 Trigger나 Stored Function이 생성되어 사용되는 경우
      • Insert Delayed가 실행하는 경우
      • User Defined Function 을 호출할 때 등
  • Storage Engine 별로 지원하는 format

Storage Engine

Row Logging Supported

Statement Logging Supported

Archive

Yes

Yes

Blackhole

Yes

Yes

CSV

Yes

Yes

Example

Yes

No

Federated

Yes

Yes

Heap

Yes

Yes

InnoDB

Yes

Yes / When the Transaction isolation level is Repeatable Read or Serializable : No otherwise.

MyISAM

Yes

Yes

Merge

Yes

Yes

NDB

Yes

No


  • Storage Engine별 지원하는 logging 형태에 따라 2가지로 분류
    1. SLC (Statement-Logging Capable) - Statement Logging 방식을 지원하는 Storage Engine.
    2. RLC (Row-Logging Capable) - Row Logging 방식을 지원하는 Storage Engine
  • Binary Log에 작성되는 3가지 종류
    1. Safe : Statement 방식으로 작성 시 Replication 이나 복구 시에 문제가 되지 않는 쿼리 형태
    2. UnSafe : Statement 방식으로 작성 시 Replication 이나 복구 시에 문제가 되는 쿼리 형태
    3. Row/Binary Injection : Row Event로 Row Based 방식으로 실행하기 위해 저장되는 Event형태 (Row format 으로 로깅해야 지만, 변경 내역이 저장되는 것을 의미)
  •  MySQL은 Grant 작업은 직접적인 DML로 진행될 수도 있으나 간접적인 DDL 구문으로 실행 가능
    1. 직접적으로 실행되는 DML 는 Binlog_format에 따라 작성되는 방법이 다름 (Insert / Update / Delete / Replace / Do / Load Data Infile / Select / Truncate table)
    2. 간접적으로 실행되는 DDL 문은 Binlog_format과 상관없이 무조건 Statement방식으로 저장(Grant / Revoke / Set Password / Rename User / Create / Alter Drop)
    3. Create table .. Select 경우 Create Table 은 Statement 방식으로 저장되고 Select 부분은 Binlog_format 설정값에 따라 달라짐


반응형
반응형
Redis 관련해서 기초 이해하기 위해 아래 블로그도 많은 도움이 되었다.
물론 해당 내용이 2012년도? 내용이기에 변경된 부분도 조금 있다.

아래 내용의 경우 현재 Cluster 가 개발되어 있다.

현재 버전 2.4.15에서는 Clustering을 지원하지 않아서 Sharding을 사용할 수 밖에 없지만 2012년 내에 Clustering기능이 포함된다고 하니, 확장성에 대해서 기대해볼만하다. redis가 지원할 clustering 아키텍쳐는 ( http://redis.io/presentation/Redis_Cluster.pdf ) 를 참고하기 바란다.

Redis Cluster

Link : http://www.redisgate.com/redis/cluster/cluster.php


조만간에 Redis 설치 테스트 하여 업로드할 계획이다.

여러가지를 이것저것 해보는 것이 좋을 수도 있지만...너무 조급심을 내고 있지 않는가 걱정도 된다.




출처 : http://bcho.tistory.com/654


redis Introduction


Intro
Redis는 "REmote DIctionary System"의 약자로 메모리 기반의 Key/Value Store 이다.
Cassandra나 HBase와 같이 NoSQL DBMS로 분류되기도 하고, memcached와 같은 In memory 솔루션으로 분리되기도 한다.
성능은 memcached에 버금가면서 다양한 데이타 구조체를 지원함으로써 Message Queue, Shared memory, Remote Dictionary 용도로도 사용될 수 있으며, 이런 이유로 인스탄트그램, 네이버 재팬의 LINE 메신져 서비스, StackOverflow,Blizzard,digg 등 여러 소셜 서비스에 널리 사용되고 있다.
BSD 라이센스 기반의 오픈 소스이며 최근 VMWare에 인수되어 계속해서 업그레이드가 되고 있다.
16,000 라인정도의 C 코드로 작성되었으며, 클라이언트 SDK로는
Action Script,C,C#,C++,Clojure,Erlang,Java,Node.js,Objective-C,Perl,PHP,Python,Smalltalk,Tcl등 대부분의 언어를 지원한다. (참고 : http://www.redis.io/clients )

이번 글에서는 Redis란 무엇인지, 그리고 대략적인 내부 구조에 대해서 살펴보도록 한다.

1. Key/Value Store
Redis는 기본적으로 Key/Value Store이다. 특정 키 값에 값을 저장하는 구조로 되어 있고 기본적인 PUT/GET Operation을 지원한다.

단, 이 모든 데이타는 메모리에 저장되고, 이로 인하여 매우 빠른 write/read 속도를 보장한다. 그래서 전체 저장 가능한 데이타 용량은 물리적인 메모리 크기를 넘어설 수 있다. (물론 OS의 disk swapping 영역등을 사용하여 확장은 가능하겠지만 성능이 급격하게 떨어지기 때문에 의미가 없다.)
데이타 억세스는 메모리에서 일어나지만 server restart 와 같이 서버가 내려갔다가 올라오는 상황에 데이타를 저장을 보장하기 위해서 Disk를 persistence store로 사용한다.

2. 다양한 데이타 타입
단순한 메모리 기반의 Key/Value Store라면 이미 memcached가 있지 않은가? 그렇다면 어떤 차이가 있길래 redis가 유행하는 것일까?
redis가 Key/Value Store이기는 하지만 저장되는 Value가 단순한 Object가 아니라 자료구조를 갖기 때문에 큰 차이를 보인다.
redis가 지원하는 데이타 형은 크게 아래와 같이 5가지가 있다.

1) String
일반적인 문자열로 최대 512mbyte 길이 까지 지원한다.
Text 문자열 뿐만 아니라 Integer와 같은 숫자나 JPEG같은 Binary File까지 저장할 수 있다.

2) Set
set은 string의 집합이다. 여러개의 값을 하나의 Value 내에 넣을 수 있다고 생각하면 되며 블로그 포스트의 태깅(Tag)등에 사용될 수 있다.
재미있는 점은 set간의 연산을 지원하는데, 집합인 만큼 교집합, 합집합, 차이(Differences)를 매우 빠른 시간내에 추출할 수 있다.

3) Sorted Set
set 에 "score" 라는 필드가 추가된 데이타 형으로 score는 일종의 "가중치" 정도로 생각하면 된다.
sorted set에서 데이타는 오름 차순으로 내부 정렬되며, 정렬이 되어 있는 만큼 score 값 범위에 따른 쿼리(range query), top rank에 따른 query 등이 가능하다.


4) Hashes
hash는 value내에 field/string value 쌍으로 이루어진 테이블을 저장하는 데이타 구조체이다.
RDBMS에서 PK 1개와 string 필드 하나로 이루어진 테이블이라고 이해하면 된다.


5) List
list는 string들의 집합으로 저장되는 데이타 형태는 set과 유사하지만, 일종의 양방향 Linked List라고 생각하면 된다. List 앞과 뒤에서 PUSH/POP 연산을 이용해서 데이타를 넣거나 뺄 수 있고, 지정된 INDEX 값을 이용하여 지정된 위치에 데이타를 넣거나 뺄 수 있다. 


6) 데이타 구조체 정리
지금까지 간략하게 redis가 지원하는 데이타 구조체들에 대해서 살펴보았다.
redis의 데이타 구조체의 특징을 다시 요약하자면
  • Value가 일반적인 string 뿐만 아니라, set,list,hash와 같은 집합형 데이타 구조를 지원한다.
  • 저장된 데이타에 대한 연산이나 추가 작업 가능하다. (합집합,교집합,RANGE QUERY 등)
  • set은 일종의 집합, sorted set은 오름차순으로 정렬된 집합, hash는 키 기반의 테이블, list는 일종의 링크드 리스트 와 같은 특성을 지니고 있다.
이러한 집합형 데이타 구조 (set,list,hash)등은 redis에서 하나의 키당 총 2^32개의 데이타를 이론적으로 저장할 수 있으나, 최적의 성능을 낼 수 있는 것은 일반적으로 1,000~5,000개 사이로 알려져 있다.

데이타 구조에 따른 저장 구조를 정리해서 하나의 그림에 도식화해보면 다음과 같다.



3. Persistence
앞서도 언급하였듯이, redis는 데이타를 disk에 저장할 수 있다. memcached의 경우 메모리에만 데이타를 저장하기 때문에 서버가 shutdown 된후에 데이타가 유실 되지만, redis는 서버가 shutdown된 후 restart되더라도, disk에 저장해놓은 데이타를 다시 읽어서 메모리에 Loading하기 때문에 데이타 유실되지 않는다.
redis에서는 데이타를 저장하는 방법이 snapshotting 방식과 AOF (Append on file) 두가지가 있다.

1) snapshotting (RDB) 방식
순간적으로 메모리에 있는 내용을 DISK에 전체를 옮겨 담는 방식이다.
SAVE와 BGSAVE 두가지 방식이 있는데,
SAVE는 blocking 방식으로 순간적으로 redis의 모든 동작을 정지시키고, 그때의 snapshot을 disk에 저장한다.
BGSAVE는 non-blocking 방식으로 별도의 process를 띄운후, 명령어 수행 당시의 메모리 snaopshot을 disk에 저장하며, 저장 순간에 redis는 동작을 멈추지 않고 정상적으로 동작한다.
  • 장점 : 메모리의 snapshot을 그대로 뜬 것이기 때문에, 서버 restart시 snapshot만 load하면 되므로 restart 시간이 빠르다.
  • 단점 : snapshot을 추출하는데 시간이 오래 걸리며, snapshot 추출된후 서버가 down되면 snapshot 추출 이후 데이타는 유실된다.
    (백업 시점의 데이타만 유지된다는 이야기)
2) AOF 방식
AOF(Append On File) 방식은 redis의 모든 write/update 연산 자체를 모두 log 파일에 기록하는 형태이다. 서버가 재 시작될때 기록된  write/update operation을 순차적으로 재 실행하여 데이타를 복구한다. operation 이 발생할때 마다 매번 기록하기 때문에, RDB 방식과는 달리 특정 시점이 아니라 항상 현재 시점까지의 로그를 기록할 수 있으며, 기본적으로 non-blocking call이다.
  • 장점 : Log file에 대해서 append만 하기 때문에, log write 속도가 빠르며, 어느 시점에 server가 down되더라도 데이타 유실이 발생하지 않는다.
  • 단점 : 모든 write/update operation에 대해서 log를 남기기 때문에 로그 데이타 양이 RDB 방식에 비해서 과대하게 크며, 복구시 저장된 write/update operation을 다시 replay 하기 때문에 restart속도가 느리다.
3) 권장 사항
RDB와 AOF 방식의 장단점을 상쇠하기 위해서 두가지 방식을 혼용해서 사용하는 것이 바람직한데
주기적으로 snapshot으로 백업하고, 다음 snapshot까지의 저장을 AOF 방식으로 수행한다.
이렇게 하면 서버가 restart될 때 백업된 snapshot을 reload하고, 소량의 AOF 로그만 replay하면 되기 때문에, restart 시간을 절약하고 데이타의 유실을 방지할 수 있다.


4. Pub/Sub Model
redis는 JMS나 IBM MQ 같은 메세징에 활용할 수 있는데, 1:1 형태의 Queue 뿐만 아니라 1:N 형태의 Publish/Subscribe 메세징도 지원한다.(Publish/Subscribe 구조에서 사용되는 Queue를 일반적으로 Topic이라고 한다.)
하나의 Client가 메세지를 Publish하면, 이 Topic에 연결되어 있는 다수의 클라이언트가 메세지를 받을 수 있는 구조이다. (※ Publish/Subscribe 형태의 messaging 에 대해서는 http://en.wikipedia.org/wiki/Pub/sub  를 참고하기 바란다.)


재미있는 것중에 하나는 일반적인 Pub/Sub 시스템의 경우 Subscribe 하는 하나의 Topic에서만 Subscribe하는데 반해서, redis에서는 pattern matching을 통해서 다수의 Topic에서 message 를 subscribe할 수 있다.
예를 들어 topic 이름이 music.pop music,classic 이라는 두개의 Topic이 있을때, "PSUBSCRIBE music.*"라고 하면 두개의 Topic에서 동시에 message를 subscribe할 수 있다.

5. Replication Topology
redis는 NoSQL 계열의 Key/Store Storage인데 반해서 횡적 확장성을 지원하지 않는다.
쉽게 말해서 2.4.15 현재 버전 기준으로는 클러스터링 기능이 없다. (향후 지원 예정)
그래서 확장성(scalability)과 성능에 제약사항이 있는데, 다행이도 Master/Slave 구조의 Replication(복제)를 지원하기 때문에 성능 부분에 있어서는 어느정도 커버가 가능하다.

Master/Slave replication
Master/Slave Replication이란, redis의 master node에 write된 내용을 복제를 통해서 slave node에 복제 하는 것을 정의한다.
1개의 master node는 n개의 slave node를 가질 수 있으며, 각 slave node도 그에 대한 slave node를 또 가질 수 있다.


이 master/slave 간의 복제는 Non-blocking 상태로 이루어진다. 즉 master node에서 write나 query 연산을 하고 있을 때도 background로 slave node에 데이타를 복사하고 있다는 이야기고, 이는 master/slave node간의 데이타 불일치성을 유발할 수 있다는 이야기이기도 하다.
master node에 write한 데이타가 slave node에 복제중이라면 slave node에서 데이타를 조회할 경우 이전의 데이타가 조회될 수 있다.

Query Off Loading을 통한 성능 향상
그러면 이 master/slave replication을 통해서 무엇을 할 수 있냐? 성능을 높일 수 있다. 동시접속자수나 처리 속도를 늘릴 수 있다. (데이타 저장 용량은 늘릴 수 없다.) 이를 위해서 Query Off Loading이라는 기법을 사용하는데
Query Off Loading은 master node는 write only, slave node는 read only 로 사용하는 방법이다.
단지 redis에서만 사용하는 기법이 아니라, Oracle,MySQL과 같은 RDBMS에서도 많이 사용하는 아키텍쳐 패턴이다.
대부분의 DB 트렌젝션은 웹시스템의 경우 write가 10~20%, read가 70~90% 선이기 때문에, read 트렌젝션을 분산 시킨다면, 처리 시간과 속도를 비약적으로 증가 시킬 수 있다. 특히 redis의 경우 value에 대한 여러가지 연산(합집합,교집합,Range Query)등을 수행하기 때문에, 단순 PUT/GET만 하는 NoSQL이나 memcached에 비해서 read에 사용되는 resource의 양이 상대적으로 높기 때문에 redis의 성능을 높이기 위해서 효과적인 방법이다.

Sharding 을 통한 용량 확장
redis가 클러스터링을 통한 확장성을 제공하지 않는다면, 데이타의 용량이 늘어나면 어떤 방법으로 redis를 확장해야 할까?
일반적으로 Sharding이라는 아키텍쳐를 이용한다. Sharding은 Query Off loading과 마친가지로, redis 뿐만 아니라 일반적인 RDBMS나 다른 NoSQL에서도 많이 사용하는 아키텍쳐로 내용 자체는 간단하다.
여러개의 redis 서버를 구성한 후에, 데이타를 일정 구역별로 나눠서 저장하는 것이다. 예를 들어 숫자를 key로 하는 데이타가 있을때 아래와 그림과 같이 redis 서버별로 저장하는 key 대역폭을 정해놓은 후에, 나눠서 저장한다.
데이타 분산에 대한 통제권은 client가 가지며 client에서 애플리케이션 로직으로 처리한다.

현재 버전 2.4.15에서는 Clustering을 지원하지 않아서 Sharding을 사용할 수 밖에 없지만 2012년 내에 Clustering기능이 포함된다고 하니, 확장성에 대해서 기대해볼만하다. redis가 지원할 clustering 아키텍쳐는 ( http://redis.io/presentation/Redis_Cluster.pdf ) 를 참고하기 바란다.

6. Expriation
redis는 데이타에 대해서 생명주기를 정해서 일정 시간이 지나면 자동으로 삭제되게 할 수 있다.
redis가 expire된 데이타를 삭제 하는 정책은 내부적으로 Active와 Passive 두 가지 방법을 사용한다.
Active 방식은 Client가 expired된 데이타에 접근하려고 했을 때, 그때 체크해서 지우는 방법이 있고
Passive 방식은 주기적으로 key들을 random으로 100개만 (전부가 아니라) 스캔해서 지우는 방식이 이다.
expired time이 지난 후 클라이언트에 의해서 접근 되지 않은 데이타는 Active 방식으로 인해서 지워지지 않고 Passive 방식으로 지워져야 하는데, 이 경우 Passive 방식의 경우 전체 데이타를 scan하는 것이 아니기 때문에, redis에는 항상 expired 되었으나 지워지지 않는 garbage 데이타가 존재할 수 있는 원인이 된다.

7. Redis 설치(윈도우즈)
https://github.com/rgl/redis/downloads 에서 최신 버전 다운로드 받은후
redis-server.exe를 실행

클라이언트는 redis-cli.exe를 실행
아래는 테스트 스크립트
    % cd src
    % ./redis-cli
    redis> ping
    PONG
    redis> set foo bar
    OK
    redis> get foo
    "bar"
    redis> incr mycounter
    (integer) 1
    redis> incr mycounter
    (integer) 2
    redis> 


참고 자료


반응형

+ Recent posts