Python 实现雪花算法

Python 实现雪花算法

雪花算法:雪花算法是一种分布式全局唯一ID,一般不需要过多的深入了解,一般个人项目用不到分布式之类的大型架构,另一方面,则是因为,就算用到市面上很多 ID 生成器帮我们完成了这项工作。

介绍:Twitter 于 2010 年开源了内部团队在用的一款全局唯一 ID 生成算法 Snowflake,翻译过来叫做雪花算法。Snowflake 不借助数据库,可直接由编程语言生成,它通过巧妙的位设计使得 ID 能够满足递增属性,且生成的 ID 并不是依次连续的。

参考文章:https://www.cnblogs.com/oklizz/p/11865750.html

1.原理及介绍

Snowflake 是 Twitter 提出的一个算法,其目的是生成一个64位的整数;

64位的分布图如下图所示:

雪花算法

  • 1 bit:一般是符号位,不做处理。
  • 41bit : 用来记录时间戳,这里可以记录69年,如果设置好起始时间,比如今年是 2022 ,那么可以用到 2091 年,到时候怎么办,这个系统要是能够使用 69 年,估计系统早已经优化过很多次了。
  • 10bit : 用来记录机器ID,总共可以记录1024台机器,一般用前5位代表数据中心,后面5位是某个数据中心的机器ID。(注:位数可以根据情况进行设定。)
  • 12bit:循环用来对同一个毫秒内产生的不同的 ID,12位可以最多记录4095(212-1)次,多余的需要在下一毫秒进行处理。

上面是一个将64bit划分标准,当然也不一定这么做,可以根据不同的业务的具体场景进行行划分,例如:

  1. 服务目前QPS10万,预计几年之内会发展到百万。

  2. 当前机器三地部署,上海,北京,深圳都有。

  3. 当前机器10台左右,预计未来会增加至百台。

这个时候我们根据上面的场景可以再次合理的划分62 bit,QPS 几年之内会发展到百万,那么每毫秒就是千级的请求,目前 10 台机器那么每台机器承担百级的请求,为了保证扩展,后面的循环位可以限制到 1024,也就是2^10,那么循环位10位就足够了

机器三地部署我们可以用3bit总共8来表示机房位置,当前的机器10台,为了保证扩展到百台那么可以用7bit 128来表示,时间位依然是41bit,那么还剩下64-10-3-7-41-1 = 2bit,还剩下2bit可以用来进行扩展。

复制

时钟回拨:因为机器的原因会发生时间回拨,雪花算法是强依赖时间的,如果发生时间回拨,有可能会发生重复ID,在我们上面的nextId中我们用当前时间和上一次时间进行判断,如果当前时间小于上一次的时间,那么肯定是发生了回拨,算法会直接抛出异常。

2.实现

2.1 知识补充

  • python中为位运算

    运算符描述实例
    <<左移运算符:运算数的各二进位全部左移若干位,
    <<右边的数字指定了移动的位数,高位丢弃(前面无效的0),低位补0.
    60 << 2 = 240
    >>右移运算符:把>>左边的的运算数的各二进位全部
    右移若干位,运算符右边的数字指定了右移的位数。
    低位丢弃(无效的0),高位补0.
    60>>2 = 15
    ^按位异或运算符:当两两对应的二进位相异时,结果取1.01^11 = 10
    a = 60 # 60 的二进制位数是: 0011 1100 (111100)
    
    print(a << 2) # 0011 1100 左移两位 1111 0000 = 240
    
    print(a >> 2) # 0011 1100 右移两位 0000 1111 = 15
    

    image-20220819153440572

    # ^ 二进制之间的异或运算,当两值不同的时候为 1.
    8 ^ 16 = 24
    # 0000 1000   8 
    # 0001 0000	  16
    # 0001 1000   24   结果
    

    image-20220819154645947

  • 回顾 bin 函数

    bin()函数的返回值要从Ob之后开始阅读。

    image-20220819151336024

2.2 算法实现

import time
import logging

from exceptions import InvalidSystemClock # 继承的Excpetion即可。

# 64 位 id 的划分,通常机器位和数据位各为 5 位
WORKER_ID_BITS = 5 # 机器位
DATACENTER_ID_BITS = 5 # 数据位
SEQUENCE_BITS = 12 # 循环位

# 最大取值计算,计算机中负数表示为他的补码
MAX_WORKER_ID = -1^(-1 << WORKER_ID_BITS) # 2**5 -1 =31
MAX_DATACENTER_ID = -1 ^(-1 << DATACENTER_ID_BITS)

# 移位偏移计算
WORKER_ID_SHIFT = SEQUENCE_BITS
DATACENTER_ID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS
TIMESTAMP_LEFT_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS + DATACENTER_ID_BITS

