0. Introduction

1) memcached + 디스크 persistence 기능 추가 = membase

2) membase 에 Apache CouchDB를 기반으로 새롭게 만든 솔루션임

3) 100% memcached protocol 호환

3) JSON document를 직접 저장할 수 있는 Document DB 형태를 가짐

4) NoSQL의 분산 이론인 CAP 에서 CP (Consistency & Partition tolerance) 의 부분에 해당

5) 데이타에 대한 일관성 보장, 노드간의 네트워크 장애시에도 서비스 제공 가능 

6) XACR(Cross Data Center Replication)이라는 기능을 이용하여 물리적으로 떨어진 데이타 센터간 데이터 복제 가능

7) Indexing, Grouping ,Ordering, Join 가능


1. Couchbase Overview

1) Architecture and Structure

(1) cluster-based system : 클러스터는 모두 동일한 노드들을 가진다.

(2) document based storage system : 각 도큐먼트는 unique한 "document ID" 로 식별됨

- 키는 최대 250 바이트

- 값(Value)은 카우치베이스 버킷의 경우 20MB, Memcachd 방식 버킷은 1MB 까지 저장 가능

- 저장시 키와 값 이외에 메타데이터(CAS, TTL, Flag)가 같이 저장되며 60 bytes 메모리 사용

- 모든 키와 메타데이터를 메모리에 유지함.

(3) data 는 disk 에 영구적으로 저장되며, 빠른 데이터 접근을 위해 cahing layer 를 제공

(4) 클라이언트 라이브러리와 카우치베이스 서버가 어느곳에 필요한 document object 가 있는지 자동으로 처리

2) Buckets and vBuckets

(1) Bucket : RDBMS 의 database 에 해당

- 모든 Bucket 은 식별가능한 이름을 가짐.

- 모든 Bucket 은 자동적으로 클러스터 안에 shard 된다.

- 모든 Bucket 은 패스워드를 지정 할 수 있다.

- 모든 Bucket 은 RAM quota, replication 설정, disk storage 를 가진다.

- 모든 Bucket 은 독립적으로 관리되고 색인된다.

- 각 Bucket 별로 RAM quota, replication 설정, 디스크 저장소를 가진다.

- 하나의 Cluster 내에서 Bucket 의 최대 수는 기본 10개이다. (성능상 보통 10개 권장, 최대 128개까지 생성 가능)

(2) vBucket : Bucket 은 vBucket 으로 나뉘어 Storage 에 저장된다.

- vBucket Map 을 통하여 각 노드로 할당됨.

- document ID (key) 에 대한 hash 값을 계산한 후, 각 hash 값에 따라서 저장되는 vBucket 값을 매핑한 다음, 각 Bucket 값을 노드에 매핑한다. (hash(document ID) -> vBucket -> node)

- 기본적으로 vBucket 은 1024개

- 노드가 추가되면 vBucket Map 이 변경되고, 해당 변경 사항에 따라 각 노드로 데이터가 이동한다. (Rebalancing)

- 클라이언트 SDK 는 vBucket 과 노드에 대한 맵핑 정보를 클러스터로부터 받아서 관리한다. 

- 따라서 클라이언트는 Proxy 등을 거치지 않고 직접 데이터가 저장된 노드로 접근이 가능하다.

3) Data 의 저장과 추출

(1) Basic Operations

- Create : store(DocumentID, DocumentValue)  => add(), set()

- Read : get() => get(DocumentID)

- Update : Update(DocumentID, DocumentValue) => replace(), incr(), decr(), append(), prepend(), cas()

- Delete : delete(DocumentID) => delete()

(2) 모든 operation 은 atomic 함. 즉, 성공 또는 실패이고, multiple client 로 부터의 같은 operation 요청은 순차적으로 처리된다.


4) Time to Live

(1) Lazy Identification : TTL 만료시 데이터가 바로 삭제되지는 않으며, expired 된 데이터에 접근시 존재하지 않는 것과 동일하게 처리됨.

(2) 데이터는 디폴트 1시간 주기로 backgroud 프로세스에 의해 메모리와 디스크로부터 삭제된다.

5) Data Consistency

(1) 각 도큐먼트는 오직 하나의 active location 을 가진다.

