cql 컬렉션(map, set, list)에는 기능도 많다. 자바로 개발되었던 이유로 cql을 사용해 탄력적으로 개발할 수 있다.



ALTER TABLE "user_status_updates"

ADD "starred_by_users" SET<text>;


ALTER TABLE "user_status_updates"

ADD "shared_by" LIST<text>;


ALTER TABLE "users"

ADD social_identities MAP<text,bigint>;



UPDATE 예제는 다음과 같다. UPDATE문은 INSERT문과 기반이 같은 upsert 이기 때문에 insert도 되고 update도 된다. 


CQL 컬렉션의 가장 강력한 기능은 컬렉션에 개별 값을 저장할 수 있다.



UPDATE images SET tags = tags + { 'cute', 'cuddly' } WHERE name = 'cat.jpg';

UPDATE images SET tags = tags - { 'lame' } WHERE name = 'cat.jpg';



UPDATE plays SET players = 5, scores = scores + [ 14, 21 ] WHERE id = '123-afde';

UPDATE plays SET players = 5, scores = [ 12 ] + scores WHERE id = '123-afde';



UPDATE users SET favs['author'] = 'Ed Poe' WHERE id = 'jsmith'

UPDATE users SET favs = favs +  { 'movie' : 'Cassablanca' } WHERE id = 'jsmith'



주의 할점은 list의 삭제와 변경은 성능 이슈가 있지만, map과 set의 삭제와 변경은 list에 비해 성능 이슈가 덜하다.




또한 컬렉션 컬럼에 인덱스를 사용할 수 있다. 그러나 성능 이슈가 있을 수 있는 대용량 트래픽에서는 사용하지 않는 것이 좋다. 


map 컬렉션 컬럼에 보조 인덱스를 생성하면, map의 키와 map의 값 모두 인덱스가 생성된다. 따라서 맵의 키만 인덱스 생성을 원한다면 KEYS라는 오퍼레이터를 사용한다.


CREATE INDEX ON "users" (KEYS("social_identities"));




검색은 다음과 같이 진행한다. 


SELECT "username", "social_identities"

FROM users

WHERE "social_identities" CONTAINS KEY 'facebook';




CQL에서는 컬렉션 컬럼을 부분적으로 읽을 수 없다. 컬렉션에서 데이터를 검색하는 유일한 방법은 컬렉션 전체를 읽는 것이다. 따라서 성능 이슈가 있는 곳에 사용할 때는 주의 깊게 사용해야 한다.


용량 제한이 있는데 컬렉션은 64KB를 넘지 않는 데이터를 포함할 수 있다. 하나의 컬렉션에 64KB 이상의 데이터를 추가할 수 없지만 컬렉션을 읽으려 하면 64KB까지만 데이터를 읽기 때문에 결과가 잘려 데이터 손실이 발생한다.

따라서 제한 없이 커질 수 있을 예정의 데이터는 컬렉션 컬럼에 적합하지 않다. 만약 계속 데이터가 커진다면 64KB 크기를 넘지 않는 여러 개의 컬렉션으로 쪼개야 한다.



카산드라 컬렉션의 다른 한계는 WHERE...IN 절을 사용해 여러 로우를 선택할 때 컬렉션을 읽을 수 없다는 점이다. 다음 쿼리는 에러가 발생한다.


SELECT * FROM "user_status_updates"

WHERE "username" = 'alice'

AND "id" IN (

 1234

);




테이블에 컬렉션 컬럼이 존재하면 WHERE...IN을 사용할 때는 컬렉션이 아닌 컬럼만 명시적으로 선택해야 한다.



튜플도 지원한다.


CREATE TABLE cycling.route (race_id int, race_name text, point_id int, lat_long tuple<text, tuple<float,float>>, PRIMARY KEY (race_id, point_id));





컬럼을 사용하는 주요 포인트가 있다. 