# X序号循环掩码
SEQUENCE_MASK = -1^(-1 << SEQUENCE_BITS)

# Twitter 元年时间戳
TWEPOCH = 1288834974657

logger = logging.getLogger('雪花算法')

class IdWorker(object):
    '''
    用于生成IDS.
    '''

    def __init__(self,datacenter_id,worker_id,sequence=0):
        '''
        初始化方法
        :param datacenter_id:数据id
        :param worker_id:机器id
        :param sequence:序列码
        '''
        if worker_id > MAX_WORKER_ID or worker_id <0:
            raise ValueError('worker_id 值越界')
        if datacenter_id >MAX_DATACENTER_ID or datacenter_id < 0:
            raise ValueError('datacenter_id 值越界')

        self.worker_id = worker_id
        self.datacenter_id = datacenter_id
        self.sequence = sequence

        self.last_timestamp = -1 # 上次计算的时间戳

    def _gen_timestamp(self):
        '''
        生成整数时间戳。
        :return:
        '''
        return int(time.time()*1000)

    def get_id(self):
        '''
        获取新的ID.
        :return:
        '''
        # 获取当前时间戳
        timestamp = self._gen_timestamp()

        # 时钟回拨的情况
        if timestamp < self.last_timestamp:
            logging.error('clock is moving backwards. Rejecting requests util {}'.format(self.last_timestamp))
            raise InvalidSystemClock


        if timestamp == self.last_timestamp:
            # 同一毫秒的处理。
            self.sequence = (self.sequence+1) & SEQUENCE_MASK
            if self.sequence == 0:
                timestamp =self._til_next_millis(self.last_timestamp)
        else:
            self.sequence = 0

        self.last_timestamp =timestamp

        new_id = (((timestamp - TWEPOCH) << TIMESTAMP_LEFT_SHIFT)|(self.datacenter_id << DATACENTER_ID_SHIFT)|(self.worker_id << WORKER_ID_SHIFT))|self.sequence
        return new_id


    def _til_next_millis(self,last_timestamp):
        '''
        等到下一毫秒。
        :param last_timestamp:
        :return:
        '''
        timestamp = self._gen_timestamp()
        while timestamp <= last_timestamp:
            timestamp = self._gen_timestamp()
        return timestamp

if __name__ == '__main__':
    worker = IdWorker(1,2,0)
    print(worker.get_id())

image-20220819210716468

2.3 第三方包的使用

pip install pysnowflake

启动服务

snowflake_start_server --worker=1

image-20220819212353337

编写程序,获取id

from snowflake import client

print(client.get_guid())

image-20220819212502856

