分布式序列生成器

本文介绍了数据库序列的概念、作用及数据库对其的支持,特别是在分布式数据库环境下,由于数据分片,传统数据库序列不再适用。文章详细讨论了分布式序列的实现,如UUID、Twitter的Snowflake、MongoDB ObjectID以及Ticket Server方案,并阐述了序列生成器的重要性和设计思想,强调其在高并发下保证唯一性和效率的需求。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、序列:

1) 数据库序列的定义:

序列(Sequence)其实是序列号的生成器,可以为表中的行自动生成序列号,产生一组等间隔的数值(类型为数字)。

 

2) 数据库序列的作用:

其主要的用途是生成表的主键值,用来在多用户环境下产生唯一整数的数据库对象。可以在插入语句中引用,也可以通过查询检查当前值,或使序列增至下一个值,并能协调多行或者多表的主键操作。


3) 数据库对序列的支持:

1.   Oracle是支持使用序列(Sequence)来处理主键字段,可以设置起始值,步长,最大值,是否循环,缓存大小。确保主键唯一,非常方便管理。

2.   MySQL是不支持序列(Sequence)的,MySQL提供了数据库字段自增长( auto increament)的功能来实现类似的序列的功能。但在实际使用过程中会发现,MySQL的自增长       (Increment)有诸多的弊端:不能控制步长、不能设置开始索引和是否循环等;若需要迁移 数据库,则对于主键这部分存在很大的问题。


二、分布式序列:

1)  传统数据库模式:

传统的架构模式就是 应用连接数据库直接对数据进行访问,这种架构特点就是简单方便。

但是随着目前数据量不断的增大我们就遇到了问题:

a)   单个表数据量太大

b)   单个库数据量太大

c)   单台数据量服务器压力很大

d)   读写速度遇到瓶颈

当面临以上问题时,我们会想到的第一种解决方式就是向上扩展(scale up)简单来说就是不断增加硬件性能。这种方式只能暂时解决问题,当业务量不断增长时还是解决不了问题。

此时我们不得不依赖于第二种方式:水平扩展,直接增加机器,把数据库放到不同服务器上,在应用到数据库之间加一个proxy进行路由,这样就可以解决上面的问题了。

 

2)  分布式数据库:

分布式数据库顾名思义,具有分布数据的功能,实际上它的数据存储方式是物理上是分布的,逻辑上是集中的。也就是说分布式数据就是指数据和程序可以不位于一个服务器上,而是分散到多个服务器,这些服务器由通讯网络联接在一起,每个服务器都是一个独立的数据库系统,它们都拥有各自的数据库、中央处理机、终端,以及各自的局部数据库管理系统。

因此分布式数据库系统可以看作是一系列集中式数据库系统的联合。它们在逻辑上属于同一系统,但在物理结构上是分布式的。

 

3)  分布式序列

一旦数据库被切分到多个物理结点上,我们将不能再依赖数据库自身的主键生成机制。一方面,某个分区数据库自生成的ID无法保证在全局上是唯一的;另一方面,应用程序在插入数据之前需要先获得ID,以便进行SQL路由。

说明:

假如数据表有1亿条数据,而且还在不断的增加,这里我们就需要考虑到分表分库,假设我们采用Hash或者是用户取模求余的方法将这个表拆分成10个表,每个表的结构相同,其中有一个主键id,那么10个表中的id需要唯一不同,在单表的时候,使用数据表自增长是没有问题的。当分成10个表后,就无法用到数据库自增长了。


三、主流序列生成方式

1)  UUID

UUID的目的,是让分散式系统中的所有元素,都能有唯一的辨识信息,而不需要通过中央控制端来做辨识信息的指定。如此一来,每个人都可以创建不与其它人冲突的UUID。在这样的情况下,就不需考虑数据库创建时的名称重复问题。

一组UUID,是由一串16位组(亦称128位)的16进位数字所构成,是故UUID理论上的总数为216 x 8=2128,约等于3.4 x 1038。也就是说若每纳秒产生1兆个UUID,要花100亿年才会将所有UUID用完。

UUID的标准型式包含32个16进位数字,以连字号分为五段,形式为8-4-4-4-12的36个字符。示例:

550e8400-e29b-41d4-a716-446655440000

 

2)  twitter的Snowflake(id分发中心)

