Posting

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

[MACHBASE Go] Golang 모듈

golang용 Machbase module 사용방법

post-thumbnail

개요

이전의 글(cgo 로 마크베이스와 go 프로그램을 연동해 보자)에서 설명한 바와 같이 golang에서 Machbase를 사용하려면 cgo를 사용하여야 한다. 
하지만 EdgeMaster를 개발하면서 cgo로 Machbase와 go 프로그램를 연동하여 사용을 해보니 불편한 점이 많았다. 그 중에서도 가장 어려운 점은 C language를 알아야지만 프로그램을 수정할 수 있다는 것이었다.
이 점을 개선하기 위하여 golang만으로 사용이 가능한 Machbase module을 만들었다. 시간이 부족한 관계로 필요한 부분만 먼저 개발하였기 때문에 모든 기능을 제공하지는 않지만 필수적인 기능은 대부분 구현되어 있다.
golang을 사용하는 개발자들에게 조금이라도 도움이 되었으면 하는 마음에 소스를 공개한다.

이 글에서는 Machbase module의 기본적인 사용방법을 간단한 예제를 통해 알아보겠다.

이 글은 golang과 Machbase에 대한 기본적인 지식이 있다는 가정 아래 작성되었으며, 여기에서 설명하는 소스와 Machbase module은 아래의 링크에서 다운로드 받을 수 있다.
github 링크 : https://github.com/MACHBASE/MachbaseModule4golang

시작하기 전에

Machbase module은 Connection과 Statement로 구성된다.
Connection은 Machbase와 연결을 하는 struct이고, Statement는 연결된 Connection에서 생성되어 작업을 수행하기 위한 struct이다.
하나의 Connection에 여러 개의 Statement를 생성하여 Select와 같은 Query를 수행하거나 Append를 사용하여 Data를 추가한다.

준비사항

machbase CLI 라이브러리를 추가해야 한다.
$MACHBASE_HOME 폴더에 machbase가 설치되었다면, 다음과 같이 환경 변수를 추가한다.

export LD_LIBRARY_PATH=$MACHBASE_HOME/lib:$LD_LIBRARY_PATH
export CGO_CFLAGS="-I$MACHBASE_HOME/include"
export CGO_LDFLAGS="-L$MACHBASE_HOME/lib -lmachbasecli_dll"

machbase 접속

var sMachbaseConnect *machbase.MachbaseConnect = nil
var RC_SUCCESS int = 0
var RC_FAILURE int = -1
 
// create connect struct
sMachbaseConnect = machbase.CreateConnect()
 
defer func() {
    if sMachbaseConnect.DisconnectDB() == RC_FAILURE {
        fmt.Println("Machbase DisConnect Fail")
    } else {
        fmt.Println("Machbase DisConnect Success")
    }
    sMachbaseConnect = nil
}()
 
// create driver for db connect
sIp := "127.0.0.1"
sPort := "5656"
sId := "SYS"
sPass := "MANAGER"
sDriver := "SERVER=" + sIp + ";UID=" + sId + ";PWD=" + sPass + ";CONNTYPE=1;PORT_NO=" + sPort + ";CONNECTION_TIMEOUT=3"
 
// db conenct
if sMachbaseConnect.ConnectDB(sDriver) == RC_FAILURE {
    fmt.Println(sMachbaseConnect.PrintConErr())
} else {
    fmt.Println("Machbase Connect Success!!")
}

return

sMachbaseConnect = machbase.CreateConnect()

위 구문은 Machbase에 접속하기 위한 준비물인 MachbaseConnect struct를 생성받는 과정이다.
CreateConnect 함수는 MachbaseConnect struct를 생성하고 그 주소 값을 반환해주는 함수이다.

sMachbaseConnect.ConnectDB(sDriver)

위 구문이 Machbase에 접속하는 과정이다.
sDriver는 마크베이스에 사용되는 접속 문자열로 Machbase 매뉴얼에 자세하게 나와 있으니 참조하여 사용하면 된다.
sDriver를 ConnectDB 함수에 argument로 넘겨주면 Machbase에 접속 문자열 정보를 통해 접속을 시도하고 성공하면 0 실패하면 -1이 반환된다.
실패한 경우 sMachbaseConnect.PrintConErr()를 통해 접속에 실패한 원인을 확인할 수 있다.

sMachbaseConnect.DisconnectDB()

