[java] 분산 시퀀스 번호 생성?

나는 일반적으로 과거에 데이터베이스 시퀀스를 사용하여 시퀀스 번호 생성 을 구현했습니다 .

예 : Postgres SERIAL 유형 사용 http://www.neilconway.org/docs/sequences/

데이터베이스가없는 대규모 분산 시스템의 시퀀스 번호를 생성하는 방법이 궁금합니다. 누구나 여러 클라이언트 에 대해 스레드 안전 방식으로 시퀀스 번호 생성을 달성하기위한 모범 사례에 대한 경험이나 제안이 있습니까?



답변

좋아요, 이것은 제가 지금 처음 보는 아주 오래된 질문입니다.

특정 기준 (일반적으로 생성 시간)에 따라 (선택적으로) 느슨하게 정렬 할 수있는 시퀀스 번호고유 ID 를 구별해야합니다 . 실제 시퀀스 번호는 다른 모든 작업자가 수행 한 작업에 대한 지식을 의미하므로 공유 상태가 필요합니다. 분산 된 대규모 방식으로이 작업을 수행하는 쉬운 방법은 없습니다. 네트워크 브로드 캐스트, 각 작업자에 대한 창 범위, 고유 작업자 ID에 대한 분산 해시 테이블 등을 살펴볼 수 있지만 많은 작업이 필요합니다.

고유 ID는 또 다른 문제입니다. 분산 된 방식으로 고유 ID를 생성하는 몇 가지 좋은 방법이 있습니다.

a) Twitter의 Snowflake ID 네트워크 서비스를 사용할 수 있습니다 . Snowflake는 다음과 같습니다.

  • 네트워크 서비스, 즉 고유 ID를 얻기 위해 네트워크 전화를 겁니다.
  • 생성 시간별로 정렬 된 64 비트 고유 ID를 생성합니다.
  • 서비스는 확장 성이 뛰어나고 (잠재적으로) 가용성이 높습니다. 각 인스턴스는 초당 수천 개의 ID를 생성 할 수 있으며 LAN / WAN에서 여러 인스턴스를 실행할 수 있습니다.
  • Scala로 작성되었으며 JVM에서 실행됩니다.

b) UUID 및 Snowflake의 ID가 만들어 지는 방식에서 파생 된 접근 방식을 사용하여 클라이언트 자체에서 고유 ID를 생성 할 수 있습니다 . 여러 옵션이 있지만 다음과 같은 내용이 있습니다.

  • 최상위 40 비트 정도 : 타임 스탬프; ID의 생성 시간. (우리는 생성 시간별로 ID를 정렬 할 수 있도록 타임 스탬프에 가장 중요한 비트를 사용하고 있습니다.)

  • 다음 14 개 정도의 비트 : 각 생성기가 생성 된 각 새 ID에 대해 하나씩 증가하는 생성기 별 카운터 . 이렇게하면 동시에 생성 된 ID (동일한 타임 스탬프)가 겹치지 않습니다.

  • 마지막 10 개 정도의 비트 : 각 생성기에 대한 고유 한 값입니다. 이를 사용하면 모든 생성기가이 값으로 인해 겹치지 않는 ID를 생성하므로 생성기간에 동기화를 수행 할 필요가 없습니다 (매우 어렵습니다).

c) 타임 스탬프와 임의의 값만 사용하여 클라이언트에서 ID를 생성 할 수 있습니다 . 이렇게하면 모든 생성기를 알 필요가 없으며 각 생성기에 고유 한 값을 할당 할 수 있습니다. 반대로 이러한 ID는 전역 적으로 고유하다고 보장 할 수 없으며 고유 할 가능성매우 높습니다 . (충돌하려면 하나 이상의 생성기가 정확히 동시에 동일한 임의의 값을 생성해야합니다.)

  • 최상위 32 비트 : 타임 스탬프, ID 생성 시간.
  • 최하위 32 비트 : 32 비트 임의성, 각 ID에 대해 새로 생성됩니다.

d) 쉬운 방법 은 UUID / GUID를 사용하는 것 입니다.


답변

이제 더 많은 옵션이 있습니다.