보조 인덱스는 단일 컬럼에만 적용할 수 있다. 예를 들어 education_history 컬럼이 각각 name과 year 컬럼으로 분리되어 있다면 해당 컬럼들의 주어진 값 조합으로 레코드를 효율적으로 검색할 수있는 인덱스를 생성할 수 없다. 튜플을 사용해 두 값을 단일 컬럼에 위치시키고 해당 컬럼에 인덱스를 추가해서 여러 컬럼에 인덱스를 추가한 것과 동일한 효과를 얻을 수 있다.

(카산드라의 보조 인덱스 단점을 컬렉션으로 해결할 수 있는 특징이 있다.)




튜플의 확장 개념인 사용자 정의 타입( udt)를 지원하기도 한다. 이름을 더 추가한다.



cqlsh> CREATE TYPE cycling.basic_info (

  birthday timestamp,

  nationality text,

  weight text,

  height text

);



CREATE TABLE cycling.cyclist_stats ( id uuid PRIMARY KEY, lastname text, basics FROZEN<basic_info>);  




대부분의 상황에서 사용자 정의 타입은 이름이 포함된 필드와 부분 선택의 추가 이점을 제공하기 때문에 튜플보다 더 나은 선택이 될 것이다.






구분

리스트

튜플

사용자 정의 타입

크기

유연

유연

유연

고정

고정

개별 변경

가능

가능

가능

불가능

불가능

부분 선택

불가능

불가능

불가능

불가능

가능

이름-값 쌍

불가능

불가능

가능

불가능

가능

여러 타입

불가능

불가능

키와 값

가능

가능

인덱스

개별 엘리먼트

개별 엘리먼트

개별 엘리먼트

전체 값

전체 값

기본 키 사용 여부

불가능

불가능

불가능

가능

가능



참고


http://www.datastax.com/documentation/cql/3.3/cql/cql_reference/delete_r.html


http://docs.datastax.com/en/cql/3.3/cql/cql_reference/cqlUpdate.html


http://docs.datastax.com/en/cql/3.1/cql/cql_using/use_collections_c.html


http://cassandra.apache.org/doc/old/CQL-3.0.html#collections


https://docs.datastax.com/en/cql/3.1/cql/cql_reference/tupleType.html


https://docs.datastax.com/en/cql/latest/cql/cql_using/useCreateUDT.html

Posted by 김용환 '김용환'

댓글을 달아 주세요

Mac OS에서 카산드라를 실행했다가 비정상 종료가 되면 가끔 다음 에러가 발생할 때가 있다. 


재시작을 한다 한들 결과를 똑같다.



INFO  [main] 2017-08-11 23:18:05,341 CommitLog.java:157 - Replaying ./bin/../data/commitlog/CommitLog-6-1502171952033.log, ./bin/../data/commitlog/CommitLog-6-1502171952034.log, ./bin/../data/commitlog/CommitLog-6-1502422474087.log, ./bin/../data/commitlog/CommitLog-6-1502422474088.log, ./bin/../data/commitlog/CommitLog-6-1502422504239.log, ./bin/../data/commitlog/CommitLog-6-1502422504240.log, ./bin/../data/commitlog/CommitLog-6-1502452966387.log, ./bin/../data/commitlog/CommitLog-6-1502452966388.log, ./bin/../data/commitlog/CommitLog-6-1502457013860.log, ./bin/../data/commitlog/CommitLog-6-1502457013861.log, ./bin/../data/commitlog/CommitLog-6-1502457041056.log, ./bin/../data/commitlog/CommitLog-6-1502457041057.log

ERROR [main] 2017-08-11 23:18:05,622 JVMStabilityInspector.java:82 - Exiting due to error while processing commit log during initialization.

org.apache.cassandra.db.commitlog.CommitLogReadHandler$CommitLogReadException: Unexpected error deserializing mutation; saved to /var/folders/ch/zbmq4sk149gcz172ylw54m140000gp/T/mutation410555022742916493dat.  This may be caused by replaying a mutation against a table with the same name but incompatible schema.  Exception follows: java.io.IOError: java.io.IOException: Corrupt empty row found in unfiltered partition


