UUID
UUID(Universally Unique Identifier)是通用唯一识别码,在许多领域用作标识,比如我们常用的数据库也可以用它来作为主键,原理上它是可以对任何东西进行唯一的编码的。

import uuid
name = "Li"
namespace = "Frank"

'''
基于MAC地址,时间戳,随机数来生成唯一的uuid,可以保证全球范围内的唯一性。
'''
print(uuid.uuid1())

'''
算法与uuid1相同,不同的是把时间戳的前4位置换为POSIX的UID。不过需要注意的是
python中没有基于DCE的算法,所以python的uuid模块中没有uuid2这个方法。
'''
print(uuid.uuid2())

'''
通过计算一个命名空间和名字的md5散列值来给出一个uuid,所以可以保证命名空间中的
不同名字具有不同的uuid,但是相同的名字就是相同的uuid了。【感谢评论区大佬指出】
namespace并不是一个自己手动指定的字符串或其他量,而是在uuid模块中本身给出的一些值。
比如uuid.NAMESPACE_DNS,uuid.NAMESPACE_OID,uuid.NAMESPACE_OID这些值。
这些值本身也是UUID对象,根据一定的规则计算得出。
'''
print(uuid.uuid3(uuid.NAMESPACE_DNS,name))

'''
通过伪随机数得到uuid,是有一定概率重复的
'''
print(uuid.uuid4())

'''
和uuid3基本相同,只不过采用的散列算法是sha1
'''
print(uuid.uuid5(uuid.NAMESPACE_DNS,name))

数据库自增ID

默认情况下在数据库每一个表里都有一个名为id的字段,它也被称之为主键ID,它的特点是从1开始,当每新增一条数据,id里的值就会自动加1,因为它的内容是int类型的且数据具有连续性,也适合用来作为索引,看起来我们找到了唯一ID的办法。
自增ID的问题在于以下两点:

  • 当数据量太大,比如客户信息一张表的数据超过上千万条时,我们就面临着要分表存储的问题,在多张表的情况下,如何划分自增ID是一个很麻烦的问题。
  • 安全问题,客户端可以根据自增ID很轻易猜出我们的业务数据,按照顺序遍历就是了。

雪花算法(snowflake)

Snowflake 以 64 bit 来存储组成 ID 的4 个部分:

1、最高位占1 bit,值固定为 0,以保证生成的 ID 为正数;

2、中位占 41 bit,值为毫秒级时间戳;

3、中下位占 10 bit,值为工作机器的 ID,值的上限为 1024;

4、末位占 12 bit,值为当前毫秒内生成的不同 ID,值的上限为 4096

python uuid 生成位数 python生成唯一数字id_python uuid 生成位数

Python 代码:

import time
import logging


# 64位ID的划分
WORKER_ID_BITS = 5
DATACENTER_ID_BITS = 5
SEQUENCE_BITS = 12

# 最大取值计算
MAX_WORKER_ID = -1 ^ (-1 << WORKER_ID_BITS)  # 2**5-1 0b11111
MAX_DATACENTER_ID = -1 ^ (-1 << DATACENTER_ID_BITS)

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

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

# Twitter元年时间戳
TWEPOCH = 1288834974657


logger = logging.getLogger('flask.app')


class IdWorker(object):
    """
    用于生成IDs
    """

    def __init__(self, datacenter_id, worker_id, sequence=0):
        """
        初始化
        :param datacenter_id: 数据中心(机器区域)ID
        :param worker_id: 机器ID
        :param sequence: 其实序号
        """
        # sanity check
        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:int timestamp
        """
        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 until {}'.format(self.last_timestamp))
            raise

        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 << WOKER_ID_SHIFT) | self.sequence
        return new_id

    def _til_next_millis(self, last_timestamp):
        """
        等到下一毫秒
        """
        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())

运行结果是19位的ID

1425714057960366080

总结

UUID

优点: 本机生成,效率高,全局唯一性,通用标准。

缺点:不利于存储,在Mysql的InnoDB引擎下做索引很影响效率,不利于海量数据查询。

数据库自增ID

优点:简单,生成时没有编码成本。

缺点:极度依赖数据库,分表分库或者数据库做主从结构时无法保证唯一性,ID在生成时会出现性能瓶颈。

雪花算法(snowflake)

优点: 不依赖第三方系统,ID全局唯一,数据具有递增的连续性,便于查询。

缺点:依赖系统时钟,如果系统时钟有问题,会导致ID重复(该问题可以通过很多方式避免)