> php教程 > php手册 > 분산 시스템을 위한 고유 ID 생성 솔루션 요약

분산 시스템을 위한 고유 ID 생성 솔루션 요약

坏嘻嘻
풀어 주다: 2018-09-14 13:39:20
원래의
5495명이 탐색했습니다.

고유 시스템 ID는 시스템을 설계할 때 자주 접하는 문제이며, 우리는 이 문제로 종종 어려움을 겪습니다. 다양한 시나리오, 요구 사항 및 성능 요구 사항에 맞게 ID를 생성하는 방법에는 여러 가지가 있습니다. 따라서 일부 더 복잡한 시스템에는 여러 ID 생성 전략이 있습니다. 다음은 몇 가지 일반적인 ID 생성 전략입니다.

1. 데이터베이스 자체 증가 시퀀스 또는 필드

가장 일반적인 방법입니다. 데이터베이스를 사용하면 전체 데이터베이스가 고유합니다.

장점:

  1. 간단하고 편리한 코드, 만족스러운 성능.

  2. 숫자 ID가 자연스럽게 정렬되어 페이징이나 정렬이 필요한 결과에 매우 유용합니다.

단점:

  1. 데이터베이스마다 구문과 구현이 다르므로 데이터베이스 마이그레이션 중에 또는 여러 데이터베이스 버전을 지원할 때 처리해야 합니다.

  2. 단일 데이터베이스 또는 읽기-쓰기 분리 또는 하나의 마스터와 다중 슬레이브의 경우 하나의 마스터 라이브러리만 생성할 수 있습니다. 단일 장애 지점이 발생할 위험이 있습니다.

  3. 성능이 요구 사항을 충족하지 못하는 경우 확장이 더 어렵습니다.

  4. 병합해야 하는 여러 시스템이 있거나 데이터 마이그레이션이 관련된 경우 상당히 고통스러울 것입니다.

  5. 테이블과 데이터베이스를 분리할 때 문제가 발생합니다.

최적화 계획:

  1. 메인 라이브러리의 단일 지점에 대해 여러 마스터 라이브러리가 있는 경우 각 마스터 라이브러리에 대해 설정된 시작 번호가 다르며 단계 길이는 동일합니다. 마스터의 수입니다. 예: Master1은 1, 4, 7, 10을 생성하고 Master2는 2,5,8,11을 생성하고 Master3은 3,6,9,12를 생성합니다. 이를 통해 클러스터에서 고유한 ID를 효과적으로 생성할 수 있으며 ID 생성 데이터베이스 작업의 로드를 크게 줄일 수도 있습니다.

2. UUID 일반적인 방법.

데이터베이스나 프로그램을 사용하여 생성할 수 있습니다. 일반적으로 세계에서 유일합니다.

장점:

  1. 간단하고 코딩하기 쉽습니다.

  2. ID 생성 성능은 매우 좋으며, 기본적으로 성능 문제는 없습니다.

  3. 세상에 단 하나뿐인 데이터 마이그레이션, 시스템 데이터 병합, 데이터베이스 변경 등이 발생해도 침착하게 처리할 수 있습니다.

단점:

  1. 정렬이 없으며 추세가 증가한다고 보장할 수 없습니다.

  2. UUID는 문자열을 사용하여 저장되는 경우가 많아 쿼리 효율성이 상대적으로 낮습니다.

  3. 저장 공간이 비교적 큰 데이터베이스라면 저장 용량을 고려해야 합니다.

  4. 대량의 데이터 전송됨

  5. 을(를) 읽을 수 없습니다.

3. Redis가 ID를 생성합니다

데이터베이스를 사용하여 ID를 생성하는 성능이 충분하지 않은 경우 Redis를 사용하여 ID를 생성해 볼 수 있습니다. 이는 주로 단일 스레드인 Redis에 의존하므로 전역적으로 고유한 ID를 생성하는 데에도 사용할 수 있습니다. 이는 Redis의 원자적 연산 INCR 및 INCRBY를 사용하여 달성할 수 있습니다.

Redis 클러스터를 사용하면 더 높은 처리량을 얻을 수 있습니다. 클러스터에 5개의 Redis가 있다고 가정합니다. 각 Redis의 값은 각각 1, 2, 3, 4, 5로 초기화할 수 있으며, 이후 단계 크기는 5입니다. 각 Redis에서 생성된 ID는 다음과 같습니다.

A: 1,6,11,16,21 B: 2,7,12,17,22 C: 3,8,13,18,23 D: 4,9,14 , 19,24 E: 5,10,15,20,25