Machbase와 접속을 해제하고 할당받은 메모리를 해제한다.
이때 주의할 점은 sMachbaseConnect가 nil인 상태에서 DisconnectDB함수를 호출하면 panic이 발생하므로 if조건으로 nil이 아닐 때만 DisconnectDB함수를 호출하도록 해야 한다.
또한 Machbase접속에 실패하더라도 DisconnectDB함수를 호출해야 메모리 해제 과정을 거치므로 접속에 실패했다고 DisconnectDB함수를 호출하지 않는 상황이 없도록 한다.

Statement 생성

var sMachbaseConnect *machbase.MachbaseConnect = nil
var sMachbaseStmt *machbase.MachbaseStmt = nil
 
var RC_SUCCESS int = 0
var RC_FAILURE int = -1
  
// create connect struct
sMachbaseConnect = machbase.CreateConnect()
  
defer func() {
    if sMachbaseStmt.FreeStmt() == RC_FAILURE {
        fmt.Println("Machbase Free Statement Fail")
    } else {
        fmt.Println("Machbase Free Statement Success")
    }
    sMachbaseStmt = nil
 
    if sMachbaseConnect.DisconnectDB() == RC_FAILURE {
        fmt.Println("Machbase DisConnect Fail")
    } else {
        fmt.Println("Machbase DisConnect Success")
    }
    sMachbaseConnect = nil
}()
 
sDriver := "SERVER=127.0.0.1;UID=SYS;PWD=MANAGER;CONNTYPE=1;PORT_NO=5656;CONNECTION_TIMEOUT=3"
 
// db conenct
if sMachbaseConnect.ConnectDB(sDriver) == RC_FAILURE {
    fmt.Println(sMachbaseConnect.PrintConErr())
    return
} else {
    fmt.Println("Machbase Connect Success!!")
}
 
sMachbaseStmt = sMachbaseConnect.CreateStmt()
 
// stmt alloc
if sMachbaseStmt.AllocStmt() != RC_SUCCESS {
    fmt.Println(sMachbaseStmt.PrintStmtErr())
} else {
    fmt.Println("Machbase Statement Alloc Success!!")
}
 
return

Statement 생성도 Machbase에 접속하는 것과 비슷하다.

sMachbaseStmt = sMachbaseConnect.CreateStmt()

정상적으로 Machbase와 접속된 sMachbaseConnect에 CreateStmt함수를 호출해서 Statement 생성에 필요한 struct 주소를 반환받아 변수에 저장한다.

sMachbaseStmt.AllocStmt()

Statement를 생성하는 과정이다. 정상적으로 생성했다면 0, 실패했다면 -1을 반환한다.
마찬가지로 Statement 생성에 실패했으면 sMachbaseStmt.PrintStmtErr()함수를 통해 실패 원인을 알 수 있다.
접속 실패는 PrintConErr()이고 Statement 생성 실패는 PrintStmtErr()이니 구분하여야 한다.
PrintStmtErr함수는 Statement 생성 실패뿐만 아니라 밑에서 설명할 Statement관련 작업들의 실패 원인도 알 수 있는 함수이다.

sMachbaseStmt.FreeStmt()

Statement를 삭제하고 메모리를 해제한다.
sMachbaseConnect에서 설명한 바와 같이 sMachbaseStmt가 nil인 상태에서 FreeStmt함수를 호출하면 panic이 발생하므로 if조건을 통해 nil이 아닐 때만 FreeStmt함수를 호출해야 한다.
Statement 삭제 및 Machbase 접속 해제를 하는 상황이라면 항상 Statement를 먼저 삭제하고 Machbase 접속을 해제해야 한다.

이제부터는 Machbase에 접속이 되어있고 Statement까지 생성이 되어있다는 가정하에 코드 예제를 설명한다.

쿼리실행

sSql := "create tagdata table TAG (name varchar(80) primary key, time datetime basetime, value double summarized)"
 
// create tag table
if sMachbaseStmt.ExecDirect(sSql) == RC_SUCCESS {
    fmt.Println("Create Tag Table Success!!")
} else {
    fmt.Println("Create Tag Table Fail : ",sMachbaseStmt.PrintStmtErr())
}

Select를 제외한 Create, Insert, Delete 등 쿼리를 실행할 수 있다.
Tag Table를 생성하는 것을 예로 들면 아래와 같이 본인이 원하는 쿼리를 string으로 만들어준다.
sSql := “create tagdata table TAG (name varchar(80) primary key, time datetime basetime, value double summarized)”

