生成唯一序列号 Unique ID

         唯一的序列号Unique ID,在程序的各个方面都有所应用,特别是数据存储方面。很多数据库都需要一个自增的唯一的序列号作为Primary Key。

 

         最简单的Unique ID就是在内存中维护一个long序列,每次取用的时候做i++。这是最简单的UniqueID,在单线程程序中适用。

         随着盘子越做越大,架构和程序会引入各种新的特性,这时候需要重新考虑Unique ID的维护:

         引入多线程,取数的时候就会出现线程竞争,这时候需要通过加锁来保证ID的唯一性,可以使用AtomicLong来解决冲突和一致性的问题。

         引入多进程,原来放在同一个程序内存的Unique ID会变得不适用,只能往外迁,通过一个单独的进程来维护和提供服务。这时候,可以把维护Unique ID的任务交给数据库,数据库的Sequence就是充当这个角色。UniqueID大部分功能都是和数据捆绑在一起的,理所当然地可以把Unique ID的逻辑放到持久层。另外,为了降低数据库的压力,取Sequence可以按步长一次取一个范围。

         引入分布式,同理多进程,当需要在分布式系统中维护Unique ID,需要使用一个单独的服务来提供Unique ID的服务。系统数据的量级也大大提升,单独的数据库一般难以满足数据量的需求,需要做读写分离,分表分库等一系列的优化。这时候,数据库来维护Unique ID不仅需要兼顾本身集群的特性,还要应对大数量的并发请求,难免会造成性能的下降。

         所以,通过一个单独的服务来创建和维护Unique ID(一般称为发号器),是非常有必要的。把Unique ID的逻辑分离出去,也便于数据库系统的伸缩扩展。当然,为了提高发号器的效率,减少网络的延迟,通常会按照不同数据中心不同机房来单独部署。同时,要做好各个发号器间的同步,例如以时间为生成标准,则需要同步时间。

         可是,程序取号的锁竞争和网络延迟是不可避免的,所以最理想的Unique ID策略还是本地生成把发号器内嵌到每个程序中,同时保证生成标准的同步

 

         对于小规模应用,维护UniqueID很简单。接下来主要讨论,分布式系统中Unique ID的维护逻辑。综上所述,处理策略主要分三个方向:

借助第三方:数据库,Redis

单独服务:发号器(Weibo,Twitter的SnowFlake)

本地生成:UUID

         这里主要考虑点:发号器的性能和高可用性。ID的长度,用64bit的一个long来存储是最合适的(UUID的128bit就太长了),符合操作系统的处理,但是长度有限,很难把完全唯一的信息都存进去。


具体的分析过程和策略可以参考《Unique ID的特性需求分析》和《Unique ID策略》。



你可能感兴趣的:(Java相关,读书笔记,杂记)