Tag Archives: NoSQL

Cassandra 특정 노드가 부하가 심해질때 긴급 처리 방법

사용자 삽입 이미지
카산드라 서버를 운영하다 보면 매우 가끔씩 특정 노드가 혼자서 미친듯이 폭주하더니 이내 뻗어버리는 현상이 발생합니다. 이경우 잘 대처하지 않고 방치해 두면 마치 전염병처럼 다른 노드까지 영향을 미치곤 합니다. 매우 간단하게 시도해 볼 수 있는 방법을 정리해 보겠습니다.

문제 파악

1. 특정 노드가 폭주를 하게 될 경우 서버의 로드가 10을 넘어 30혹은 더 높은 수치까지 치솟습니다.
2. /var/log/cassandra/system.log 파일에 다음과 같은 StatusLogger가 지속적으로 출력됩니다.
[code]INFO [ScheduledTasks:1] 2011-08-09 17:52:56,601 StatusLogger.java (line 88) system.NodeIdInfo
INFO [ScheduledTasks:1] 2011-08-09 17:52:56,601 StatusLogger.java (line 88) system.IndexInfo
INFO [ScheduledTasks:1] 2011-08-09 17:52:56,601 StatusLogger.java (line 88) system.LocationInfo
INFO [ScheduledTasks:1] 2011-08-09 17:52:56,601 StatusLogger.java (line 88) system.Migrations
INFO [ScheduledTasks:1] 2011-08-09 17:52:56,601 StatusLogger.java (line 88) system.HintsColumnFamily
INFO [ScheduledTasks:1] 2011-08-09 17:52:56,601 StatusLogger.java (line 88) system.Schema[/code]
3. 구동중인 어플리케이션에서 다음과 같은 접속불가를 나타내는 Thrift 에러가 발생합니다.
[code]TSocket: Could not connect to 123.123.123.123:9160 (Connection timed out [110])[/code]

해결 방법 – 데이터가 꼬여있을(Conflict) 경우

[code]nodetool -h localhost disablethrift
nodetool -h localhost disablegossip[/code]
위와 같은 명령을 사용하여 현재 운영중인 서비스에서 제외시킵니다. thrift를 비활성화 할 경우 어플리케이션에 응답하지 않으면 gossip을 끌 경우 노드들간의 통신을 하지 않으며 결과적으로 다른 노드들에서 보기엔 이 노드가 Down된것처럼 인식이 됩니다.
[code]nodetool -h localhost flush[/code]
위의 명령을 사용하여 현재 메모리에 올라가 있는 데이터를 강제로 SSTable에 쓰기를 합니다. 비정상적인 종료가 일어날 경우 데이터 손실로 이어질수도 있으므로 일단 데이터를 파일형태의 디비로 옮기는 작업을 합니다.
[code]nodetool -h localhost scrub[/code]
위의 명령은 SSTable을 새로 생성을 하는 명령입니다. 모든 데이터를 점검하여 새로운 SSTable을 생성합니다.  /var/log/cassandra/system.log에 데이터 관련된 Fatal에러가 발생할 경우에도 문제가 수정이 됩니다.
[code]nodetool -h localhost enablegossip[/code]
위의 명령을 실행하여 gossip을 구동합니다. 다시 클러스터에 참여를 하게 되며 다른 노드들과의 통신을 시작합니다. 이상태에서 top명령등을 사용하여 노드의 상태를 확인합니다. 정상으로 돌아왔다면 잠시 폭주하는듯 하다가 이내 안정화 됩니다.
[code]nodetool -h localhost enablethrift[/code]
모든것이 정상이라면 다시 서비스에 참여시켜야 겠죠?

해결방법 – 메모리가 부족한 상황

모든 해결 방법이 위의 경우와 동일합니다만 scrub을 사용하지 않고 compact를 사용합니다. 기본적으로 카산드라는 Compaction이라는 과정을 주기적으로 실행하며 SSTable을 병합 및 규모를 축소하는 작업을 지속적으로 하게 됩니다. 하지만 어쩐일인지 정상적으로 정리 정돈이 이루어지지 않아 서버의 부하로 이어지는 경우가 있습니다. 이 경우 튜닝이 필요하겠지만 급한대로 compact명령을 통해 강제로 Compaction을 할 수 있습니다.
[code]nodetool -h localhost disablethrift
nodetool -h localhost disablegossip
nodetool -h localhost flush
nodetool -h localhost compact
nodetool -h localhost enablegossip
nodetool -h localhost enablethrift[/code]

