分享

生成唯一ID的四种办法 程序员必备

 曾淼Mark 2019-12-31

生成唯一ID的四种办法 程序员必备

我们在实际编程过程中会经常遇到需要用唯一ID的场合,这些唯一ID还会存到数据库中以便于我们将来进行查询。

例如用户编号、订单编号、客户编号等等,几乎凡是需要用来严格划分用户数据归属性的地方就需要用到唯一ID,否则A的数据到了B那,数据乱了整个系统也就算是毁了。

那么唯一ID该如何有效的生成就变成了一门学问了。

今天我们来讲讲在Python里生成唯一ID的几种方式,包括但不限于以下4种。

1.UUID

UUID应该是大家耳熟能详的一个东西了,它的全称叫 通用唯一识别码(英語:Universally Unique Identifier,缩写:UUID

import uuid uid = uuid.uuid1() print(uid) print(uid.hex)

生成唯一ID的四种办法 程序员必备

通过以上代码我们用Python生成来一个UUID字符串,用的是uuid1方法生成,默认会生成一个带减号(-)的字符串,我们可以通过hex数据拿到不带减号的版本,可以根据实际情况使用。

那么什么是uuid1呢?我们来看看Python关于UUID的源码。

生成唯一ID的四种办法 程序员必备

篇幅有限我就不贴所有源码了,不过我们可以从源码注释里得知,uuid1这个方法主要是根据当前机器ID,一个随机序列号和时间戳生成的一个随机字符串。

如果是简单的应用,数据量也不大的情况,是可以用UUID来作为唯一编号的,不过在大型系统里,UUID其实不太适合作为主要数据的唯一ID,特别是用户ID,订单ID等等。

原因是我们在数据库里通常会对这些数据字段做了索引以便于系统进行频繁的查询,而在很大的数据体量里,一串32位长度的随机字符串作为索引就是一个灾难,我们一般不建议在非线性数据的字段上做索引,这样效率会非常低。

至于为什么字符串不适合做数据库索引我们以后再讲,在这里只需要知道UUID不适合做需要频繁查询的数据唯一ID就是了。

2:数据库主键自增ID

前面说到UUID因为索引的问题不适合在海量数据里作为主要数据的唯一ID,那么数据库自带的主键ID是否能作为唯一ID使用呢?

什么是自增ID呢?它长成下面这样。

生成唯一ID的四种办法 程序员必备

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

但是自增ID的问题在于以下两点:

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

综上所述,自增ID虽然能用,但也不是一个特别好的办法。

3: mongodb的ObjectId


相信使用过mongodb的朋友们很清楚,它的文档默认的key其实也是一个uuid,所以我们也可以利用mongodb的ObjectId来产生一个UUID。

在python里直接使用一个叫bson的第三方包即可,BSON是一种计算机数据交换格式,主要被用作MongoDB数据库中的数据存储和网络传输格式。


主要代码如下:

import bson
demoid = bson.ObjectId()
print(demoid)

生成唯一ID的四种办法 程序员必备

不过mongodb的ObjectId和uuid一样,还是存在相同的问题,这里仅作参考。


4: 雪花算法 Snowflake。

以上三种办法我们都试过了,或多或少都有一些缺陷,那么有没有更好的办法呢?既可以兼顾查询效率,也可以兼顾数据的唯一性。

让我们来把目光瞄向很多大厂,因为它们的服务每天都会产生海量的数据,那么我们看看它们是如何做的。

twitter(推特)前些年把自己的唯一ID生成算法开源了,也叫做雪花算法,取自(世界上没有一片相同的雪花)。

先看看雪花算法的基本原理。

生成唯一ID的四种办法 程序员必备

snowflake算法核心思想上是用64位的二进制来表示数据。

其中41位的时间戳表示:当前时间戳减去某个设定的起始时间。

10位标识表示:不同的机器、数据库的标识ID等等,序列号为每秒或每毫秒内自增的id。

因为雪花算法最后生成出来的其实是一串唯一的数字,而非字符串,适合做数据库索引,看起来非常满足我们的需求,下面就来看看雪花算法在Python中的实现吧。

核心代码如下:

def __init__(self, worker_id=0, data_center_id=0): self.worker_id = worker_id self.data_center_id = data_center_id self.user_agent_parser = re.compile('^[a-zA-Z][a-zA-Z\-0-9]*$') self.logger = logging.getLogger('idworker') # stats self.ids_generated = 0 # 2019.08.08 08:08:08 timestamp self.twepoch = 1565222888000 self.sequence = 0 self.worker_id_bits = 5 self.data_center_id_bits = 5 self.max_worker_id = -1 ^ (-1 << self.worker_id_bits) self.max_data_center_id = -1 ^ (-1 << self.data_center_id_bits) self.sequence_bits = 12 self.worker_id_shift = self.sequence_bits self.data_center_id_shift = self.sequence_bits self.worker_id_bits self.timestamp_left_shift = self.sequence_bits self.worker_id_bits self.data_center_id_bits self.sequence_mask = -1 ^ (-1 << self.sequence_bits) self.last_timestamp = -1 def _time_gen(self): return int(time.time() * 1000) def _till_next_millis(self, last_timestamp): timestamp = self._time_gen() while last_timestamp <= timestamp: timestamp = self._time_gen() return timestamp def _next_id(self): timestamp = self._time_gen() if self.last_timestamp > timestamp: self.logger.warning('clock is moving backwards. Rejecting request until %i' % self.last_timestamp) raise InvalidSystemClock( 'Clock moved backwards. Refusing to generate id for %i milliseocnds' % self.last_timestamp) if self.last_timestamp == timestamp: self.sequence = (self.sequence 1) & self.sequence_mask if self.sequence == 0: timestamp = self._till_next_millis(self.last_timestamp) else: self.sequence = 0 self.last_timestamp = timestamp new_id = ((timestamp - self.twepoch) << self.timestamp_left_shift) | ( self.data_center_id << self.data_center_id_shift) | (self.worker_id << self.worker_id_shift) | self.sequence self.ids_generated = 1 return new_id


最后我们来用雪花算法生成几个唯一ID试试。

生成唯一ID的四种办法 程序员必备

通过以上例子我们可以发现,雪花算法第一是生成的数字类型的数据,第二数字之间是具有一定的连续性的,这样的优点在查询上非常具有效率。

以上介绍的4种生成唯一ID的方式各有千秋,在不同的应用场景下会发挥不同的用处,事实上在分布式系统里,还有很多各种各样的解决方案,在这里就不一一提供了。

我们先来总结以上四种方式的优缺点吧。

总结

  • UUID/ObjectID
    • 优点: 本机生成,效率高,全局唯一性,通用标准。
    • 缺点:不利于存储,在Mysql的InnoDB引擎下做索引很影响效率,不利于海量数据查询。
  • 数据库自增ID
    • 优点:简单,生成时没有编码成本。
    • 缺点:极度依赖数据库,分表分库或者数据库做主从结构时无法保证唯一性,ID在生成时会出现性能瓶颈。
  • 雪花算法(snowflake)
    • 优点: 不依赖第三方系统,ID全局唯一,数据具有递增的连续性,便于查询。
    • 缺点:依赖系统时钟,如果系统时钟有问题,会导致ID重复(该问题可以通过很多方式避免)

关于如何生成唯一ID我们就介绍到这,在设计系统时根据实际情况选择最适合我们的方案,从个人的角度出发,有限推荐雪花算法的方案,理论上来说,它在一秒钟可以生成400多万个唯一ID,也就是说我们的业务数据并发量没有达到每秒400万次的情况下,这个方案都是安全的。

还有更多的唯一ID生成方式,欢迎大家回复,便于交流。

因篇幅有限,关于Python的雪花算法实现代码请私信我获取。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多