at org.apache.cassandra.db.commitlog.CommitLogReader.readMutation(CommitLogReader.java:409) [apache-cassandra-3.10.jar:3.10]

at org.apache.cassandra.db.commitlog.CommitLogReader.readSection(CommitLogReader.java:342) [apache-cassandra-3.10.jar:3.10]

at org.apache.cassandra.db.commitlog.CommitLogReader.readCommitLogSegment(CommitLogReader.java:201) [apache-cassandra-3.10.jar:3.10]

at org.apache.cassandra.db.commitlog.CommitLogReader.readAllFiles(CommitLogReader.java:84) [apache-cassandra-3.10.jar:3.10]

at org.apache.cassandra.db.commitlog.CommitLogReplayer.replayFiles(CommitLogReplayer.java:140) [apache-cassandra-3.10.jar:3.10]




commit 로그를 replay하다가 에러가 발생한 이유인데, commit log가 바이너리라서 읽기도 애매하긴 하다. 정확한 문제를 찾기도 전에 어려워질 수 있다. commit 로그를 백업 디렉토리에 move시켜 놓고 다시 재시작하면 정상적으로 동작한다. 


mkdir -p ~/dev/backup/

mv data/commitlog ~/dev/backup/

./bin/cassandra



Posted by 김용환 '김용환'

댓글을 달아 주세요


cassandra 핵심 내용중 batch log에 대한 내용이 아래 url에 적혀 있다. 


https://www.datastax.com/dev/blog/atomic-batches-in-cassandra-1-2



batchlog 테이블은 node local이다.. 


The batchlog table is node-local, along with the rest of the system keyspace.


노드 로컬(node-local) : 배치가 실행되는 노드에서 배치 로그가 저장된다는 것을 의미한다.




Posted by 김용환 '김용환'

댓글을 달아 주세요


카산드라에서 UUID를 생성하는 방법은 (좀 적은 개수의 로우를 가진) 아무 테이블에서 LIMIT 1로 select now()를 호출한다. 



cqlsh:my_status> SELECT NOW() FROM "user_status_updates" LIMIT 1;


 system.now()

--------------------------------------

 08376da0-7c8e-11e7-95fc-eb95772c8baf


(1 rows)

cqlsh:my_status> SELECT NOW() FROM "user_status_updates" LIMIT 1;


 system.now()

--------------------------------------

 0e2d4ea0-7c8e-11e7-95fc-eb95772c8baf


(1 rows)

cqlsh:my_status> SELECT NOW() FROM "user_status_updates" LIMIT 1;


 system.now()

--------------------------------------

 0eee0690-7c8e-11e7-95fc-eb95772c8baf


(1 rows)

cqlsh:my_status> SELECT NOW() FROM "user_status_updates" LIMIT 1;


 system.now()

--------------------------------------

 0fa63300-7c8e-11e7-95fc-eb95772c8baf


(1 rows)

cqlsh:my_status> SELECT NOW() FROM "user_status_updates" LIMIT 1;


 system.now()

--------------------------------------

 1053b110-7c8e-11e7-95fc-eb95772c8baf





만약 테이블에 now()로 저장된 UUID 필드를 갖고 있다면 다양한 포맷으로 볼 수 있다.

SELECT id, toDate(id), unixtimestampof(id), toTimestamp(id)  FROM "user_status_updates"  limit 1;

 id                                   | system.todate(id) | system.unixtimestampof(id) | system.totimestamp(id)
--------------------------------------+-------------------+----------------------------+---------------------------------
 97719c50-e797-11e3-90ce-5f98e903bf02 |        2014-05-30 |              1401412401813 | 2014-05-30 01:13:21.813000+0000




자세한 내용은 다음을 참조한다.
http://docs.datastax.com/en/cql/3.3/cql/cql_reference/timeuuid_functions_r.html





Posted by 김용환 '김용환'

댓글을 달아 주세요

전체 keyspace를 덤프뜨려면 다음과 같이 진행한다.



 



$ ./bin/cqlsh -e "desc schema"


CREATE KEYSPACE users WITH replication = {'class': 'SimpleStrategy', 'replication_factor': '1'}  AND durable_writes = true;


