자바 눈꽃 알고리즘 의 원리 와 실전 튜 토리 얼 실현

SnowFlake 알고리즘 은 트 위 터 에서 시작 하 는 분포 식 id 생 성 알고리즘 입 니 다.그 핵심 사상 은 64 bit 의 long 형 숫자 를 전체 국면 에서 유일한 id 로 사용 하 는 것 이다.분포 식 시스템 에서 의 응용 이 매우 광범 위 하고 ID 는 시간 스탬프 를 도입 하여 기본적으로 증가 하 는 것 을 유지 하 며 뒤의 코드 에 상세 한 주석 이 있다.
이 64 개의 bit 중 1 개의 bit 는 사용 하지 않 고 그 중의 41 bit 를 밀리초 로 하고 10 bit 를 작업 기계 id 로 하고 12 bit 를 시리 얼 번호 로 한다.

예 를 들 어 다음 64 bit 의 long 형 숫자 를 들 어 보 겠 습 니 다.
4.567917.첫 번 째 부분 은 bit:0 입 니 다.이것 은 무의미 합 니 다4.567917.두 번 째 부분 은 41 bit 이다.시간 스탬프 를 나타 낸다세 번 째 부분 은 5 bit:기계실 id,10001 을 나타 낸다4.567917.네 번 째 부분 은 5 bit 이다.기계 id,1 1001 을 나타 낸다4.567917.다섯 번 째 부분 은 12 비트 입 니 다.표 시 된 번호 입 니 다.바로 특정한 기관실 의 한 기계 에서 이 밀리초 안에 동시에 생 성 된 id 의 번호 입 니 다.0000 00000000① 1 bit:안 쓰 는데 왜?
이 진 에서 첫 번 째 bit 가 1 이 라면 모두 마이너스 이지 만 우리 가 생 성 한 id 는 모두 양수 이기 때문에 첫 번 째 bit 통일 은 모두 0 이다.
② 41 bit:시간 스탬프 를 나타 내 고 단 위 는 밀리초 입 니 다.
41 bit 가 표시 할 수 있 는 숫자 는 무려 2^41-1 에 달 합 니 다.즉,2^41-1 밀리초 값 을 표시 할 수 있 습 니 다.성인 으로 환산 하면 69 년 의 시간 을 표시 합 니 다.
③ 10 bit:작업 기계 id 를 기록 하 는 것 은 이 서 비 스 를 최대 2^10 대의 기계,즉 1024 대의 기계 에 배치 할 수 있 음 을 나타 낸다.
하지만 10 bit 에 서 는 5 개의 bit 가 기관실 id 를 대표 하고 5 개의 bit 는 기계 id 를 대표 합 니 다.최대 2^5 개의 기관실(32 개의 기관실)을 대표 하고,각 기관실 에 서 는 2^5 개의 기계(32 대의 기계)를 대표 할 수도 있 고,자사 의 실제 상황 에 따라 확정 할 수도 있다 는 뜻 이다.
④ 12 bit:이것 은 같은 밀리초 안에 발생 하 는 서로 다른 id 를 기록 하 는 데 쓰 인 다.
12 bit 가 대표 할 수 있 는 최대 정 수 는 2^12-1=4096 이다.즉,이 12 bit 가 대표 하 는 숫자 로 같은 밀리초 안의 4096 개의 다른 id 를 구분 할 수 있다 는 것 이다.
쉽게 말 하면 특정한 서비스 가설 이 전체 국면 에서 유일한 id 를 생 성 하려 면 SnowFlake 알고리즘 을 배치 한 시스템 에 요청 을 보 낼 수 있 습 니 다.이 SnowFlake 알고리즘 시스템 에서 유일한 id 를 생 성 할 수 있 습 니 다.
이 SnowFlake 알고리즘 시스템 은 먼저 자신 이 있 는 기관실 과 기 계 를 알 고 있 을 것 이다.예 를 들 어 기관실 id=17,기계 id=12.
이 어 SnowFlake 알고리즘 시스템 이 이 요청 을 받 은 후 먼저 바 이 너 리 연산 방식 으로 64 bit 의 long 형 id 를 생 성 합 니 다.64 bit 중 첫 번 째 bit 는 무의미 합 니 다.
이어서 41 개의 bit 를 사용 하면 현재 시간 스탬프(단위 에서 밀리초)를 사용 할 수 있 습 니 다.그리고 5 개의 bit 에 이 기계실 id 를 설정 하고 5 개의 bit 에 기계 id 를 설정 할 수 있 습 니 다.
마지막 으로 다시 한 번 판단 해 보 겠 습 니 다.현재 이 기계 의 이 밀리초 동안 이것 은 몇 번 째 요청 입 니 다.이번 id 생 성 요청 에 번 호 를 추가 하여 마지막 12 bit 로 합 니 다.
마지막 으로 64 비트 의 id 가 나 왔 습 니 다.다음 과 같 습 니 다.

이 알고리즘 은 한 기관실 의 한 기계 에서 같은 밀리초 안에 유일한 id 를 생 성 했다 고 보증 할 수 있다.한 밀리초 안에 여러 개의 id 가 생 성 될 수 있 지만 마지막 12 개의 bit 번호 로 구분 된다.
다음은 이 SnowFlake 알고리즘 의 코드 실현 을 간단하게 살 펴 보 겠 습 니 다.이것 이 바로 예제 입 니 다.여러분 이 이 뜻 을 이해 한 후에 이 알고리즘 을 스스로 개조 해 보 세 요.
한 마디 로 하면 64 bit 의 숫자 중 각 bit 비트 로 서로 다른 표지 위 치 를 설정 하고 모든 id 를 구분 하 는 것 이다.
SnowFlake 알고리즘 구현 코드 는 다음 과 같 습 니 다.

 
public class IdWorker {
 
