Posting

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

[MACHBASE 테스트] YCSB 퍼포먼스

마크베이스 YCSB 퍼포먼스 입력 테스트 수행

마크베이스 전임연구원 조용찬

이 포스팅에서는 TPCx-IoT 테스트를 통해 마크베이스의 뛰어난 성능을 신뢰도있는 밴치마크를 통해 검증하기위해서, TPCx-IoT의 밴치마크 테스트를 수행하는 과정에 기반이되는 YCSB (Yahoo Cloud Serving Benchmark) 테스트 프로그램을 마크베이스에 연결해서 수행하는 방법에 대해 정리해보고자 합니다.

TPCx-IoT

YCSB 를 알기 전에, 이 테스트 프로그램을 차용한 TPCx-IoT 에 대해 알아야 합니다. 우선 TPC는 트랜잭션 처리 및 데이터베이스 벤치 마크를 정의하고 객관적이고 입증 가능한 성능 데이터를 업계에 보급하기 위해 설립 된 비영리 법인입니다. 실제 업계에서 통상적으로 사용되는 패턴들을 테스트화시켜서 장비나 서비스들이 사용 사례에따라 어떠한 성능을 가지고있는지 테스트들을 구성합니다. 그중 TPCx-IoT는 실시간 데이터 입력과 검색에 초점이 맞추어진 테스트 케이스입니다. IoT 게이트웨이를 위한 다양한 소프트웨어 및 하드웨어 솔루션을 직접 비교할 수 있는 벤치마크입니다. 데이터의 영구 저장, 데이터의 집계, 실시간 분석등의 기능을 수행합니다. 일반적으로 실시간 분석 쿼리를 실행하면서 대용량의 데이터를 대량의 장치에서 수집하는 환경에서의 가격 대비 성능을 확인할 수 있습니다. IoT 환경에서의 machbase 성능을 명시적으로 확인하기위해서 현재의 내용을 진행중에 있습니다.

현재 벤치마크가 완료된 사례로 Dell의 PowerEdge, Cisco의 UCS Mini가 있습니다. 자세한 내용은 TPCx-IoT 페이지를 참고해주세요.

YCSB

Yahoo 에서 만든 Cloud Service 용 Benchmark 테스트 프로그램을 말합니다. 여러 클라우드 서비스의 성능을 측정해서 더 적합한 서비스를 찾아내기 위한 테스트입니다. java를 기반으로 작성된 테스트로, 패키지 관리는 maven으로 작성되어 있습니다. 서비스가 지원해야하는 인터페이스는 총 다섯가지로 init, insert, update, read, delete가 있습니다. 각 Data Service Vender 는 이 인터페이스에 맞춰 작성을 해 줘야 하는데, 이 포스팅에서 마크베이스는 init과 insert만 작성해보도록 합니다.

준비하기

자바기반이기때문에 플랫폼의 영향을 받지않을 것으로 보이지만, 익숙한 리눅스에서 테스트 준비를 진행합니다.

메이븐 저장소 관리하기

ycsb에서의 빌드는 기본적으로 mvn(메이븐)을 통해서 수행되고 있습니다. 그리고 빌드를 위해 필요한 ycsb 코어를 비롯해, 각 DB에서 작성한 client 또한 메이븐의 저장소에 모두 등록이 되어있습니다. 다른 DB 라면 이미 등록되었겠지만, 마크베이스의 JDBC 는 아직 등록되어 있지 않습니다. 이런 경우에는 새로운 클라이언트를 수동 추가하기 위해서는 이 저장소 문제를 가장 먼저 해결해야 했습니다. 원격 저장소를 포함해 로컬 저장소를 추가하는 방법을 사용했습니다. 메이븐은 모든 설정을 pom.xml 파일을 통해서 수행하며, 각 디렉토리마다 수행을 위한 pom.xml 을 포함하고 있도록 되어있습니다. 이 중에서 가장 상위에 존재하는 pom.xml 파일을 super pom이라고 하는데, 해당 파일의 설정값이 하위의 모든 디렉토리에서 사용될 수 있습니다. 여기에 로컬 저장소를 추가할 경우, 이후에 모든 곳에서 메이븐의 메인 저장소와 로컬 저장소를 모두 탐색하게 됩니다. 따라서, 최상위 pom.xml에 아래의 내용을 추가합니다. 로컬 저장소를 추가해도 기본값으로 설정되어있는 메이븐 저장소는 자동으로 함께 탐색됩니다.