CREATE TABLE users. follow_relation (

...

}



파일로 저장하려면 다음과 같이 진행한다.


$ ./bin/cqlsh -e "desc schema" > schema.cql




특정 keyspace만 파일로 저장하려면 다음과 같이 진행한다.



$ ./bin/cqlsh -e "desc keyspace my_status" > my_status.cql

$ cat schema.cql

CREATE KEYSPACE my_status WITH replication = {'class': 'SimpleStrategy', 'replication_factor': '1'}  AND durable_writes = true;


CREATE TABLE my_status.follow_relation (

    followed_username text,

    follower_username text,

....

}




생성된 keyspace 파일을 import하는 방법은 cqlsh에 들어가서 source 명령을 사용하면 된다. 


$./bin/cqlsh

Connected to Test Cluster at 127.0.0.1:9042.

[cqlsh 5.0.1 | Cassandra 3.10 | CQL spec 3.4.4 | Native protocol v4]

Use HELP for help.

cqlsh> source 'schema.cql'

cqlsh> use my_status;

cqlsh:my_status> describe my_status;

Posted by 김용환 '김용환'

댓글을 달아 주세요


카산드라(cassandra)에서 IN과 ORDER BY를 함께 싸용하면 다음과 같은 에러가 발생할 수 있다. 

(참고로 ORDER BY 다음에는 클러스터링 키를 사용함으로서, 원하는 대로 파티션 키와 상관없이 생성 시간을 내림차순으로 결과를 얻을 수 있다)


InvalidRequest: Error from server: code=2200 [Invalid query] message="Cannot page queries with both ORDER BY and a IN restriction on the partition key; you must either remove the ORDER BY or the IN and sort client side, or disable paging for this query"



이 때에는 PAGING OFF라는 커맨드를 사용하면 에러가 발생하지 않고 정상적으로 동작한다.



Posted by 김용환 '김용환'

댓글을 달아 주세요



카산드라에서 복합 기본 키(compound primary key)에 대한 설명이다. 


모든 카산드라 테이블에는 파티션 키가 있어야 한다. 클러스터링 컬럼은 선택 사항이다.



즉, 테이블은 하나 이상의 파티션 키 컬럼과 0개 이상의 클러스터링 컬럼을 정의할 수 있다.


카산드라의 테이블의 파티션 키는 로우을 논리적으로 관련된 번들로 그룹핑한다. 




클러스터링 컬럼의 작업은 파티션 내의 로우 순서를 결정한다. 


읽기 시간을 데이터를 읽을 때 많은 엘리먼트에서 분류하는 것은 비용이 많이 든다. 


다행히도 카산드라는 로우를 클러스터링 순서로 저장한다. 그래서 로우를 검색할 때, 로우는 저장된 순서대로 로우를 리턴할 뿐이다. 읽을 때 값 비싼 정렬 작업이 없다는 점이 장점이다. 



동일한 기본 키를 공유하는 모든 로우는 디스크에 연속적으로 저장된다. 로우는 클러스터링 컬럼 값 별로 정렬된다. 


각 파티션은 저장소 레벨에서 엄격하게 바인딩되기 때문에 동일한 파티션 키를 공유할 수 있는 로우 개수에 상한이 있다. 

이론적으로 해당 상한 값은 약 20억 개의 전체 컬럼 값이다. 예를 들어 10개의 데이터 컬럼을 가진 테이블이 있는 경우 상한 값은 파티션 키 당 2억 개의 로우가 된다.


복합 기본 키를 사용하는 데이터 모델링에 대한 자세한 내용http://www.datastax.com/documentation/cql/3.1/cql/ddl/ddl_compound_keys_c.html를 참조한다.



따라서 클러스터링 키에 uuid 값이 있다면 정렬이 자동으로 되어 SELECT로 이를 활용해 읽을 때 정렬할 필요가 없어진다. 

Posted by 김용환 '김용환'

댓글을 달아 주세요


카산드라에 존재하는 uuid version 1 테스트 코드이다. 