이것은 로드되는 시스템에 따라 결정될 수 있으며 나중에 수정하기 어려울 것입니다. 그러나 3~5개의 서버는 기본적으로 서버의 요구를 충족할 수 있으며 모두 다른 ID를 얻을 수 있습니다. 단, 단계 크기와 초기값이 미리 필요합니다. Redis 클러스터를 사용하면 단일 실패 지점 문제도 해결할 수 있습니다.

또한 Redis를 사용하여 0부터 시작하는 일일 일련번호를 생성하는 것이 더 적합합니다. 예를 들어 주문번호 = 날짜 + 해당일의 자체 증가 숫자입니다. Redis에서 매일 키를 생성하고 INCR을 사용하여 축적할 수 있습니다.

장점:

  1. 은 데이터베이스에 의존하지 않고 유연하고 편리하며 데이터베이스보다 성능이 좋습니다.

  2. 숫자 ID가 자연스럽게 정렬되어 페이징이나 정렬이 필요한 결과에 매우 유용합니다.

단점:

  1. 시스템에 Redis가 없으면 새로운 구성 요소를 도입해야 하므로 시스템 복잡성이 증가합니다.

  2. 코딩 및 구성에 필요한 작업량은 상대적으로 큽니다.

4. Twitter의 눈송이 알고리즘

snowflake는 Twitter의 오픈 소스 분산 ID 생성 알고리즘이며, 그 결과는 긴 ID입니다. 핵심 아이디어는 41비트를 밀리초 수로 사용하고, 10비트를 시스템 ID로 사용하고(5비트는 데이터 센터, 5비트는 시스템 ID), 12비트를 밀리초 내의 일련 번호로 사용하는 것입니다. 4096 ID 생성) 끝에는 항상 0인 부호 비트가 있습니다. 구체적인 구현 코드는 https://github.com/twitter/snowflake

에서 확인할 수 있습니다.
public class IdWorker {
// ==============================Fields===========================================
/** 开始时间截 (2015-01-01) */
private final long twepoch = 1420041600000L;

/** 机器id所占的位数 */
private final long workerIdBits = 5L;

/** 数据标识id所占的位数 */
private final long datacenterIdBits = 5L;

/** 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数) */
private final long maxWorkerId = -1L ^ (-1L << workerIdBits);

/** 支持的最大数据标识id,结果是31 */
private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);

/** 序列在id中占的位数 */
private final long sequenceBits = 12L;

/** 机器ID向左移12位 */
private final long workerIdShift = sequenceBits;

/** 数据标识id向左移17位(12+5) */
private final long datacenterIdShift = sequenceBits + workerIdBits;

/** 时间截向左移22位(5+5+12) */
private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;

/** 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095) */
private final long sequenceMask = -1L ^ (-1L << sequenceBits);

/** 工作机器ID(0~31) */
private long workerId;

/** 数据中心ID(0~31) */
private long datacenterId;

/** 毫秒内序列(0~4095) */
private long sequence = 0L;

/** 上次生成ID的时间截 */
private long lastTimestamp = -1L;

//==============================Constructors=====================================
/**
 * 构造函数
 * @param workerId 工作ID (0~31)
 * @param datacenterId 数据中心ID (0~31)
 */
public IdWorker(long workerId, long datacenterId) {
    if (workerId > maxWorkerId || workerId < 0) {
        throw new IllegalArgumentException(String.format("worker Id can&#39;t be greater than %d or less than 0", maxWorkerId));
    }
    if (datacenterId > maxDatacenterId || datacenterId < 0) {
        throw new IllegalArgumentException(String.format("datacenter Id can&#39;t be greater than %d or less than 0", maxDatacenterId));
    }
    this.workerId = workerId;
    this.datacenterId = datacenterId;
}

// ==============================Methods==========================================
/**
 * 获得下一个ID (该方法是线程安全的)
 * @return SnowflakeId
 */
public synchronized long nextId() {
    long timestamp = timeGen();

    //如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常
    if (timestamp < lastTimestamp) {
        throw new RuntimeException(
                String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
    }

    //如果是同一时间生成的,则进行毫秒内序列
    if (lastTimestamp == timestamp) {
        sequence = (sequence + 1) & sequenceMask;
        //毫秒内序列溢出
        if (sequence == 0) {
            //阻塞到下一个毫秒,获得新的时间戳
            timestamp = tilNextMillis(lastTimestamp);
        }
    }
    //时间戳改变,毫秒内序列重置
    else {
        sequence = 0L;
    }

    //上次生成ID的时间截
    lastTimestamp = timestamp;

    //移位并通过或运算拼到一起组成64位的ID
    return ((timestamp - twepoch) << timestampLeftShift) //
            | (datacenterId << datacenterIdShift) //
            | (workerId << workerIdShift) //
            | sequence;
}

/**
 * 阻塞到下一个毫秒,直到获得新的时间戳
 * @param lastTimestamp 上次生成ID的时间截
 * @return 当前时间戳
 */
protected long tilNextMillis(long lastTimestamp) {
    long timestamp = timeGen();
    while (timestamp <= lastTimestamp) {
        timestamp = timeGen();
    }
    return timestamp;
}

/**
 * 返回以毫秒为单位的当前时间
 * @return 当前时间(毫秒)
 */
protected long timeGen() {
    return System.currentTimeMillis();
}

//==============================Test=============================================
/** 测试 */
public static void main(String[] args) {
    IdWorker idWorker = new IdWorker(0, 0);
    for (int i = 0; i < 1000; i++) {
        long id = idWorker.nextId();
        System.out.println(Long.toBinaryString(id));
        System.out.println(id);
    }
}}
로그인 후 복사