local-repo
local repository
file://${project.basedir}/lib

메이븐 라이브러리 경로제가 테스트를 작성한 환경에서는 YCSB 프로젝트가 받아져있는 최상위 디렉토리의 lib 폴더를 로컬 저장소로 지정했습니다.

메이븐 라이브러리 경로

메이븐은 기본적으로 다양한 ‘관습(Convetion)’ 을 유지하고 권장하고 있습니다. 관습이 익숙한 사람들에게는 빠른 능률을 제공한다는 경향입니다. 적응되면 작업 효율이 올라가는 효과가 있을 것 같습니다만.. 메이븐과 자바에 익숙하지 않은 유저에게 학습하는데 많은 에너지를 쓰게 만듭니다. 예를 들자면 아래와 같습니다.

super pom.xml
5.5.0
bindings.properties
machbase:com.yahoo.ycsb.db.MachbaseClient

위와 같이 서로 다른 위치에 있는 파일들의 설정들이 서로 유기적으로 작용하게 됩니다. version 값에 대해서 한번 설정했을 뿐이지만, 해당 값들이 라이브러리의 위치에 일일이 관여합니다. 다른 db의 저장소 경로를 확인해서 유사하게 입력해보는등 여러 차례의 시도가 필요했습니다.

super pom.xml

최상위 pom.xml에는 사용될 마크베이스의 현재 버전 정보와 메이븐이 빌드할 모듈중에 machbase가 추가되었음을 명시해주어야합니다.machbase.version값은 이후 machbase lib의 경로를 참고 할 때 사용됩니다. module은 빌드를 시도할 하위 디렉토리를 의미합니다.

5.5.0

machbase

distribution

machbase의 ycsb binding 패키지 설정을 추가해야합니다. 위치는 YCSB_HOME/distrivution/pom.xml입니다. 아래에 아래의 내용을 추가합니다.

com.yahoo.ycsb
machbase-binding
${project.version}

마크베이스 인터페이스 작성

마크베이스 클라이언트 작성을 위한 디렉토리가 필요합니다. 위 module에서 입력했던 machbase 디렉토리를 최상위에 생성합니다. 그리고 모든 디렉토리가 그렇듯 pom.xml를 추가해서 해당 디렉토리에 대한 정보를 작성합니다. 각 디렉토리는 해당 데이터베이스와 관련된 binding.jar를 생성하는것을 목표로 합니다. ycsb가 제공하는 인터페이스에 부합하는 client 파일이 포함된 binding.jar를 만들기위해서 필요한 설정입니다. 거의 대부분의 설정은 다른 데이터베이스와 유사성을 가지기에 비슷하게 작성하면되며, dependency만 필요한 내용들을 추가하면됩니다. 단, 새로 추가되는 클라이언트 프로그램에 대한 설정값들은 디렉토리에도 사용되기때문에, 환경 설정이 완료될때까지는 기억하고 있으면 도움이 됩니다.

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  4.0.0
  
    com.yahoo.ycsb
    binding-parent
    0.16.0-SNAPSHOT
    ../binding-parent
  
  machbase-binding
  Machbase Binding
  jar
  
      com.machbase.client
      machbase-client
      ${machbase.version}
    
      com.yahoo.ycsb
      core
      ${project.version}
      provided
    
      org.slf4j
      slf4j-api