Snowflake是twitter开源的一款独立的适用于分布式环境的ID生成服务器。生成的ID是64Bits,同时满足高性能(>10K ids/s),低延迟(<2ms)和高可用。与MongoDB ObjectID类似这里生成的ID也是时间上有序的。编码方式也和ObjectID类似,如下:

0           41     51     64
+-----------+------+------+
|time       |pc    |inc   |
+-----------+------+------+

·        前41bits是以微秒为单位的timestamp。

·        接着10bits是事先配置好的机器ID。

·        最后12bits是累加计数器。

 

3)  MongoDB ObjectID(类似UUID的方式)

MongoDB中每一条记录都有一个’id’字段用来唯一标示本记录。如果用户插入数据时没有显示提供’id’字段,那么系统会自动生成一个。ObjectID一共12Bytes,设计的时候充分考虑了分布式环境下使用的情况,所以能保证在一个分布式MongoDB集群中唯一。ObjectID格式如下:

0        4      7    9      12
+--------+------+----+------+
|time    |pc    |pid |inc   |
+--------+------+----+------+

·        前四个字节是UnixTimestamp。

·        接着三个字节是当前机器“hostname/mac地址/虚拟编号”其中之一的MD5结果的前3个字节。

·        接着两个字节是当前进程的PID。

·        最后三个字节是累加计数器或是一个随机数(只有当不支持累加计数器时才用随机数)。

最后生成的仍然是一个用16进制表示的串,如47cc67093475061e3d95369d。这里MongoDB的ObjectID相对UUID有个很大的优点就是ObjectID是时间上有序的。另外还有ObjectID本身也包含了很多其它有用的信息,通过直接解码ObjectID即可直接获得这些信息。

 

4)  Ticket Server(数据库生存方式)

这个是Flickr在遇到生成全局ID问题时采用的办法。利用了数据库中auto_increment的特性和MySQL特有的REPLACEINFO命令,专门一个数据库实例用来产生ID。大致的过程是这样的:

首先建立一个表,比如用来产生64bitsID的,叫做’Ticket64′

CREATE TABLE `Tickets64` (
  `id` bigint(20) unsigned NOT NULL auto_increment,
  `stub` char(1) NOT NULL default '',
  PRIMARY KEY  (`id`),
  UNIQUE KEY `stub` (`stub`)
) ENGINE=MyISAM

向里边插入一条记录后大致是这样:

+-------------------+------+
| id                | stub |
+-------------------+------+
| 72157623227190423 |    a |
+-------------------+------+

当需要一个64BitsID的时候,执行如下SQL 语句:

REPLACE INTO Tickets64 (stub) VALUES ('a');
SELECT LAST_INSERT_ID();

另外为了防止这个TicketServer单点故障,可以设置两个TicketServer实例。其中一个产生奇数ID,另一个产生偶数ID。

TicketServer1:
auto-increment-increment = 2
auto-increment-offset = 1
 
TicketServer2:
auto-increment-increment = 2
auto-increment-offset = 2
应用交替请求两个Server,这样不仅压力减小一半,故障风险也降低一半。不过这里也有个问题,就是当一台机器故障时,另一台正常机器产生的ID将会领先故障机器一截,可能会造成不再是时间上有序的ID。按照Flickr的说法,这并不影响他们的应用。


四、 序列生成器的作用:

我们可以把所有序列集中放在一个地方进行管理,对每个序列独立管理,每台机器使用序列时都从这个序列生成器上获得,这样可以解决了上面提到的MySQL不支持序列的问题,还把模块的功能划分的很清楚。

这样任何的程序创建序列和获取序列,都可以使用这个序列生成器来操作。这个序列生成器不光要为系统提供序列,还要考虑生成器的性能问题,生成器的稳定性问题,容灾性问题等。


五、序列生成器思想

序列生成器应具备以下功能:

1. 生成器接口要支持高并发访问,在高并发访问的情况下,仍然可以保证每个应用获取到的序列是唯一不重复的。

2. 生成器要存在缓存机制,应用请求序列的时候,优先从缓存中获取序列,以保证程序大量访问,仍然不影响效率。

3. 生成器要支持序列持久化,在创建序列和缓存机制不存在系列的情况下,要将序列持久化到数据库中,确保序列的信息不能丢失。

4. 序列生成器应采用一主一备,起到容灾作用。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值