(2) 모든 set, get, update 는 같은 서버에서 이뤄진다.

6) Views, Indexes, and Querying

(1) Views : document data 를 원하는 포맷으로 만드는 것

- map/reduce function 에 의해 변환된 데이터를 별도로 저장. (map : 문서 형식을 변환, reduce : summarize)

- 증분적(incremental) map/reduce 사용으로 업데이트된 데이터에 대해서만 데이터가 추가/수정 되기 때문에 로드는 크지 않다.


7) Document ID

(1) 사용 정책 예

- 사용자의 unique한 Data 를 document id로 사용

- prefix + sequence number 를 사용

- UUID 솔루션 이용

(2) 모든 Document meta data와 document ID는 메모리에 저장되므로 사이즈에 대한 고려 필요한

2. 설치

1) Couchbase Server 설치

(1) http://www.couchbase.com/nosql-databases/downloads 에서 OS별 서버 다운로드

(2) 다운로드 받은 파일 설치 (CentOS)

- rpm -i couchbase-server-enterprise-3.0.2-centos6.x86_64.rpm

(3) 11211, 11210, 4369, 8091, 21100~21199 방화벽 포트 해제 (vi /etc/sysconfig/iptables)

-A INPUT -m state --state NEW -m tcp -p tcp --dport 8091 -j ACCEPT

-A INPUT -m state --state NEW -m tcp -p tcp --dport 11211 -j ACCEPT

-A INPUT -m state --state NEW -m tcp -p tcp --dport 11210 -j ACCEPT

-A INPUT -m state --state NEW -m tcp -p tcp --dport 11209 -j ACCEPT

-A INPUT -m state --state NEW -m tcp -p tcp --dport 4369 -j ACCEPT

-A INPUT -m state --state NEW -m tcp -p tcp --dport 21100:21299 -j ACCEPT

(4) service iptables restart

(5) 웹 브라우저로 http://hostname:8091 접속하여 남은 설정 진행

- Location for persistent storage : data 저장 위치

- Server Quota : 클러스터내의 모든 노드에게 공유될 해당 노드의 Couchbase RAM 사용량 (four nodes * 2GB = 8GB)

- Bucket Quota : Bucket 별 Quota 설정

- Bucket Type : memcached (cached only) or Couchbase (persisted and scaleable)

2) Client Library

(1) PHP

- Download the SDK from the SDK Page

 tar xzf php-ext-couchbase-$system-$arch.tar.gz (couchbase.so)

 phpi -i | grep ini (php.ini 위치 확인)

 extension=/path/to/couchbase.so 추가

(2) Code Sample

<?php

$cb = new Couchbase("127.0.0.1:8091", "", "", "default");

$spoon = $cb->get("spoon");

if ($spoon) {

echo "$spoon";

}

else {

echo "There is no spoon.";

$cb->set("spoon", "Hello World!", 10);

}

?>

3. Getting Data In and Out

1) CRUD Operations

(1) add(id, document [, expiry]) : Add an item if ID doesn’t already exist

(2) set(id, document [, expiry]) : Store a document with ID

(3) replace(id, document [, expiry]) : Update the document for an existing ID

(4) cas(id, document, check [, expiry]) : Update the document for an existing ID providing the check matches

(5) get(id) : Get the specified document

(6) incr(id [, offset]) : Increment the document value by offset

(7) decr(id [, offset] : Decrement the document value by offset

(8) append(id, value) : Append the content to the end of the current document

(9) prepend(id, value) : Prepend the content to the beginning of the current document

(10) delete(id) : Delete the specified document

2) TTL

(1) 30일 (30 * 24 * 60 * 60) 보다 작은 값은 상대적인 값으로 인식. 즉, 현재 시간으로 부터 n 초 이후 expire

(2) 30일 이상의 값은 절대적 값(unix time) 으로 인식. (ex : 1381921696 = 16th October 2013)

(3) zero 값은 expire 설정하지 않음을 의미.

3) Storing Data

(1) set(docid, docdata [, expiry])

$cb->set('message', 'Hello World!');

$cb->set('message', 'Hello World!', 10); // 10 초 후 expire

(2) add(docid, docdata [, expiry])