위의 파일을 생성하고 난뒤에 실제로 마크베이스에 대응하는 client를 작성해야합니다. 클라이언트 자바 파일의 위치는 $YCSB_HOME/machbase/src/main/java/com/yahoo/ycsb/db 입니다. 파일이름은 bindings.property에서 지정했던 MachbaseClient.java로 하도록 하겠습니다. 위의 파일을 생성하고 난뒤에 실제로 마크베이스에 대응하는 client를 작성해야합니다.

MachbaseClient.java

클라이언트 프로그램에서 작성해야하는 함수는 총 다섯개로 init, insert, delete, update, read가 있습니다. 데이터의 입력을 확인하는 테스트를 진행시키기위해서 init과 insert 함수의 작성을 진행해보겠습니다. mvn 빌드 시, 자바 파일의 문법을 테스트하는 과정이 포함되어있으니, 문법을 맞춰서 작성해좋습니다.(ex, indent space 2)

init

서버와 접속하기위한 기본 설정값들을 획득해와서 연결을 획득해 둡니다. insert 이전의 과정은 모두 init에서 끝나야하므로, 이후 다른 과정에서 전처리가 필요하다면 모두 init에서 설정되어야합니다.

@Override
  public void init() throws DBException {
    Properties sProps = getProperties();
    String host = sProps.getProperty(HOST_PROPERTY, "localhost");
    String port = sProps.getProperty(PORT_PROPERTY, "5656");
    String url = sProps.getProperty(URL_PROPERTY, "jdbc:machbase://"+host+":"+port+"/mydb");

    try {
      Class.forName("com.machbase.jdbc.driver");
      conn = java.sql.DriverManager.getConnection(url, sProps);
    } catch (ClassNotFoundException ex) {
      System.err.println("Exception : unable to load machbase jdbc driver class");
    } catch (Exception e) {
      System.err.println("Exception : " + e.getMessage());
    }
  }

insert

데이터를 insert문을 이용해 데이터를 입력하도록 합니다. 1. 반복적인 statement 생성을 방지하기위해 테이블 이름을 비교해서 테이블 변경을 체크합니다. 2. 테이블이 지정되지않았거나 테이블이 변경되었을 경우, prepareStmt를 생성하면서 insert쿼리를 입력해주어야하기 때문에 키리스트와 같은 갯수의 물음표 리스트를 iterator를 이용해서 생성합니다. 첫 컬럼은 ycsb_key로 사용하기로 정했기때문에 배열의 값을 추가하기전에 지정해둡니다. 3. key에서 지정했던것처럼 1번째에 key를 등록하고, 나머지 값들을 이어서 setString을 이용해서 지정합니다. 4. executeUpdate()를 통해서 insert문을 실행합니다. 위의 과정을 통해 한번의 insert가 실행될 때마다 하나의 레코드가 데이터베이스에 입력됩니다.

@Override
  public Status insert(final String table, final String key, final Map values) {
    try {
      if (!table.equals(tableName)) {
        Set set = values.keySet();
        Iterator iterator = set.iterator();
        String keys = "ycsb_key";
        String fieldsQues = "?";
        int count = 1;
        while(iterator.hasNext()){
          keys += "," + (String)iterator.next();
          fieldsQues += ",?";
        }
        String insertQuery = "INSERT INTO "+table+"("+keys+") values ("+fieldsQues+")";
        preStmt = (MachPreparedStatement)conn.prepareStatement(insertQuery);
        tableName = table;
      }
      preStmt.setString(count, key);
      count++;
      for (Map.Entry entry : values.entrySet()) {
        preStmt.setString(count, entry.getValue().toString());
        count++;
      }
    preStmt.executeUpdate();
  } catch (Exception e) {
    if (log.isErrorEnabled()) {
      log.error("Could not insert value for key " + table +":"+key, e);
    } else {
      System.out.println(e.toString());
    }
    return Status.ERROR;
  }
  return Status.OK;
}

라이브러리 배치

초기에 로컬저장소 설정을 했었습니다. 해당 위치에 machbase.jar 파일을 위치시켜줘야 합니다.

