자주 묻는 질문

자주 묻는 질문 (Frequently Asked Questions)

Cassandra 사용자들이 자주 묻는 질문과 답변을 모았어요.

출처: Frequently Asked Questions

본문

listen_address를 0.0.0.0(모든 내 주소)으로 수신하도록 설정할 수 없나요?

Cassandra는 gossip 기반 분산 시스템이고, listen_address는 노드가 다른 노드에게 자신에게 도달할 주소를 알려주는 주소예요. 다른 노드에게 "내 주소 중 아무거나로 연락해"라고 말하는 것은 나쁜 생각이에요. 클러스터의 서로 다른 노드가 여러분을 위해 서로 다른 주소를 고르면, 나쁜 일(Bad Things)이 발생해요.

클러스터의 각 노드에 listen_address로 IP를 수동으로 지정하고 싶지 않다면(이해할 수 있어요!), 공란으로 두세요. 그러면 Cassandra가 InetAddress.getLocalHost()를 사용해 주소를 선택해요. 그런 다음 여러분이나 운영 팀이 올바르게 리졸브되도록 해야 해요(/etc/hosts/, dns 등).

이 과정의 한 가지 예외는 JMX인데, 기본적으로 0.0.0.0에 바인딩돼요(Java bug 6425769).

자세한 내용은 25643을 참고하세요.

Cassandra는 어떤 포트를 사용하나요?

기본적으로 Cassandra는 7000(SSL이 활성화되면 7001)을 클러스터 통신에, 9042를 네이티브 프로토콜 클라이언트에, 7199를 JMX에 사용해요. internode 통신과 네이티브 프로토콜 포트는 cassandra-yaml에서 구성할 수 있어요. JMX 포트는 cassandra-env.sh에서(JVM 옵션을 통해) 구성할 수 있어요. 모든 포트는 TCP예요.

새 노드를 추가하면 클러스터의 기존 데이터는 어떻게 되나요?

새 노드가 클러스터에 합류하면 클러스터의 다른 노드에 자동으로 연락해 올바른 데이터를 복사해요. topology-changes 참조.

Cassandra에서 데이터를 삭제했는데 디스크 사용량이 그대로예요. 왜죠?

Cassandra에 쓰는 데이터는 SSTable에 영속됩니다. SSTable은 불변이므로 삭제를 수행할 때 데이터를 실제로 제거할 수 없고, 대신 값의 새 상태를 나타내는 마커("tombstone"이라고도 함)가 쓰여져요. 걱정하지 마세요. 데이터와 tombstone 사이에서 발생하는 첫 컴팩션에서 데이터가 완전히 제거되고 해당 디스크 공간이 회수돼요. 자세한 내용은 compaction 참조.

노드들이 서로 링에 합류하는 것을 로깅했는데 nodetool ring이 하나의 항목만 보여주는 건 왜죠?

이는 각 노드에 같은 토큰이 할당된 경우 발생해요. 그렇게 하지 마세요.

이 문제는 주로 VM에 Cassandra를 설치한 다음(특히 Deb 설치 후 Cassandra를 자동 시작해 토큰을 생성·저장하는 Debian 패키지를 사용할 때) 그 VM을 다른 노드로 복제하는 사람들을 괴롭혀요.

가장 쉬운 해결책은 데이터와 commitlog 디렉터리를 지우는 것이에요. 그러면 각 노드가 다음 재시작에서 무작위 토큰을 생성하도록 보장돼요.

활성 클러스터에서(키스페이스의) 복제 계수를 변경할 수 있나요?

네, 하지만 기존 데이터의 레플리카 수를 변경하려면 전체 복구(full repair) 또는 cleanup을 실행해야 해요.

  • 원하는 키스페이스의 복제 계수를 ALTER하세요(예: cqlsh 사용).
  • 복제 계수를 줄이는 경우 클러스터에서 nodetool cleanup을 실행해 잉여 복제 데이터를 제거하세요. Cleanup은 노드 단위로 실행돼요.
  • 복제 계수를 늘리는 경우 nodetool repair -full을 실행해 데이터가 새 구성에 따라 복제되도록 하세요. Repair는 레플리카 세트 단위로 실행돼요. 이것은 집약적인 과정으로 클러스터 성능에 부정적인 영향을 줄 수 있어요. 한 번에 전체 클러스터를 복구하려고 하면 아마 압도당할 것이므로 롤링 복구를 실행하는 것이 매우 권장돼요. 이미 복구된 sstable이 건너뛰어지지 않도록 전체 복구(-full)를 실행해야 한다는 점에 유의하세요. 실제로 데이터가 있는 레플리카가 참조되도록 ConsistencyLevel.QUORUM 또는 ALL(기존 복제 계수에 따라)을 사용해야 해요. 그렇지 않으면 복구가 완료될 때까지 일부 클라이언트가 데이터가 존재하지 않는다는 말을 들을 수 있어요.

Cassandra에 (대형) BLOB을 저장할 수 있나요?

