나는 일반적으로 과거에 데이터베이스 시퀀스를 사용하여 시퀀스 번호 생성 을 구현했습니다 .
예 : Postgres SERIAL 유형 사용 http://www.neilconway.org/docs/sequences/
데이터베이스가없는 대규모 분산 시스템의 시퀀스 번호를 생성하는 방법이 궁금합니다. 누구나 여러 클라이언트 에 대해 스레드 안전 방식으로 시퀀스 번호 생성을 달성하기위한 모범 사례에 대한 경험이나 제안이 있습니까?
나는 일반적으로 과거에 데이터베이스 시퀀스를 사용하여 시퀀스 번호 생성 을 구현했습니다 .
예 : Postgres SERIAL 유형 사용 http://www.neilconway.org/docs/sequences/
데이터베이스가없는 대규모 분산 시스템의 시퀀스 번호를 생성하는 방법이 궁금합니다. 누구나 여러 클라이언트 에 대해 스레드 안전 방식으로 시퀀스 번호 생성을 달성하기위한 모범 사례에 대한 경험이나 제안이 있습니까?
답변:
좋아요, 이것은 제가 지금 처음 보는 아주 오래된 질문입니다.
특정 기준 (일반적으로 생성 시간)에 따라 (선택적으로) 느슨하게 정렬 할 수있는 시퀀스 번호 와 고유 ID 를 구별해야합니다 . 실제 시퀀스 번호는 다른 모든 작업자가 수행 한 작업에 대한 지식을 의미하므로 공유 상태가 필요합니다. 분산 된 대규모 방식으로이 작업을 수행하는 쉬운 방법은 없습니다. 네트워크 브로드 캐스트, 각 작업자에 대한 창 범위, 고유 작업자 ID에 대한 분산 해시 테이블 등을 살펴볼 수 있지만 많은 작업이 필요합니다.
고유 ID는 또 다른 문제입니다. 분산 된 방식으로 고유 ID를 생성하는 몇 가지 좋은 방법이 있습니다.
a) Twitter의 Snowflake ID 네트워크 서비스를 사용할 수 있습니다 . Snowflake는 다음과 같습니다.
b) UUID 및 Snowflake의 ID가 만들어 지는 방식에서 파생 된 접근 방식을 사용하여 클라이언트 자체에서 고유 ID를 생성 할 수 있습니다 . 여러 옵션이 있지만 다음과 같은 내용이 있습니다.
최상위 40 비트 정도 : 타임 스탬프; ID의 생성 시간. (우리는 생성 시간별로 ID를 정렬 할 수 있도록 타임 스탬프에 가장 중요한 비트를 사용하고 있습니다.)
다음 14 개 정도의 비트 : 각 생성기가 생성 된 각 새 ID에 대해 하나씩 증가하는 생성기 별 카운터 . 이렇게하면 동시에 생성 된 ID (동일한 타임 스탬프)가 겹치지 않습니다.
마지막 10 개 정도의 비트 : 각 생성기에 대한 고유 한 값입니다. 이를 사용하면 모든 생성기가이 값으로 인해 겹치지 않는 ID를 생성하므로 생성기간에 동기화를 수행 할 필요가 없습니다 (매우 어렵습니다).
c) 타임 스탬프와 임의의 값만 사용하여 클라이언트에서 ID를 생성 할 수 있습니다 . 이렇게하면 모든 생성기를 알 필요가 없으며 각 생성기에 고유 한 값을 할당 할 수 있습니다. 반대로 이러한 ID는 전역 적으로 고유하다고 보장 할 수 없으며 고유 할 가능성 이 매우 높습니다 . (충돌하려면 하나 이상의 생성기가 정확히 동시에 동일한 임의의 값을 생성해야합니다.)
d) 쉬운 방법 은 UUID / GUID를 사용하는 것 입니다.
twitter/snowflake
더 이상 유지되지 않습니다
이제 더 많은 옵션이 있습니다.
이 질문은 "오래된"질문이지만 여기에 왔으므로 지금까지 알고있는 옵션을 그대로 두는 것이 유용 할 것 같습니다.
건배
각 노드에 고유 한 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();
단순히 고유하지 않고 전 세계적으로 순차적이어야한다면 이러한 숫자를 분배하기위한 단일하고 간단한 서비스를 만드는 것을 고려할 것입니다.
분산 시스템은 상호 작용하는 많은 작은 서비스에 의존하며, 이러한 간단한 작업을 위해 다른 복잡한 분산 솔루션이 정말로 필요합니까 아니면 실제로 이익을 얻을 수 있습니까?
몇 가지 전략이 있습니다. 그러나 내가 아는 것은 실제로 배포되고 실제 시퀀스를 제공 할 수 없습니다.
memcached
빠른 원자 카운터가 있으며 대부분의 경우 전체 클러스터에 대해 충분히 빠릅니다.개인적으로 나는 UUID에 의지하거나 대부분 인접한 공간을 원한다면 memcached에 의지합니다.
(스레드 안전) UUID 생성기를 사용하지 않는 이유는 무엇입니까?
나는 이것에 대해 확장해야 할 것입니다.
UUID는 전역 적으로 고유함이 보장됩니다 (유일성이 매우 높은 난수를 기반으로 한 UUID를 피하는 경우).
사용하는 UUID 생성기 수에 관계없이 각 UUID의 글로벌 고유성에 따라 "분산"요구 사항이 충족됩니다.
"스레드 안전"요구 사항은 "스레드 안전"UUID 생성기를 선택하여 충족 할 수 있습니다.
"시퀀스 번호"요구 사항은 각 UUID의 보장 된 전역 고유성에 의해 충족되는 것으로 간주됩니다.
많은 데이터베이스 시퀀스 번호 구현 (예 : Oracle)은 단조 증가하거나 (심지어) 시퀀스 번호 증가 ( "연결"기준)를 보장하지 않습니다. 연속 된 일련 번호 배치가 연결별로 "캐시 된"블록에 할당되기 때문입니다. 이는 글로벌 고유성을 보장 하고 적절한 속도를 유지한다. 그러나 실제로 할당 된 시퀀스 번호 (시간이 지남에 따라)는 여러 연결에 의해 할당 될 때 뒤죽박죽 될 수 있습니다!
나는 이것이 오래된 질문이라는 것을 알고 있지만 우리도 같은 요구에 직면했고 우리의 요구를 충족시키는 해결책을 찾을 수 없었습니다. 우리의 요구 사항은 고유 한 ID 시퀀스 (0,1,2,3 ... n)를 가져 오는 것이었기 때문에 눈송이가 도움이되지 않았습니다. Redis를 사용하여 ID를 생성하기 위해 자체 시스템을 만들었습니다. Redis는 단일 스레드이므로 목록 / 대기열 메커니즘은 항상 한 번에 1 개의 팝을 제공합니다.
우리가하는 일은 ID 버퍼를 생성하는 것입니다. 처음에 큐에는 요청시 발송할 준비가 된 0 ~ 20 개의 ID가 있습니다. 여러 클라이언트가 ID를 요청할 수 있으며 redis는 한 번에 1 개의 ID를 팝합니다. 왼쪽에서 팝할 때마다 BUFFER + currentId를 오른쪽에 삽입하여 버퍼 목록을 계속 유지합니다. 여기에서 구현
데이터베이스를 사용하면 단일 코어로 초당 1.000+ 증가에 도달 할 수 있습니다. 꽤 쉽습니다. 자체 데이터베이스를 백엔드로 사용하여 해당 숫자를 생성 할 수 있습니다 (DDD 용어로 자체 집계 여야하므로).
비슷한 문제가있는 것 같습니다. 나는 여러 파티션을 가지고 있었고 각각에 대한 오프셋 카운터를 얻고 싶었습니다. 다음과 같이 구현했습니다.
CREATE DATABASE example;
USE example;
CREATE TABLE offsets (partition INTEGER, offset LONG, PRIMARY KEY (partition));
INSERT offsets VALUES (1,0);
그런 다음 다음 문을 실행했습니다.
SELECT @offset := offset from offsets WHERE partition=1 FOR UPDATE;
UPDATE offsets set offset=@offset+1 WHERE partition=1;
응용 프로그램에서 허용하는 경우 한 번에 블록을 할당 할 수 있습니다 (내 경우).
SELECT @offset := offset from offsets WHERE partition=1 FOR UPDATE;
UPDATE offsets set offset=@offset+100 WHERE partition=1;
추가 처리량이 필요하고 오프셋을 미리 할당 할 수없는 경우 실시간 처리를 위해 Flink를 사용하여 자체 서비스를 구현할 수 있습니다. 파티션 당 약 10 만 증가를 얻을 수있었습니다.
도움이 되었기를 바랍니다.
문제는 다음과 유사합니다. iscsi 세계에서 각 LUN / 볼륨은 클라이언트 측에서 실행되는 이니시에이터가 고유하게 식별 할 수 있어야합니다. iscsi 표준에 따르면 처음 몇 비트는 스토리지 공급자 / 제조업체 정보를 나타내야하고 나머지는 단조롭게 증가합니다.
마찬가지로 노드의 분산 시스템에서 초기 비트를 사용하여 nodeID를 나타낼 수 있으며 나머지는 단조롭게 증가 할 수 있습니다.