	//          bit      1,      ,        id     ,      bit      0。
 
	//  ID  2  5   32   1  31 
	private long workerId;
	//  ID 2  5   32   1  31 
	private long datacenterId;
	//           id       12  4096 -1 = 4095  
	private long sequence;
	//             2^41 - 1         69 
	private long twepoch = 1585644268888L;
	//5    id
	private long workerIdBits = 5L;
	//5    id
	private long datacenterIdBits = 5L;
	//       id  2   12  
	private long sequenceBits = 12L;
	//         ,  5 bit     31   ,      id     32  
	private long maxWorkerId = -1L ^ (-1L << workerIdBits);
	//        ,  5 bit     31   ,  id     32  
	private long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
 
	private long workerIdShift = sequenceBits;
	private long datacenterIdShift = sequenceBits + workerIdBits;
	private long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
	private long sequenceMask = -1L ^ (-1L << sequenceBits);
	//         ,      1  
	private long lastTimestamp = -1L;
	public long getWorkerId(){
		return workerId;
	}
	public long getDatacenterId() {
		return datacenterId;
	}
	public long getTimestamp() {
		return System.currentTimeMillis();
	}
 
 
 
	public IdWorker(long workerId, long datacenterId, long sequence) {
 
		//     id   id    31     0
		if (workerId > maxWorkerId || workerId < 0) {
			throw new IllegalArgumentException(
					String.format("worker Id can't be greater than %d or less than 0",maxWorkerId));
		}
 
		if (datacenterId > maxDatacenterId || datacenterId < 0) {
 
			throw new IllegalArgumentException(
					String.format("datacenter Id can't be greater than %d or less than 0",maxDatacenterId));
		}
		this.workerId = workerId;
		this.datacenterId = datacenterId;
		this.sequence = sequence;
	}
 
	//        ,    nextId()  ,         snowflake             id
	public synchronized long nextId() {
		//            ,     
		long timestamp = timeGen();
		if (timestamp < lastTimestamp) {
 
			System.err.printf(
					"clock is moving backwards. Rejecting requests until %d.", lastTimestamp);
			throw new RuntimeException(
					String.format("Clock moved backwards. Refusing to generate id for %d milliseconds",
							lastTimestamp - timestamp));
		}
 
		//              ,            id
		//        seqence     1,    4096
		if (lastTimestamp == timestamp) {
 
			//                 4096   ,         ,
			//            4096     ,        sequence   4096    
			sequence = (sequence + 1) & sequenceMask;
			//        ,   id    4095,       ,      ,      ID
			if (sequence == 0) {
				timestamp = tilNextMillis(lastTimestamp);
			}
 
		} else {
			sequence = 0;
		}
		//             id    ,     
		lastTimestamp = timestamp;
		//                 ,    64bit id
		//          ,  41 bit  ;   id    5 bit  ;   id    5 bit  ;      12 bit
		//          64 bit      ,   10     long 
		return ((timestamp - twepoch) << timestampLeftShift) |
				(datacenterId << datacenterIdShift) |
				(workerId << workerIdShift) | sequence;
	}
 
	/**
	 *         ,   id    4095,       ,      ,      ID
	 * @param lastTimestamp
	 * @return
	 */
	private long tilNextMillis(long lastTimestamp) {
 
		long timestamp = timeGen();
 
		while (timestamp <= lastTimestamp) {
			timestamp = timeGen();
		}
		return timestamp;
	}
	//       
	private long timeGen(){
		return System.currentTimeMillis();
	}
 
	/**
	 *  main    
	 * @param args
	 */
	public static void main(String[] args) {
		System.out.println(1&4596);
		System.out.println(2&4596);
		System.out.println(6&4596);
		System.out.println(6&4596);
		System.out.println(6&4596);
		System.out.println(6&4596);
//		IdWorker worker = new IdWorker(1,1,1);
//		for (int i = 0; i < 22; i++) {
//			System.out.println(worker.nextId());
//		}
	}
}
SnowFlake 알고리즘 의 장점:
(1)고성능 고가 용:생 성 시 데이터베이스 에 의존 하지 않 고 완전히 메모리 에서 생 성 된다.
(2)용량 이 크다:1 초 에 수백 만 의 자체 증가 ID 를 생 성 할 수 있다.
(3)ID 자체 증가:데이터베이스 에 저장 하면 색인 효율 이 높다.
SnowFlake 알고리즘 의 단점:
시스템 시간 과 의 일치 성에 의존 합 니 다.시스템 시간 이 바 뀌 거나 바 뀌 면 id 충돌 이나 중복 이 발생 할 수 있 습 니 다.
실제 적 으로 우리 의 기계실 은 그렇게 많 지 않다.우 리 는 알고리즘 을 개선 하고 10bit 의 기계 id 를 최적화 시 켜 업무 표 나 우리 시스템 과 관련 된 업무 로 만 들 수 있다.
자바 가 눈꽃 알고리즘 을 실현 하 는 원리 와 실전 튜 토리 얼 에 관 한 이 글 은 여기까지 소개 되 었 습 니 다.더 많은 자바 가 눈꽃 알고리즘 을 실현 하 는 내용 은 우리 의 이전 글 을 검색 하거나 아래 의 관련 글 을 계속 찾 아 보 세 요.앞으로 많은 응원 부탁드립니다!

좋은 웹페이지 즐겨찾기