Cassandra는 대형 파일이나 BLOB 저장에 최적화되어 있지 않으며, 단일 blob 값은 항상 완전히 읽혀 클라이언트로 전송돼요. 따라서 작은 blob(수 MB 미만)을 저장하는 것은 문제가 되지 않지만, 대형 blob을 더 작은 청크로 수동 분할하는 것이 좋아요.

특히 기본적으로 cassandra-yaml 파일의 max_mutation_size 구성 때문에 16MiB보다 큰 값은 Cassandra에 의해 거부돼요(이는 commitlog_segment_size의 절반을 기본값으로 하며, commitlog_segment_size 자체의 기본값은 32MiB예요).

nodetool이 모든 원격 호스트에 대해 "Connection refused to host: 127.0.1.1"이라고 말해요. 왜죠?

nodetool은 JMX에 의존하고, JMX는 RMI에 의존하며, RMI는 교환의 각 끝에서 필요에 따라 자체 리스너와 커넥터를 설정해요. 일반적으로 이 모든 것은 백그라운드에서 투명하게 발생하지만, 연결하는 호스트 또는 연결되는 호스트에 대한 잘못된 이름 리졸루션은 엉킨 전선과 혼란스러운 예외를 초래할 수 있어요.

DNS를 사용하지 않는다면 양쪽 끝의 /etc/hosts 파일이 정확한지 확인하세요. 그래도 실패하면 cassandra-env.sh 하단 근처에 -Djava.rmi.server.hostname=<public name> JVM 옵션을 원격 머신에서 도달할 수 있는 인터페이스로 설정해 보세요.

내 연산을 배칭하면 벌크 로드가 빨라지나요?

아니요. 데이터를 로드하기 위해 배치를 사용하면 일반적으로 지연 시간의 "스파이크"만 추가해요. 대신 비동기 INSERT를 사용하거나 실제 bulk-loading을 사용하세요.

예외는 단일 파티션에 대한 갱신 배칭인데, 이는 좋을 수 있어요(단일 배치의 크기가 합리적으로 유지된다면). 하지만 맹목적으로 모든 것을 배칭하지 마세요!

RHEL에서 노드가 링에 합류하지 못해요

SELinux가 켜져 있는지 확인하고, 켜져 있으면 꺼 주세요.

이메일 목록에서 어떻게 구독을 해지하나요?

[email protected]로 이메일을 보내세요.

top이 Cassandra가 Java 힙 최대값보다 훨씬 많은 메모리를 사용한다고 보고하는데 왜죠?

Cassandra는 내부적으로 Memory Mapped Files(mmap)를 사용해요. 즉, 운영 체제의 가상 메모리 시스템을 사용해 디스크의 여러 파일을 Cassandra 프로세스의 주소 공간에 매핑해요. 이는 "가상" 메모리, 즉 주소 공간을 "사용"하며 top 같은 도구가 그에 따라 보고해요. 하지만 64비트 시스템에서 가상 주소 공간은 사실상 무제한이므로 걱정할 필요가 없어요.

일반적으로 의미하는 "메모리 사용" 관점에서 중요한 것은 실제 사용된 메모리를 나타내는 brk() 또는 mmap'd /dev/zero에 할당된 데이터 양이에요. 핵심 문제는 mmap'd 파일의 경우 물리 메모리에 데이터를 상주시킬 필요가 없다는 것이에요. 따라서 물리 메모리에 상주시키는 것은 본질적으로 캐시로 존재하는 것이며, 일반 I/O가 커널 페이지 캐시가 읽은/쓴 데이터를 유지하게 하는 것과 같은 방식이에요.

일반 I/O와 mmap()의 차이는 mmap()의 경우 메모리가 실제로 프로세스에 매핑되어 top이 보고하는 가상 크기에 영향을 준다는 것이에요. 표준 I/O 대신 mmap()을 사용하는 주요 근거는 읽기가 메모리를 건드리는 것만 수반한다는 사실이에요. 메모리가 상주하는 경우 그냥 읽기만 하면 되고 페이지 폴트도 발생하지 않아요(그래서 커널 진입과 반-컨텍스트 스위치 오버헤드가 없어요). 이에 대한 자세한 내용은 여기에서 다뤄요.

시드(seeds)란 무엇인가요?

시드는 시작 중에 클러스터를 발견하는 데 사용돼요.

노드가 어떤 노드를 시드로 참조하도록 구성하면, 링의 노드는 시드가 아닌 노드보다 시드에 Gossip 메시지를 더 자주 보내는 경향이 있어요(gossip 섹션 참조). 즉, 시드는 Gossip 네트워크의 허브로 작동해요. 시드를 사용하면 각 노드가 다른 노드의 상태 변경을 빠르게 감지할 수 있어요.

시드는 또한 새 노드가 부트스트랩 시 링의 다른 노드를 배우기 위해 참조해요. 링에 새 노드를 추가할 때 연락할 살아있는 시드를 하나 이상 지정해야 해요. 노드가 링에 합류하면 다른 노드를 배우므로 후속 부팅에서는 시드가 필요 없어요.