继续努力,终成大器!

  • 8
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 雪花算法是一种用于生成唯一 ID 的算法。它通常用于分布式系统中,用于生成唯一的数字 ID。 下面是用 Python 实现雪花算法的示例: ``` import time class Snowflake: def __init__(self, worker_id, datacenter_id): self.worker_id = worker_id self.datacenter_id = datacenter_id self.epoch = 1577808000000 # 开始时间, 2020-01-01 00:00:00 self.worker_id_bits = 5 # worker id 的位数 self.datacenter_id_bits = 5 # datacenter id 的位数 self.max_worker_id = -1 ^ (-1 << self.worker_id_bits) # 最大 worker id self.max_datacenter_id = -1 ^ (-1 << self.datacenter_id_bits) # 最大 datacenter id self.sequence_bits = 12 # 序列号的位数 self.worker_id_shift = self.sequence_bits # worker id 在 id 中的偏移量 self.datacenter_id_shift = self.sequence_bits + self.worker_id_bits # datacenter id 在 id 中的偏移量 self.timestamp_left_shift = self.sequence_bits + self.worker_id_bits + self.datacenter_id_bits # 时间戳在 id 中的偏移量 self.sequence_mask = -1 ^ (-1 << self.sequence_bits) # 序列号的掩码 self.last_timestamp = -1 # 上次生成 id 的时间戳 self.sequence = 0 # 序列号 def get_next_id(self): """生成唯一 id""" timestamp = int(time.time() * 1000) # 如果时间戳小于上次生成 id 的时间戳, 说明系统时钟回退过, 这里做异常处理 if timestamp < self.last_timestamp: raise ValueError(f'Clock moved backwards. Refusing to generate id for {self.last_timestamp - timestamp} milliseconds') # 如果时间戳和上次生成 id 的时间戳相同 if timestamp == self.last_timestamp: self.sequence = (self. ### 回答2: 雪花算法也被称为Twitter的分布式ID生成算法,可以用于生成全局唯一的ID。下面是使用Python实现雪花算法的示例代码: ```python import time class SnowflakeIDGenerator: def __init__(self, worker_id, datacenter_id): # 初始化参数 self.worker_id = worker_id self.datacenter_id = datacenter_id self.sequence = 0 self.last_timestamp = -1 # 定义一些常量 self.TWITTER_EPOCH = 1288834974657 self.SEQUENCE_BITS = 12 self.WORKER_ID_BITS = 5 self.DATACENTER_ID_BITS = 5 self.MAX_SEQUENCE = 4095 # 校验参数合法性 if self.worker_id < 0 or self.worker_id >= (1 << self.WORKER_ID_BITS): raise ValueError("Worker ID超出范围") if self.datacenter_id < 0 or self.datacenter_id >= (1 << self.DATACENTER_ID_BITS): raise ValueError("Datacenter ID超出范围") def generate_id(self): timestamp = self.get_timestamp() if timestamp < self.last_timestamp: raise ValueError("时钟回拨发生") if timestamp == self.last_timestamp: self.sequence = (self.sequence + 1) & self.MAX_SEQUENCE if self.sequence == 0: timestamp = self.wait_next_millis(self.last_timestamp) else: self.sequence = 0 self.last_timestamp = timestamp # 生成ID snowflake_id = ((timestamp - self.TWITTER_EPOCH) << (self.SEQUENCE_BITS + self.WORKER_ID_BITS + self.DATACENTER_ID_BITS)) \ | (self.datacenter_id << (self.SEQUENCE_BITS + self.WORKER_ID_BITS)) \ | (self.worker_id << self.SEQUENCE_BITS) \ | self.sequence return snowflake_id def get_timestamp(self): return int(time.time() * 1000) def wait_next_millis(self, last_timestamp): timestamp = self.get_timestamp() while timestamp <= last_timestamp: timestamp = self.get_timestamp() return timestamp ``` 以上代码定义了一个名为SnowflakeIDGenerator的类,该类通过传入的worker_id和datacenter_id生成唯一的ID。generate_id()方法用于生成新的ID,get_timestamp()方法用于获取当前时间戳,wait_next_millis()方法用于处理时钟回拨情况。 使用该类可以按照以下方式生成ID: ```python generator = SnowflakeIDGenerator(worker_id=1, datacenter_id=1) id = generator.generate_id() print(id) ``` 生成的ID是一个64位的整数,其中高位是一个毫秒级的时间戳,接着是datacenter_id、worker_id和sequence。这样生成的ID可以在分布式系统中保持全局唯一。 ### 回答3: 雪花算法,也被称为Twitter的分布式ID生成算法,旨在生成全局唯一的64位ID。下面是用Python实现雪花算法的类代码示例: ```python import time class Snowflake: def __init__(self, worker_id: int, datacenter_id: int): self.worker_id = worker_id self.datacenter_id = datacenter_id self.sequence = 0 self.last_timestamp = -1 def _gen_timestamp(self): return int(time.time() * 1000) def generate_id(self): timestamp = self._gen_timestamp() if timestamp < self.last_timestamp: raise Exception("Timestamp error") if timestamp == self.last_timestamp: self.sequence = (self.sequence + 1) & 4095 if self.sequence == 0: timestamp = self._wait_next_milliseconds(self.last_timestamp) else: self.sequence = 0 self.last_timestamp = timestamp new_id = ((timestamp - 1562976000000) << 22) | (self.datacenter_id << 17) | (self.worker_id << 12) | self.sequence return new_id def _wait_next_milliseconds(self, last_timestamp): while last_timestamp == self.last_timestamp: time.sleep(0.01) return self._gen_timestamp() ``` 这个Snowflake类具有worker_id(0-31)和datacenter_id(0-31)两个参数,用于初始化生成唯一ID的对象。generate_id()方法用于生成雪花算法的ID,首先获取当前的时间戳,如果时间戳小于上一次的时间戳,则抛出异常,避免时间戳被回拨。如果时间戳相同,则递增序列号,如果序列号达到上限,则等待下一毫秒。最后,根据时间戳、数据中心ID、工作节点ID和序列号生成最终的唯一ID。这个算法最多可以每秒生成4096个ID,由于时间戳占据了高位,所以生成的ID按时间有序。 可以按下面的示例来使用这个Snowflake类: ```python if __name__ == "__main__": snowflake = Snowflake(3, 5) # 初始化一个Snowflake实例,参数分别是worker_id和datacenter_id for i in range(10): new_id = snowflake.generate_id() print(new_id) ``` 这个示例程序使用worker_id为3,datacenter_id为5来生成10个唯一ID,并输出到控制台上。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值