snowflake 알고리즘은 프로젝트의 필요에 따라 수정될 수 있습니다. 예를 들어, 향후 데이터 센터 수, 각 데이터 센터의 머신 수, 가능한 동시성 수를 통일된 밀리초 단위로 추정하여 알고리즘에 필요한 비트 수를 조정합니다.

장점:

  1. 은 데이터베이스에 의존하지 않고 유연하고 편리하며 데이터베이스보다 성능이 좋습니다.

  2. 단일 머신에서 ID는 시간에 따라 증가합니다.

단점:

  1. 단일 머신에서는 증분식이지만 분산 환경으로 인해 각 머신의 시계를 완전히 동기화할 수 없으며 때로는 전체적으로 증분식이 아닌 상황이 있을 수 있습니다.

5. Zookeeper를 사용하여 고유 ID 생성

zookeeper는 주로 znode 데이터 버전을 통해 일련 번호를 생성합니다. 클라이언트는 이 버전 번호를 고유하게 사용할 수 있습니다. 일련 번호.

Zookeeper는 고유 ID를 생성하는 데 거의 사용되지 않습니다. 주로 사육사에 의존하고 여러 단계에서 API를 호출하기 때문에 경쟁이 높으면 분산 잠금 사용을 고려해야 합니다. 따라서 동시성이 높은 분산 환경에서는 성능이 이상적이지 않습니다.

6. MongoDB의 ObjectId

MongoDB의 ObjectId는 눈송이 알고리즘과 유사합니다. 경량으로 설계되었으며, 전 세계적으로 고유한 동일한 방법을 사용하여 다양한 기계에서 쉽게 생성할 수 있습니다. MongoDB는 처음부터 분산 데이터베이스로 설계되었으며 여러 노드를 처리하는 것이 핵심 요구 사항입니다. 샤딩된 환경에서 생성하기가 훨씬 쉬워졌습니다. 형식은 다음과 같습니다: [src/main/resources/objectId.png] 여기에 그림 설명을 작성하십시오:

분산 시스템을 위한 고유 ID 생성 솔루션 요약

처음 4바이트는 표준 에포크에서 시작하는 타임스탬프(초)입니다. 다음 5바이트와 결합된 타임스탬프는 두 번째 수준의 고유성을 제공합니다. 타임스탬프가 먼저 나오므로 이는 ObjectId가 삽입된 순서대로 대략 정렬된다는 의미입니다. 이는 효율성을 높이기 위해 인덱스로 사용하는 것과 같은 작업에 유용합니다. 이 4바이트는 문서가 생성된 시간도 의미합니다. 대부분의 클라이언트 라이브러리는 ObjectId에서 이 정보를 얻기 위한 메서드를 공개합니다. 다음 3바이트는 호스트의 고유 식별자입니다. 일반적으로 시스템 호스트 이름의 해시입니다. 이렇게 하면 서로 다른 호스트가 충돌 없이 서로 다른 ObjectId를 생성할 수 있습니다. 동일한 시스템의 여러 동시 프로세스에서 생성된 ObjectId가 고유한지 확인하기 위해 다음 2바이트는 ObjectId를 생성한 프로세스 식별자(PID)에서 가져옵니다. 처음 9바이트는 동일한 초에 서로 다른 시스템의 서로 다른 프로세스에서 생성된 ObjectId가 고유함을 보장합니다. 마지막 3바이트는 동일한 프로세스에서 동일한 초에 생성된 ObjectId도 서로 다른지 확인하기 위해 자동으로 증가하는 카운터입니다. 각 프로세스는 동일한 초에 최대 2563(16 777 216)개의 서로 다른 ObjectId를 가질 수 있습니다.

관련 추천:

php 보도자료 관리 시스템 개발 예시

PHP 개발 간단한 보도자료 시스템 튜토리얼

위 내용은 분산 시스템을 위한 고유 ID 생성 솔루션 요약의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
원천:php.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
최신 이슈
인기 추천
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