어떤 때든 노드를 시드로 만들 수 있어요. 시드 노드에는 특별한 것이 없어요. 시드 목록에 노드를 나열하면 그것은 시드예요.

시드는 자동 부트스트랩하지 않아요(즉, 노드가 자신의 시드 목록에 자신을 포함하면 자동으로 데이터를 자신에게 전송하지 않아요). 노드가 그것을 하게 하려면 먼저 부트스트랩한 다음 나중에 시드에 추가하세요. 데이터가 없으면(새 설치) 부트스트랩에 대해 걱정할 필요가 전혀 없어요.

권장 시드 사용:

  • 데이터센터당 두 개(또는 그 이상) 노드를 시드 노드로 선택하세요.
  • 시드 목록을 모든 노드에 동기화하세요.

단일 시드는 단일 실패 지점을 의미하나요?

링은 시드 없이도 작동하거나 부팅할 수 있어요. 하지만 클러스터에 새 노드를 추가할 수는 없어요. 프로덕션 시스템에서는 여러 시드를 구성하는 것이 권장돼요.

jconsole에서 JMX 메서드 X를 호출할 수 없는 건 왜죠?

일부 JMX 연산은 배열 인자를 사용하며, jconsole은 배열 인자를 지원하지 않으므로 그러한 연산은 jconsole로 호출할 수 없어요(그 연산에 대해 버튼이 비활성화돼요). 그러한 연산을 호출하려면 JMX 클라이언트를 작성하거나 배열을 지원하는 JMX 모니터링 도구가 필요해요.

로그에 "… messages dropped …"이 보이는 건 왜죠?

이것은 로드 셰딩(load shedding)의 증상이에요. Cassandra가 처리할 수 있는 것보다 더 많은 요청으로부터 스스로를 방어하는 것이에요.

노드가 받았지만 적절한 타임아웃(cassandra-yamlread_request_timeout, write_request_timeout 등 참조) 내에 처리되지 않는 internode 메시지는 처리되지 않고 버려져요(코디네이터 노드가 더 이상 응답을 기다리지 않으므로).

쓰기의 경우 이는 뮤테이션이 보내진 모든 레플리카에 적용되지 않았다는 것을 의미해요. 불일치는 읽기 복구, 힌트 또는 수동 복구로 수정될 거예요. 쓰기 연산도 결과적으로 타임아웃될 수 있어요.

읽기의 경우 이는 읽기 요청이 완료되지 않았을 수 있다는 것을 의미해요.

로드 셰딩은 Cassandra 아키텍처의 일부예요. 지속적인 문제라면 일반적으로 과부하된 노드 또는 클러스터의 신호예요.

Cassandra가 java.lang.OutOfMemoryError: Map failed로 죽어요

Cassandra가 특히 "Map failed" 메시지로 죽는다면, OS가 java가 더 많은 메모리를 잠그는 것을 거부한다는 의미예요. Linux에서 이는 일반적으로 memlock이 제한되어 있다는 뜻이에요. /proc/<pid of cassandra>/limits를 확인해 이를 검증하고 올려주세요(예: bash에서 ulimit 사용). vm.max_map_count를 늘릴 필요도 있을 수 있어요. Debian 패키지는 자동으로 처리해 준다는 점에 유의하세요.

같은 타임스탬프로 두 번의 갱신이 이루어지면 어떻게 되나요?

갱신은 서로 다른 레플리카에 다른 순서로 도착할 수 있으므로 교환적(commutative)이어야 해요. Cassandra가 승자를 결정하는 결정적인 방법(타임스탬프 동점에서)이 있는 한, 선택된 것은 다른 것만큼 유효하며 특정 사항은 구현 세부사항으로 취급되어야 해요. 단, 타임스탬프 동점의 경우 Cassandra는 두 가지 규칙을 따라요. 첫째, 삭제가 인서트/업데이트보다 우선해요. 둘째, 두 갱신이 있으면 어휘적으로 더 큰 값을 가진 것이 선택돼요.

새 노드 부트스트래핑이 "Stream failed" 오류로 실패하는 건 왜죠?

두 가지 주요 가능성이 있어요.

  1. GC가 장시간 중지를 일으켜 스트리밍 과정을 방해할 수 있어요.
  2. 백그라운드에서 발생하는 컴팩션이 TCP 연결이 실패할 만큼 오래 스트리밍을 잡고 있을 수 있어요.

첫 번째 경우에는 일반적인 GC 튜닝 조언이 적용돼요. 두 번째 경우에는 TCP keepalive를 더 낮은 값으로 설정해야 해요(Linux의 기본값은 매우 높아요). 다음을 실행해 보세요:

$ sudo /sbin/sysctl -w net.ipv4.tcp_keepalive_time=60 net.ipv4.tcp_keepalive_intvl=60 net.ipv4.tcp_keepalive_probes=5

이 설정을 영구적으로 만들려면 /etc/sysctl.conf 파일에 추가하세요.

참고: GCE의 방화벽은 10분 이상 비활성인 TCP 연결을 항상 중단해요. 그 환경에서는 위 명령을 실행하는 것이 매우 권장돼요.

더 알아보기 (Learn more)