file://${project.basedir}/lib + com/machbase/client/machbase-client/5.5.0/machbase-client-5.5.0.jar

기본경로로 지정했던 /lib 에서부터 com~5.5.0로 필요한 경로가 설정한 값에 의해 지정됩니다. 해당 위치에 jdbc jar파일을 위치시킵니다.

YCSB 바이너리 수정

마지막 단계로 ycsb 바이너리에 machbase를 추가해주어야합니다. YCSB_HOME/bin/ycsb 파일을 열어서 DATABASES 키값쌍에 아래의 내용을 추가합니다. 아래의 내용을 추가하고나면, 바이너리 파일에서도 타겟 클라우드 서비스로 마크베이스가 추가되게됩니다.

DATABASES = {
  ...
  "machbase" : "com.yahoo.ycsb.db.MachbaseClient",
  ...
} 

빌드

패키지를 빌드하기 위해서는 설정이 모두 끝난 뒤, YCSB 루트 디렉토리로 이동합니다.

mvn clean package -U

실행 전 테이블 생성결과는 아래와 같습니다. 약식으로 machbase와 ycsb관련 내용만 출력했습니다.

[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO]
[INFO] YCSB Root .......................................... SUCCESS [ 0.910 s]
[INFO] Core YCSB .......................................... SUCCESS [ 3.688 s]
[INFO] Per Datastore Binding descriptor ................... SUCCESS [ 0.125 s]
[INFO] YCSB Datastore Binding Parent ...................... SUCCESS [ 0.477 s]
[INFO] Couchbase Binding .................................. SUCCESS [ 0.771 s]
[INFO] Machbase Binding ................................... SUCCESS [ 1.151 s]
[INFO] YCSB Release Distribution Builder .................. SUCCESS [ 0.607 s]
[INFO] ------------------------------------------------------------------------

실행 전 테이블 생성

테이블 생성과 관련된 스크립트는 작성한 바가 없으므로 machsql을 통해서 미리 테이블을 생성해주어야합니다. 한개의 varchar 키와 열개의 text field를 가지는 테이블을 생성합니다. 테이블 이름은 테스트 프로퍼티에 usertable로 설정되있으므로 통일해줍니다. 완성된 생성 구문은 아래를 참고합니다.

CREATE TABLE usertable (
 YCSB_KEY VARCHAR(255),
 FIELD0 TEXT, FIELD1 TEXT,
 FIELD2 TEXT, FIELD3 TEXT,
 FIELD4 TEXT, FIELD5 TEXT,
 FIELD6 TEXT, FIELD7 TEXT,
 FIELD8 TEXT, FIELD9 TEXT
);

실행

실행할 바이너리는 ./bin/ycsb입니다. load 커맨드를 사용해 데이터를 입력합니다. 타겟 서비스는 지금까지 추가한 machbase입니다. 사용할 프로퍼티는 workloads/workloada로 총 1000건의 데이터를 입력하는 테스트입니다. 

./bin/ycsb load machbase -P workloads/workloada

위의 명령으로 실행할 수 있으며, 아래와 같은 결과가 출력됩니다. 아래처럼 진행한 과정에 대한 내용이 나오며, 전체, 각 과정에서의 성능에 대한 값이 출력됩니다. 실제로 데이터가 잘 들어갔는지 확인하기위해서는 machsql로 데이터베이스에서 확인해보시면됩니다.

