Posting

Machbase의 최신 소식을 지금 만나보세요

[MACHBASE 파이썬] 고속 데이터 입력

마크베이스(Machbase) 파이썬 기반  고속 데이터 입력

들어가며

파이썬 모듈을 통해서 대량의 텍스트 화일이나 라이브로 생성되는 데이터를 빠르게 마크베이스에 입력해야 하는 경우가 있을 듯 하다.

기존의 Direct Execute 방식은 구조상 POVE(Parsing, Optimization, Validation, Execution)를 매번 거쳐야 하기 때문에 비용이 많이 드는 구조인데, 마크베이스에서는 다행스럽게도 이런 과정 없이 직접 데이터를 넣을 수 있도록 API를 제공해 준다.

어떻게 쓰는지..얼마나 빠른지 한번 사용해 보도록 하자.

Append API 설명

매뉴얼의 설명을 보면 (http://krdoc.machbase.com:8090/display/MANUAL/Pythonhttp://doc.machbase.com/python-module)
아래와 같이 꽤 간단하게 나와 있는데 소스코드를 통해 설명해 보도록 하겠다.

machbaseDB.append(aTableName, aTypes, aValues, aFormat) ; 

다음과 같이 sample_table 테이블에 데이터를 넣는 것을 예로 하겠다.

create table sample_table(d1 short, d2 integer, d3 long, f1 float, f2 double, name varchar(20), text text, bin binary, v4 ipv4, v6 ipv6, dt datetime)
aTableName

여기에는 텍스트로 데이터를 입력할 테이블 이름을 입력한다. 테이블 명은 “sample_table”이 될 것이다.

aTypes

여기에는 입력할 해당 테이블 구조의 컬럼들의 고유한 데이터 타입을 순서대로 입력해 주어야 한다.

이 타입을 알기 위해서는 다음과 같이 machbaseAPI의 columns()라는 함수를 통해서 sample_table의 컬럼 정보를 얻어서 타입 값을 얻어온다.

발췌한 소스코드와 실제 값은 다음과 같다.

db = machbase()
if db.open('127.0.0.1','SYS','MANAGER',5656) is 0 :
return db.result() # 에러 처리
tableName = 'sample_table'
db.columns(tableName)
result = db.result() # result에 컬럼 정보가 들어간다.

그럼 result의 구조를 보면 다음과 같이 리턴된다.

print result;
{"name":"D1","type":"4","length":"2"},{"name":"D2","type":"8","length":"4"},{"name":"D3","type":"12","length":"8"},
{"name":"F1","type":"16","length":"4"},{"name":"F2","type":"20","length":"8"},{"name":"NAME","type":"5","length":"20"},
{"name":"TEXT","type":"49","length":"67108864"},{"name":"BIN","type":"97","length":"67108864"},
{"name":"V4","type":"32","length":"5"},{"name":"V6","type":"36","length":"17"},{"name":"DT","type":"6","length":"8"}


예를 들어 D1 컬럼은 타입이 4이고, 길이가 2라는 것을 알 수 있다.

우리가 필요한 것은 이 타입값이므로, 다음 코드를 통해 타입을 만들 수 있다.

types = []
for item in re.findall('{[^}]+}',result):
types.append(json.loads(item).get('type')) # 타입만을 꺼내서 types 구조체에 입력.
print types;

그래서, 최종적으로 Append에 입력될 sample_table의 types 값은 다음과 같이 출력된다.

[u'4', u'8', u'12', u'16', u'20', u'5', u'49', u'97', u'32', u'36', u'6']

한번만 준비하면 되는 작업이므로 전체적인 성능에는 큰 영향을 미치지 않는다.

aValues

여기는 실제 입력될 값을 array의 배열 형태로 넘기면 된다.

[ [ 컬럼1, 컬럼2...컬럼..n], [ 컬럼1, 컬럼2...컬럼..n], [ 컬럼1, 컬럼2...컬럼..n], [ 컬럼1, 컬럼2...컬럼..n], [ 컬럼1, 컬럼2...컬럼..n]]

이렇게 넘기면, 다수의 레코드를 한번에 입력할 수 있는 구조가 되는 것이다.

예를 들어, sample_table에 다음과 같은 텍스트를 3건 입력한다고 하면, (data.txt라고 하자)

1,2,30000,10000.0,6666.66666667,char-1,text log-1,binary image-1,192.168.9.1,2001:0DB8:0000:0000:0000:0000:1428:1001,2015-05-18 15:26:11
2,4,60000,6666.66666667,5000.0,char-2,text log-2,binary image-2,192.168.9.2,2001:0DB8:0000:0000:0000:0000:1428:1002,2015-05-18 15:26:12
3,6,90000,5555.55555556,4444.44444444,char-3,text log-3,binary image-3,192.168.9.3,2001:0DB8:0000:0000:0000:0000:1428:1003,2015-05-18 15:26:13

이 구조는 다음과 같은 코드로 입력될 것이다.

with open('data.txt','r') as f:
    for line in f.readlines():
    v = []
    i = 0
    for l in line[:-1].split(','):
    t = int(types[i])
        if t == 4 or t == 8 or t == 12 or t == 104 or t == 108 or t == 112:
            #short integer long ushort uinteger ulong
            v.append(int(l))
            elif t == 16 or t == 20:
            #float double
            v.append(float(l))
        else:
            v.append(l)
            i+=1
            values.append(v)

위의 코드에서 볼 수 있듯이 텍스트로 된 입력화일의 각 컬럼이 각각의 타입별로 배열 values 에 append 됨을 알 수 있다.

실제 입력이 끝난 values를 내부를 출력해 보면 다음과 같다.

print values;
[[1, 2, 30000, 10000.0, 6666.66666667, 'char-1', 'text log-1', 'binary image-1', '192.168.9.1', '2001:0DB8:0000:0000:0000:0000:1428:1001', '2015-05-18 15:26:11'],
[2, 4, 60000, 6666.66666667, 5000.0, 'char-2', 'text log-2', 'binary image-2', '192.168.9.2', '2001:0DB8:0000:0000:0000:0000:1428:1002', '2015-05-18 15:26:12'],
[3, 6, 90000, 5555.55555556, 4444.44444444, 'char-3', 'text log-3', 'binary image-3', '192.168.9.3', '2001:0DB8:0000:0000:0000:0000:1428:1003', '2015-05-18 15:26:13']]

앞의 그냥 insert 구문을 텍스트로 하는 것보다는 매우 빠르게 입력될 것이다.  왜냐하면, 실제 데이터 값을 직접적으로 전송하기 때문이다.

aFormat

이것은 입력되는 레코드 중에 datetime 포맷이 있는 경우 해당 포맷을 지정하는 것이다.

논리적으로는 스키마에 존재하는 datetime 컬럼마다 하나씩 포맷이 지정되어야 하지만, 현재의 구조는 단일 포맷으로 구성되어 있는 제약이 있다.

위의 입력 포멧에 따라 ‘YYYY-MM-DD HH24:MI:SS’ 로 지정하면 잘 동작한다.

이 타임 포맷에 대한 자세한 사항은 TO_CHAR 함수의 매뉴얼을 참고하면 될 듯 하다.
(http://krdoc.machbase.com:8090/pages/viewpage.action?pageId=3178517#id-지원함수-TO_CHAR)

100만건의 샘플 데이터를 대량으로 입력해 보기

마크베이스의 파이썬 샘플 디렉토리로 가 보자.

sjkim2@myhost:~$ cd $MACHBASE_HOME/sample/python
sjkim2@myhost:~/machbase_home/sample/python$ ls -al
-rw-rw-r-- 1 sjkim2 sjkim2 767 8월 9 11:45 MakeData.py
-rw-rw-r-- 1 sjkim2 sjkim2 624 8월 3 12:12 Makefile
-rw-rw-r-- 1 sjkim2 sjkim2 693 8월 9 11:14 Sample1Connect.py
-rw-rw-r-- 1 sjkim2 sjkim2 2424 8월 9 11:14 Sample2Simple.py
-rw-rw-r-- 1 sjkim2 sjkim2 1999 8월 9 11:42 Sample3Append.py
-rw-rw-r-- 1 sjkim2 sjkim2 0 8월 3 12:12 __init__.py
sjkim2@myhost:~/machbase_home/sample/python$ 

여기에서 100만건 데이터 생성을 위해 MakeData.py를 수정하자.

def makeData():
    with open('data.txt','w') as f:
        for i in range(1,1000001): # <=== 이 값을 1000001로 수정.
            text = str(i%32768)+","+str(i+i)+","+str((long)(i+i+i)*10000)+","+str((float)(i+2)/(i+i+i)*10000)+","+str((float)(i+1)/(i+i+i)*10000)+",char-"+str(i)+",text log-"+str(i)+",binary image-"+str(i)+",192.168.9."+str(i%256)+",2001:0DB8:0000:0000:0000:0000:1428:"+str(i%8999+1000)+",2015-05-18 15:26:"+str(i%40+10)+"\n";
    f.write(text)
if __name__=="__main__":
    makeData()    

make를 그냥 수행하면, data.txt가 자동으로 생성이 된다. wc를 통해서 생성된 화일의 레코드 건수를 확인해 보자.

sjkim2@myhost:~/machbase_home/sample/python$ make
/home/sjkim2/machbase_home/webadmin/flask/Python/bin/python MakeData.py
sjkim2@myhost:~/machbase_home/sample/python$ wc -l data.txt
1000000 data.txt
sjkim2@myhost:~/machbase_home/sample/python$

1000000만건이 생성되었음을 알 수 있다.

이제 고속으로 입력해 보자.

입력은 이미 샘플이 작성되어 있는 Sample3Append.py를 다시 수행하면 되는데, 시간 측정을 위해 앞뒤로 date 명령어를 넣고 다음과 같이 수행해 보았다.

sjkim2@myhost:~/machbase_home/sample/python$ date; make run_sample3; date
2017. 08. 09. (수) 11:54:09 KST
/home/sjkim2/machbase_home/webadmin/flask/Python/bin/python Sample3Append.py
{"EXECUTE RESULT":"Append success"}
2017. 08. 09. (수) 11:54:54 KST
sjkim2@myhost:~/machbase_home/sample/python$ 

실제로 데이터가 입력되었는지 machsql로 확인해 보자.

Mach> select count(*) from sample_table;
count(*)
-----------------------
1000000
[1] row(s) selected.
Elapsed time: 0.001

1000000만건 입력에 45초 정도 걸렸기 때문에 초당 2만 2천건 정도를 로딩할 수 있었던 것 같다.

물론, 더 빠르게 하려면 C 프로그램이나 csvimport 혹은 machloader를 사용하면 충분할 것으로 생각되며, 파이썬을 통해 응용 프로그램 내부에서 빠른 데이터
로딩 시에는 매우 유용하게 사용될 수 있을 것이다.

Contact the Machbase team with your questions!
info@machbase.com

연관 포스트

C언어로 Binary data를 Machbase에 넣기

1.개요 Data를 다루다 보면 numeric, varchar 형 데이터뿐만 아니라 JPG, PNG, MP3와 같은 Binary data도 database에 저장해야 할 때가 존재한다. 그러나 일반 data들과는 달리 Binary

[MACHBASE 연동] Android studio에서 JDBC 연결하기

마크베이스 기술지원본부 이현민 1. 개요 수많은 데이터들이 많은 환경에서 생성되고 있는 오늘날, 우리 현대인들의 동반자인 스마트폰 또한 데이터생성의 주체로써 또는 전달자로서 알게 모르게 그 구실을