$cb->add('message', 'Hello World!');

4) Retrieving Data

(1) 1 item

$message = $cb->get('message')

(2) N items

- bulk load : View 사용, 개별 load 보다 효율적임

- 라이브러리 사용 : $ret = $cb->getMulti(array('recipe1','recipe2'));

5) Updating Data

(1) document data 전체가 update 되므로, 필요시 미리 데이터를 가져와서 수정 후 update 한다.

(2) $cb->replace("welcome-message", "Hello World!");

(3) $cb->set('user', json_encode($record));

6) Concurrent Updates

(1) compare and swap (cas()) function

- 일종의 타임스탬프와 비슷하게 get 할때 데이터에 체크를 해두고 이때 나오는 CAS ID와 값이 같을때만 update 허용

(2) CAS 시나리오

- Client A 가 document A1 의 값과 CAS ID 를 얻는다.

- Client B 가 document A1 의 값과 CAS ID 를 얻는다.

- Client A 가 변경된 값과 CAS ID 를 가지고 업데이트를 시도하면 성공

- Client B가 변경된 다른 값과 CAS ID 를 가지고 업데이트 시도하면 실패

(3) 예제

$value = client->get("customer", NULL, $casvalue);

$response = client->cas($casvalue, "customer", "new string value");

7) Server-side Updates

(1) Increment 

$cb->increment('counter');

(2) Decrement

(3) Append

$cb->append('message', ' World!');

(4) Prepend

8) Asynchronous Operations

$format_recipe = function($key, $value) {

return ('<li>' . $value['title'] . '</li>');

};

$ret = $cb->getDelayed(array('recipe1','recipe2'),0,$format_recipe);

9) Pessimistic Locking

$recipe = $cb->getAndLock('recipe1', &$cas);

$recipe = 'new value';

# This will fail, because we are not supplying the CAS value

$cb->set('recipe1', $recipe);

# This will succeed and then unlock document

$cb->set('recipe1', $recipe, 0, $cas);

// $cb->unlock('recipe1',$cas); // 명시적 unlock

10) Deleting Data

$cb->delete('message');

4. Storing and Updating

1) Initial Storage

$cb->set(uniqid(),$recipe);

2) connection option 으로 Serializer 설정 가능

$cb->setOption(OPT_SERIALIZER, SERIALIZER_JSON);

3) Storing Related Data : 관련된 document ID 를 값으로 저장해서 참조

$commentid = uniqid();

$commentret = $cb->set($commentid,$comment);

$recipe = $cb->get("00118CCC-3027-11E2-BB0D-B67A7E241592");

array_path($recipe[comments],$commentid);

$ret = $cb->set("00118CCC-3027-11E2-BB0D-B67A7E241592", $recipe, 0, $cas);

4) Loading Related Data : 최초 데이터를 가져온 후 다른 document ID 를 찾아서 조회

$recipe = $cb->get("00118CCC-3027-11E2-BB0D-B67A7E241592");

$comments = $cb->getMulti($recipes['comments']);

5. Views and Queries

1) 소개

(1) NoSQL 솔루션들은 key / value Storage 로 보통 key 를 통해 데이터를 조회하게 된다. 

(2) filtering, indexing, grouping, ordering 과 같은 기능은 뷰를 통해 제공되며

(3) 저장된 document 를 필터링하여 원하는 다른 형태의 데이터 모델로 변환하여 보여 주는 것으로 일종의 읽기 전용 테이블(RDBMS의 view) 로 볼 수 있다.

(4) View 는 Map 과 Reduce 라는 2 개의 definition function 을 가지며 둘 다 자바스크립트로 작성한다.

(5) Bucket 은 N개의 Design Document 를 가지고 각 Design Document 는 N개의 View 를 가질 수 있다.

(6) N 개의 View 를 가지고 있다고 하면, 해당 document 가 변경될 때 동시에 2 개의 view 가 update 되고 index 를 생성할 것이므로 성능상의 고려를 해야 한다.

(7) 버킷당 4개의 Design Document 를, 하나의 Design Document 당 10개 이하의 뷰를 구현하도록 권고

2) Map / Reduce

(1) Map : output format 과 contents 를 정의한다. (required)

