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



'cassandra' 카테고리의 다른 글

[cassandra3]  (0) 2017.08.15
[cassandra3] 컬렉션과 사용자 정의 타입(udt)  (0) 2017.08.12
[cassandra] node local의 의미  (0) 2017.08.10
[cassandra3] select now()  (0) 2017.08.09
[cassandra3] schema 백업(backup)/복구(restore)하기  (0) 2017.08.08
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 '김용환'
,