cql은 다음과 같이 사용한다.


cql> SELECT "uuid", DATEOF("uuid"), FROM "activities";


uuid | system.dateof(uuid)

---- + ----------------------------

11.. .   2017-05-30 01:13:21.813000+0000




http://docs.datastax.com/en/cql/3.3/cql/cql_reference/timeuuid_functions_r.html



실제 코드로 테스트해볼 수 있다. 



import java.util.Date;

import java.util.Map;

import java.util.UUID;



UUID uuid = com.datastax.driver.core.utils.UUIDs.timeBased(); 

String uuidStr = uuid.toString(); 

System.out.println(uuidStr); 




결과는 다음과 같다.


c4888d00-61f3-11e7-afc1-53e0e7faa58a

Thu Jul 06 11:35:26 KST 2017




uuid의 timestamp를 보고 싶다면 UNIXTIMESTAMPOF 함수를 사용한다. 



cql> SELECT "uuid", UNIXTIMESTAMPOF("uuid"), FROM "activities";

 system.unixtimestampof(id)

+----------------------------

              1401412401813

              1401411917725



UNIXTIMESTAMPOF 함수는 UUIDs.unixTimestamp()와 동일하다.


long timestamp = com.datastax.driver.core.utils.UUIDs.unixTimestamp(uuid) ; 

System.out.println(new Date(timestamp));


Posted by 김용환 '김용환'

댓글을 달아 주세요



cassandra의 null의 개념은 '없다'를 의미하고..


관계형 데이터베이스에서의 null은 정의하지 않음(미지의 값)을 의미한다.



 SELECT * FROM "profiles" WHERE "location" IS NULL;

SyntaxException: line 1:42 mismatched input 'NULL' expecting K_NOT (...FROM "users" WHERE "location" IS [NULL]...)




참조


https://ko.wikipedia.org/wiki/Null_(SQL)



수학적 연산[편집]

널은 데이터 값이 아니라, 미지의 값에 대한 표시일 뿐이기 때문에, Null에 수학적 연산을 사용하는 것은 미지의 값으로 나타난다.[4] 아래의 예에서, 10을 Null에 곱하면 결과값은 Null이 된다.:

10 * NULL          -- 결과는 NULL

이것은 예기치 않은 결과를 야기한다. 예를 들어, 널을 0으로 나누려 한다면, 플랫폼은 ‘0으로 나눈’ 예상된 데이터 예외값을 던지는 대신, 널 값을 반환한다.[4] 비록 이러한 행위가 ISO SQL 표준으로 정의되어 있지는 않지만, 많은 DBMS 벤더들이 이러한 연산을 유사하게 다룬다. 예를 들어, 오라클, PostgreSQL, MySQL 서버, 그리고 마이크로소프트 SQL 서버 플랫폼은 모두 널 값을 아래와 같이 반환한다 :

NULL / 0



Posted by 김용환 '김용환'

댓글을 달아 주세요

cassandra3 모니터링을 telegraf로 사용하려 하는데. 

일부 중요 mbean 지표(/org.apache.cassandra.db:type=StorageService, /org.apache.cassandra.net:type=FailureDetector)를 telegraf로 보지 못한다. 


실제 telegraf-cassandra 모니터링 코드는 다음과 같다.


https://github.com/influxdata/telegraf/blob/master/plugins/inputs/cassandra/cassandra.go#L270-L280



if strings.HasPrefix(metric, "/java.lang:") {

m = newJavaMetric(serverTokens["host"], metric, acc)

} else if strings.HasPrefix(metric,

"/org.apache.cassandra.metrics:") {

m = newCassandraMetric(serverTokens["host"], metric, acc)

} else {

// unsupported metric type

acc.AddError(fmt.Errorf("E! Unsupported Cassandra metric [%s], skipping",

metric))

continue

}



telegraf 1.4에서 지원예정이라 한다.. (더 늦어질 지도.)


https://github.com/influxdata/telegraf/issues/2567









Posted by 김용환 '김용환'

댓글을 달아 주세요