- emit 함수를 통해 view key 와 view value 두 가지의 정보를 리턴하면 된다.

- doc 인자는 버킷 내에 저장된 각 document 이며, meta 는 해당 도큐먼트의 메타 데이터(flag, cas ..) 이다.

function (doc, meta)

{

emit (doc.title, doc.content);

}

(2) Reduce : summarize 또는 reduce 가 필요할 경우 옵션으로 사용한다 (optional)

3) Development View

(1) 개발중에 실제 document 의 변경을 회피하고 전체 데이터를 대상으로 작업하는 부하를 막기 위해 제공

(2) "dev_" prefix 를 이용

(3) 기본적으로 작은 범위의 문서들로 제한하여 동작한다. (전체 데이터가 아닌, 부분 데이터로 동작. 전체로 하고 싶을 경우 별도 옵션 사용)

(4) 개발이 완료되면 Production View 로 전환하여 서비스한다.

4) View Contents : view output 은 아래의 정보를 포함

(1) Document ID 

- 모든 emit 호출은 document ID 를 동반

- get() 함수를 이용하여 document를 load 가능

(2) View Key

- emit 함수의 첫번째 인자. 

- 어떻게 search 할 것인지에 따라 구성

- 여러개의 Key 로 구성하고자 할 경우 array 로 작성

(3) View Value

- emit 함수의 두번째 인자

- Value 도 Key 와 함께 index 안에 저장되므로 사이즈를 고려해야 함.

- index 의 의미로만 사용할 경우 (reduce 를 사용 안할 경우 등) value 는 null 로 지정

5) Accessing Views from a Client Library

(1) 기본 사용 예제 

<table>

<tr><td>Recipe Title</td></tr>

<?php

$cb = new Couchbase("127.0.0.1:8091", "recipes", "", "recipes");

$result = $cb->view("dev_recipes", "bytitle"); // design document name, view name

foreach($result["rows"] as $row) {

echo '<tr><td>',$row['key'],'</td></tr>';

}

?>

</table>

(2) 특정 키로 조회 : key

$result = $cb->view("recipes", "bytitle", array('key' => 'Apple Pie'));

(3) 하나 이상의 키로 조회 : keys

$result = $cb->view("dev_recipes", "bytitle", array('keys' => array('Shepherds pie', 'Mariners pie')));

(4) 키 범위로 조회 : startkey, endkey

$result = $cb->view("dev_recipes", "bytitle", array('startkey' => 'Meat loaf','endkey' => 'Mexican tacos'));

(5) UTF-8문자 0xFFFF(가장 큰 문자) 를 이용한 문자열 조회

$result = $cb->view("dev_recipes", "bytitle", array('startkey' => 'Mediterranean','endkey' => 'Mediterranean\uFFFF'));

6) Options

(1) descending : Return the documents in descending by key order

(2) endkey : Stop returning records when the specified key is reached. Key must be specified as a JSON value.

(3) endkey_docid : Stop returning records when the specified document ID is reached

(4) full_set : Use the full cluster data set (development views only).

(5) group : Group the results using the reduce function to a group or single row

(6) group_level : Specify the group level to be used

(7) inclusive_end : Specifies whether the specified end key should be included in the result

(8) key : Return only documents that match the specified key. Key must be specified as a JSON value.

(9) keys : Return only documents that match each of keys specified within the given array. Key must be specified as a JSON value. Sorting is not applied when using this option.

(10) limit : Limit the number of the returned documents to the specified number

(11) on_error : Sets the response in the event of an error. stop will stop returning rows; continue will notify you of the error, but continue returning rows from other nodes.

(12) reduce : Use the reduction function.

(13) skip : Skip this number of records before starting to return the results

(14) stale : Allow the results from a stale view to be used. ok uses a stale index; false forces an index update; update_after updates the index after it has been accessed (default)

(15) startkey : Return records with a value equal to or greater than the specified key. Key must be specified as a JSON value.

(16) startkey_docid : Return records starting with the specified document ID

7) Indexes

(1) Disk 에 write 된 Persisted Document 에 대해 index 작업이 이뤄진다.

(2) 이는 기간, 또는 설정된 도큐먼트의 변경 횟수, 또는 두 경우 모두의 경우 이뤄진다.