sMachbaseStmt.ExecDirect(sSql)

Create, Insert, Delete등 완성된 쿼리를 실행하는 함수이다.
ExecDirect라는 함수에 argument로 본인이 만든 쿼리를 넘기면 Machbase에서 실행을 하고 성공했다면 0, 실패했다면 -1이 반환된다.
실패했다면 실패한 원인을 보는 PrintStmtErr함수를 호출하여 확인하면 된다.

Append

sTable := "TAG"
 
// data append open
if sMachbaseStmt.AppendOpen(sTable) != RC_SUCCESS {
    fmt.Println("AppendOpen Fail : ",sMachbaseStmt.PrintStmtErr())
    return
}
 
sType := make([]int, 3)
sValue := make([]string, 3)
//var sDateFormat string = "YYYY-MM-DD HH24:MI:SS"  //use timeformat
var sDateFormat string = ""
var sLen int = 3
sCount := 0
 
for sCount < 100 {
    sType[0] = machbase.MACHBASE_VARCHAR
    sValue[0] = "X1"
         
    sType[1] = machbase.MACHBASE_DATETIME
    //sValue[1] = time.Now().Format("2006-01-02 15:04:05")  //use timeformat
    sValue[1] = strconv.FormatInt(time.Now().UnixNano(), 10)
         
    sType[2] = machbase.MACHBASE_DOUBLE
    s1 := rand.NewSource(time.Now().UnixNano())
    r1 := rand.New(s1)
    sValue[2] = strconv.Itoa(r1.Intn(100))
        
    if sMachbaseStmt.AppendDataV2(sType, sValue, sDateFormat, sLen) != RC_SUCCESS {
        fmt.Println("AppendDataV2 Fail : ",sMachbaseStmt.PrintStmtErr())
    }
 
    sCount++
}
 
if sMachbaseStmt.AppendFlush() != RC_SUCCESS {
    fmt.Println("AppendFlush Fail : ",sMachbaseStmt.PrintStmtErr())
} else {
    fmt.Println("AppendFlush Success!!")
}
 
sResult := sMachbaseStmt.AppendClose()
 
if sResult == RC_FAILURE {
    fmt.Println("AppendClose Fail : ",sMachbaseStmt.PrintStmtErr())
} else {
    fmt.Println("Success Count : ",sResult)
}
 
return

Machbase는 서버에 Data를 초고속으로 입력하기 위해 Append 프로토콜을 제공하고 있다.
Append Open -> Append Data 반복 -> Append Close 의 순서대로 진행된다.
Append함수에 대한 자세한 설명은 아래의 링크를 참조하면 된다.
Append CLI함수 : Machbase 매뉴얼

sMachbaseStmt.AppendOpen(sTable)

Append를 시작하기 위하여 AppendOpen 함수를 사용한다.
AppendOpen에는 Data를 입력할 table명이 필요하다. 여기서는 Tag table에 Data를 입력하기 때문에 sTable에 TAG를 대입하였다.

Append Data 준비

Data를 입력하기 위한 변수는 4가지가 필요하다.

1. table의 column type
2. column에 입력할 Data
3. DateTime Format
4. column 개수

변수 설명

1. table의 column type
type을 저장하기위한 변수 sType을 길이가 3인 []int type으로 생성하였다.
예제로 만들어져있는 Tag table의 column type은 name(varchar), time(datetime), value(double)이다.
column 순서에 맞도록 type을 sType에 저장했으며 column type은 machbase.go를 보면 확인할 수 있다.

2. column에 입력할 Data
각 column에 들어갈 Data를 저장하기 위한 변수 sValue를 길이가 3인 []string type으로 생성하였다.
type같은 경우 []int로 생성해야하고 value는 []string으로 생성해서 넘겨줘야 정상 동작한다.

3. DateTime Format
DateTime Format은 날짜 형식의 Data를 입력하기 위하여 필요하다.
Machbase module은 string 형식과, int64 형식의 nano TimeStamp를 지원한다.
예를 들면,
string 형식의 경우 "2020-02-24 09:30:00"이면 Time Format은 "YYYY-MM-DD hh24:mi:ss"로 설정하고
int64 형식의 경우 1582522308000000000이면 Time Format은 ""로 설정한다.
주의 : int64 형식을 지원하지만 sValue에 저장할 때는 string으로 변환하여 저장해야 한다. -> 1582522308000000000 -> "1582522308000000000"