주의할점

별것은 아니지만 서비스 운영중에 왠만하면 피해야 하는 명령이 하나 있습니다.
[code]nodetool -h localhost repair[/code]
노드가 이상해졌다면 위와 같은 명령이 호감이 가게 됩니다. 무언가 지금의 상황을 고쳐줄것만 같은 명령입니다. 하지만 repair는 데이터를 교정해 주는 명령으로 주변 노드들과의 데이터 비교를 통하여 신뢰할 수 있는 값으로 교정을 해주는 명령입니다. 하지만 이 과정에서 메모리를 더 잡아먹게 되며 결과적으로 SSTable이 기하급수적으로 확장되는 과정을 거칩니다. 그냥 결론만 말하자면 서비스중에 이 명령을 사용하지 마십시오. 기본적으로 Cassandra는 기본 운영중에 끊임없이 데이터의 무결성을 검증하고 교정하게 됩니다. 경험상 repair는 정기점검때와 같이 서비스가 중단된 상태가 아닌 데이터가 계속해서 변화하는 상황에서 실행했을때 좋은 상황을 맞이한적이 없군요. 조심하시길 바랍니다.

[NoSQL/Cassandra] PHPCassa 에서 TimeUUIDType을 어떻게 활용하는가?

사용자 삽입 이미지
별거 아닌 내용을 적어보려고 합니다. 하지만 처음 접할때 생각보다 어려웠던 기억이 나는군요. 이 글을 보시기 전에 PHPCassa에 대한 이해가 필요합니다. [이글]을 먼저 보시길 추천합니다.

Cassandra에는 기본적으로 TimeUUIDType이라는 것이 존재합니다. 이 타입을 이용하여 여러개의 컬럼키들을 시간순으로 정렬하는것이 가능해집니다. 기존의 MySQL등에서는 Auto Increment 옵션을 사용하여 Unique한 PK를 만들어냅니다만 Cassandra에서는 이 TimeUUIDType을 이용하여 사실상 거의 중복되지 않는 키값을 만들어 낼 수 있습니다.

그렇다면 이제 이 키값을 이용하여 서버-클라이언트간에 주고 받을 수 있어야 하는데요, 이것이 생각보다 간단하지 않습니다.(사실 알고보면 간단합니다만;) 우선 소스코드부터 보여드리겠습니다.
[code]// 1. 새로운 UUID를 생성
$newUUID = CassandraUtil::uuid1();
echo “<p>새로운 UUID생성: $newUUID</p>”;

// 2. 클라이언트/서버와 주고받을때 사용할 수 있는 문자형태로 변환
$strUUID = strval(CassandraUtil::import($newUUID));
echo “<p>식별가능한 문자열UUID로 변경: $strUUID</p>”;

// 3. 다시 바이너리 형태의 UUID로 변경
$importedUUID = CassandraUtil::import($strUUID)->bytes;
echo “<p>바이너리 형태의 UUID로 변경: $importedUUID</p>”;[/code]
위의 코드는 현재의 시간(64bit microtime)을 이용하여 UUID를 생성한 다음 그것을 서버-클라이언트간에 식별하기 쉬운 문자열로 바꾼후에 다시 그 문자열을 Cassandra에서 바로 사용할 수 있는 형태로 되돌리는 과정을 수행합니다. 위의 코드는 아래와 같은 결과를 보여줍니다.

사용자 삽입 이미지
첫번째와 마지막이 사실상 같다는 것을 알 수 있습니다. Cassandra에서는 기본적으로 이 Binary형태의 값을 사용합니다. 하지만 개발자가 사용하기 어렵고 이렇게 생성된 값을 서버/클라이언트간 주고 받는다고 해도 생각해볼점이 많아집니다. 이 경우 두번째와 같은 형태로 변경하여 사용할 수 있습니다.