(3) Document 가 expire 된 경우는 Background expiry page 의 동작에 의해 제거되고 index 된다.

(4) set() 을 이용한 데이터 저장시 option 값인 persist_to (5번째 인자) 를 이용해 disk 에 바로 저장 가능.

$cb_obj->set("spoon", "Hello World!", 10, NULL, 1);


8) Stale Indexes and Updates

(1) 현재 View(index) 의 상태를 컨트롤

(2) stale Parameter

- update_after : 현재 실행되는 query 다음에 index 갱신 수행. 즉, 다음 query 는 update 된 버전의 view 로 부터 값을 추출하게 된다. (default)

- ok : 현재 버전의 view 에서 바로 추출. 즉, view의 상태를 고려하지 않음

- false : 먼저, 강제로 index 를 update 하고 query 를 수행.


6. Searching and Query 예제

1) 조합키를 이용한 구간 조회

(1) View

function (doc, meta) {

emit([parseInt(doc.totaltime,10), doc.title], null);

}

(2) 조회 코드

$result = $cb->view("bytime", "bytimetitle", array('startkey' => array(0, "Lasagne"), 'endkey' => array(90, Lasagne"),));

// tataltime이 0 이고 "Lasagne" 인 곳에서부터 90 이고 "Lasagne" 인 곳까지 찾게 되므로

// [0,"Mixed iceberg salad with creamy basil dressing"] 과 같은 값이 나오게 된다.

// 따라서 "Lasagne" 이고 0 ~ 90 까지의 값을 찾고 싶으면, 고정 값인 "Lasagne" 을 먼저 둬야 한다.


(3) 수정된 View

function (doc, meta) {

emit([doc.title, parseInt(doc.totaltime,10)], null);

}

(4) 수정된 조회 코드

$result = $cb->view("bytime", "bytimetitle", array('startkey' => array("Lasagne", 0), 'endkey' => array("Lasagne", 90),));


2) Reduction and Built-in Functions

(1) Built-in _count Function : map() 에서 부터 추출되는 모든 rows 의 키별(group) count 값

function (doc, meta) {

if (doc.keywords) {

for (kw in doc.keywords) {

emit(kw,null); // 같은 키(kw) 별로 _count 값을 추출 가능

}

}

}

==>

{

"rows" : [

{

"key" : "convenience@add bread for complete meal",

"value" : 242

},

{

"key" : "convenience@add jacket potato for a complete meal",

"value" : 116

},

...

}


(2) _sum Function : 키별(group) 데이터의 sum 값

function (doc, meta) {

emit(doc.title, parseInt(doc.preptime, 10));

emit(doc.title, parseInt(doc.cooktime, 10));

}

==>

{

"rows" : [

{

"key" : "",

"value" : 0

},

{

"key" : "'Goat' curry",

"value" : 65

},

...

}


(3) _stats Function : 여러가지 통계값(count, min, max, sum, sumsqr)을 제공

function (doc, meta) {

if (doc.ingredients) {

for (i=0; i < doc.ingredients.length; i++)

{

if (doc.ingredients[i] != null)

{

emit(doc.ingredients[i].ingredient, parseInt(doc.totaltime, 10));

}

}

}

}

==>

{

"rows" : [

{

"value" : {

"count" : 2,

"min" : 45,

"sumsqr" : 4050,

"max" : 45,

"sum" : 90

},

"key" : "acorn squash"

},

{

"value" : {

"count" : 12,

"min" : 10,

"sumsqr" : 126270,

"max" : 255,

"sum" : 886

},

"key" : "almond essence"

}

]

}


7. Document Metatdata

1) id

(1) 저장된 data object 의 id or key

(2) couchbase database 에 저장될 때 사용되는 키

2) rev

(1) 내부적으로 사용되는 revision ID

3) type

(1) 저장된 data 의 type

(2) JSON Document = json, binary data = base64

4) flags

(1) 32bit integer flag 설정값.

(2) Client Library 사용하여 설정 가능

5) expiration

(1) Expiration Time

(2) 30일(30 * 24 * 60 * 60) 보다 작은 값일 경우 초로 인식

(3) 그 이상일 경우 unix time 으로 인식







Posted by 얼랄라