[WARN]  Running against a source checkout. In order to get our runtime dependencies we'll have to invoke Maven. Depending on the state of your system, this may take ~30-45 seconds
[DEBUG]  Running 'mvn -pl com.yahoo.ycsb:machbase-binding -am package -DskipTests dependency:build-classpath -DincludeScope=compile -Dmdep.outputFilterFile=true'
java -cp /home/hanchi/work/YCSB/machbase/conf:/home/hanchi/work/YCSB/machbase/target/machbase-binding-0.16.0-SNAPSHOT.jar:/home/hanchi/.m2/repository/org/apache/htrace/htrace-core4/4.1.0-incubating/htrace-core4-4.1.0-incubating.jar:/home/hanchi/.m2/repository/org/slf4j/slf4j-api/1.7.25/slf4j-api-1.7.25.jar:/home/hanchi/work/YCSB/core/target/core-0.16.0-SNAPSHOT.jar:/home/hanchi/.m2/repository/org/hdrhistogram/HdrHistogram/2.1.4/HdrHistogram-2.1.4.jar:/home/hanchi/.m2/repository/com/machbase/client/machbase-client/5.5.0/machbase-client-5.5.0.jar:/home/hanchi/.m2/repository/org/codehaus/jackson/jackson-mapper-asl/1.9.4/jackson-mapper-asl-1.9.4.jar:/home/hanchi/.m2/repository/org/codehaus/jackson/jackson-core-asl/1.9.4/jackson-core-asl-1.9.4.jar com.yahoo.ycsb.Client -db com.yahoo.ycsb.db.MachbaseClient -P workloads/workloada -p machbase.port=20000 -load
Command line: -db com.yahoo.ycsb.db.MachbaseClient -P workloads/workloada -p machbase.port=20000 -load
YCSB Client 0.16.0-SNAPSHOT
Loading workload...
Starting test.
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
DBWrapper: report latency for each error is false and specific error codes to track for latency are: []
[OVERALL], RunTime(ms), 123
[OVERALL], Throughput(ops/sec), 8130.081300813008
[TOTAL_GCS_PS_Scavenge], Count, 0
[TOTAL_GC_TIME_PS_Scavenge], Time(ms), 0
[TOTAL_GC_TIME_%_PS_Scavenge], Time(%), 0.0
[TOTAL_GCS_PS_MarkSweep], Count, 0
[TOTAL_GC_TIME_PS_MarkSweep], Time(ms), 0
[TOTAL_GC_TIME_%_PS_MarkSweep], Time(%), 0.0
[TOTAL_GCs], Count, 0
[TOTAL_GC_TIME], Time(ms), 0
[TOTAL_GC_TIME_%], Time(%), 0.0
[CLEANUP], Operations, 1
[CLEANUP], AverageLatency(us), 2.0
[CLEANUP], MinLatency(us), 2
[CLEANUP], MaxLatency(us), 2
[CLEANUP], 95thPercentileLatency(us), 2
[CLEANUP], 99thPercentileLatency(us), 2
[INSERT], Operations, 1000
[INSERT], AverageLatency(us), 79.504
[INSERT], MinLatency(us), 38
[INSERT], MaxLatency(us), 6391
[INSERT], 95thPercentileLatency(us), 219
[INSERT], 99thPercentileLatency(us), 328
[INSERT], Return=OK, 1000

입력 확인

$ machsql
=================================================================
     Machbase Client Query Utility
     Release Version d630f5f.develop
     Copyright 2014 MACHBASE Corporation or its subsidiaries.
     All Rights Reserved.
=================================================================
Machbase server address (Default:127.0.0.1) :
Machbase user ID  (Default:SYS)
Machbase User Password :
MACHBASE_CONNECT_MODE=INET, PORT=20000
Type 'help' to display a list of available commands.
Mach> select count(*) from usertable;
count(*)
-----------------------
1000
[1] row(s) selected.
Elapsed time: 0.000
Mach>

데이터가 잘 입력된 것을 확인할 수 있습니다.이상으로 YCSB에서 가장 간단한 load 테스트인 workloads/workloada를 실행해보기위한 최소한의 설정에 대해서 나열해보았습니다.마크베이스는 로그 데이터에 대한 UPDATE/DELETE 를 지원하지 않으므로 YCSB 가 요구하는 delete(), update() 를 만들 수 없습니다.read(), scan() 함수를 추가로 작성하고 궁극적인 목표인 TPCx-IoT 테스트를 준비하기 위한 추가 설정들은 다음 시간이 다뤄보고자 합니다.

연관 포스트

C언어로 Binary data를 Machbase에 넣기

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

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

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