4. column 개수
column 개수를 필요로 하는데 sType, sValue의 길이 중 하나만 골라 argument로 넘겨주면 된다.
sType과 sValue의 길이는 같아야 한다.

이렇게 준비된 4가지의 변수를 sMachbaseStmt.AppendDataV2(sType, sValue, sDateFormat, sLen)에 argument로 넘겨주면 Data 입력을 실시한다.

sMachbaseStmt.AppendFlush()

채널 버퍼에 쌓여있는 Data를 Machbase 서버로 즉시 전송하는 함수로 프로그램에서 필요한 경우 적절하게 사용하면 된다.
성공할 경우 0, 실패할 경우 -1을 반환한다.

sMachbaseStmt.AppendClose()

Data를 입력하기 위해 열어놓은 채널을 닫는 함수로 AppendDataV2가 끝나면 호출한다.
AppendClose함수는 실패는 -1을 성공은 Append를 성공한 레코드 개수를 반환한다.

Select

sSql := "select * from tag;"
if sMachbaseStmt.Prepare(sSql) != RC_SUCCESS {
    fmt.Println("Prepare Fail : ",sMachbaseStmt.PrintStmtErr())
} else {
    fmt.Println("Prepare Success!!")
}
 
if sMachbaseStmt.Execute() != RC_SUCCESS {
    fmt.Println("Execute Fail : ",sMachbaseStmt.PrintStmtErr())
} else {
    fmt.Println("Execute Success!!")
}
 
sInterfaceArr := make([]interface{}, sMachbaseStmt.GetColCount())
for {
    if sMachbaseStmt.Fetch(sInterfaceArr) == RC_FAILURE {
        fmt.Println("Fetch Fail : ",sMachbaseStmt.PrintStmtErr())
        break
    }
 
    fmt.Println("name : ",sInterfaceArr[0])
    fmt.Println("time : ",sInterfaceArr[1])
    fmt.Println("value : ",sInterfaceArr[2])
    fmt.Println("*****************************************")
}
 
return

Select를 하기 위해서는 아래의 변수를 준비 한다.

- Query 문 : 실행할 Query문
- interface array : 결과를 받을 변수. 결과 column의 수와 같아야 한다.

sMachbaseStmt.Prepare(sSql)

Statement에 Query문을 전달한다.

sMachbaseStmt.Execute()

Query문을 실행한다.

sMachbaseStmt.Fetch(sInterfaceArr)

결과를 전달 받을 변수로 interface array를 생성하여 전달 한다.
interface array의 길이는 Query 결과 column의 수와 같아야 하며 column 개수를 반환해주는 함수인 sMachbaseStmt.GetColCount()를 사용하면 쉽게 만들 수 있다.
interface array를 sMachbaseStmt.Fetch(sInterfaceArr)의 argument로 넘겨주면 Fetch함수에서 interface array에 Fetch한 결과를 입력한다.
반환되는 값이 문자열이면 string으로, 숫자형이면 float64로 입력된다.
따라서 사용자는 반환된 interface의 값을 사용할 형식으로 변환하여 사용해야 한다.
예를 들어 반환된 값이 int로 사용되어야 한다면 int(sInterfaceArr[index].(float64))로 변환해서 사용해야 한다.

마무리

기본적인 기능만 구현하였기 때문에 부족한 부분도 많지만 사용하는데 꼭 필요한 기능들은 갖추었다고 생각한다.
Machbase의 특징을 살리기 위해서는 Insert보다는 Append를 주로 사용하므로 PrepareStatement 기능은 필요성이 떨어진다고 생각되어 제외하였다. 만약 필요하다면 github에 올라가 있는 예제 파일에 machbase.go 소스가 포함되어 있으므로 직접 수정해서 사용해도 무방하다.

현재 개선이 필요하다고 생각하고 있는 것은 Append에서 Data를 argument로 넘길 때 모두 string으로 변환해서 사용해야 하는 것과 Select 결과가 interface array에 입력될 때 string과 float으로만 저장되는 것 등이 있는데 점차적으로 보완할 예정이다.

연관 포스트

C언어로 Binary data를 Machbase에 넣기

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

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

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