현재 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 형태로 남게 된다. 

반응형

+ Recent posts