이 질문은 “오래된”질문이지만 여기에 왔으므로 지금까지 알고있는 옵션을 그대로 두는 것이 유용 할 것 같습니다.

  • Hazelcast를 사용해 볼 수 있습니다. 1.9 릴리스에는 java.util.concurrent.AtomicLong의 분산 구현이 포함되어 있습니다.
  • Zookeeper를 사용할 수도 있습니다 . 시퀀스 노드를 만드는 방법을 제공합니다 (노드의 버전 번호 사용을 선호하지만 znode 이름에 추가됨). 그래도주의해야합니다. 시퀀스에서 누락 된 번호를 원하지 않으면 원하는 것이 아닐 수 있습니다.

건배


답변

각 노드에 고유 한 ID (어쨌든 가지고있을 수 있음)를 갖고이를 시퀀스 번호 앞에 추가 할 수 있습니다.

예를 들어 노드 1은 시퀀스 001-00001 001-00002 001-00003 등을 생성하고 노드 5는 005-00001 005-00002를 생성합니다.

독특한 🙂

또는 일종의 중앙 집중식 시스템을 원한다면 시퀀스 서버가 블록 단위로 제공되도록 고려할 수 있습니다. 이렇게하면 오버 헤드가 크게 줄어 듭니다. 예를 들어, 할당해야하는 각 ID에 대해 중앙 서버에서 새 ID를 요청하는 대신 중앙 서버에서 10,000 개의 블록으로 ID를 요청한 다음 다 떨어졌을 때 다른 네트워크 요청 만 수행하면됩니다.


답변

Redisson으로 할 수 있습니다 . 분산되고 확장 가능한 버전의 AtomicLong. 예를 들면 다음과 같습니다.

Config config = new Config();
config.addAddress("some.server.com:8291");

Redisson redisson = Redisson.create(config);
RAtomicLong atomicLong = redisson.getAtomicLong("anyAtomicLong");
atomicLong.incrementAndGet();


답변

단순히 고유하지 않고 전 세계적으로 순차적이어야한다면 이러한 숫자를 분배하기위한 단일하고 간단한 서비스를 만드는 것을 고려할 것입니다.

분산 시스템은 상호 작용하는 많은 작은 서비스에 의존하며, 이러한 간단한 작업을 위해 다른 복잡한 분산 솔루션이 정말로 필요합니까 아니면 실제로 이익을 얻을 수 있습니까?


답변

몇 가지 전략이 있습니다. 그러나 내가 아는 것은 실제로 배포되고 실제 시퀀스를 제공 할 수 없습니다.

  1. 중앙 번호 생성기가 있습니다. 큰 데이터베이스 일 필요는 없습니다. memcached빠른 원자 카운터가 있으며 대부분의 경우 전체 클러스터에 대해 충분히 빠릅니다.
  2. 각 노드에 대해 정수 범위를 분리하십시오 (예 : Steven Schlanskter의 답변 )
  3. 난수 또는 UUID 사용
  4. 노드의 ID와 함께 일부 데이터를 사용하고 모두 해시 (또는 hmac )

개인적으로 나는 UUID에 의지하거나 대부분 인접한 공간을 원한다면 memcached에 의지합니다.


답변

(스레드 안전) UUID 생성기를 사용하지 않는 이유는 무엇입니까?

나는 이것에 대해 확장해야 할 것입니다.

UUID는 전역 적으로 고유함이 보장됩니다 (유일성이 매우 높은 난수를 기반으로 한 UUID를 피하는 경우).

사용하는 UUID 생성기 수에 관계없이 각 UUID의 글로벌 고유성에 따라 “분산”요구 사항이 충족됩니다.

“스레드 안전”요구 사항은 “스레드 안전”UUID 생성기를 선택하여 충족 할 수 있습니다.

“시퀀스 번호”요구 사항은 각 UUID의 보장 된 전역 고유성에 의해 충족되는 것으로 간주됩니다.

많은 데이터베이스 시퀀스 번호 구현 (예 : Oracle)은 단조 증가하거나 (심지어) 시퀀스 번호 증가 ( “연결”기준)를 보장하지 않습니다. 연속 된 일련 번호 배치가 연결별로 “캐시 된”블록에 할당되기 때문입니다. 이는 글로벌 고유성을 보장 하고 적절한 속도를 유지한다. 그러나 실제로 할당 된 시퀀스 번호 (시간이 지남에 따라)는 여러 연결에 의해 할당 될 때 뒤죽박죽 될 수 있습니다!