分布式系统数据库主键常用解决方案

分布式系统中不同机器产生的id必须不同。可以使用snowflake保证id唯一。

snowflake原理

算法核心: 把时间戳、工作机器Id、序列号组合在一起。

除了最高位bit标记不可用之外,其余三组bit占位均可浮动,看具体的业务需求而定。默认情况下41bit的时间戳可以支持该算法使用到2089年,10bit的工作机器id可以支持1024台机器,序列号支持1毫秒产生4096个自增序列id。

ps:提供一个github上用java实现的:https://github.com/beyondfengyu/SnowFlake

时间戳

时间戳的粒度是毫秒级,代码如下:

uint64_t generateStamp()
{
    timeval tv;
    gettimeofday(&tv, 0);
    return (uint64_t)tv.tv_sec * 1000 + (uint64_t)tv.tv_usec / 1000;
}

默认情况下有41bit可以供使用,那么一共有T(1L << 41)毫秒供你使用分配,可使用年份=T/(3600*24*365*1000)=69.7年。

其中1L << 41 = 1 00000000 00000000 00000000 00000000 00000000(二级制)=2199023255552(十进制)。

工作机器id

机器级可以使用MAC地址来唯一标识工作机器,工作进程级的话可以使用IP+Path来区分工作进程。如果工作机器较少,可以写到配置文件中配置,但如果机器过多维护配置文件就有点力不从心了。

这里的解决方案需要一个工作id分配的进程,可以自己编写一个简单的进程来记录分配id,或者使用mysql的auto_increment机制。

工作进程与工作id分配器只是在工作进程启动的时候交互一次,然后工作进程可以自行将分配的id数据落文件,下一次启动直接读取文件里的id使用。

ps:工作机器id的10bit可以进一步拆分,根据自己需要拆分即可。比如5个bit标记进程id,5个bit表级线程id etc...

序列号

一些列的自增id(多线程使用atomic),1毫秒内如果需要给多条消息分配id,若同一毫秒序列号4095个用完了(2^12=4096),则“等待至下一毫秒”。

uint64_t waitNextMs(uint64_t lastStamp)
{
    uint64_t cur = 0;
    do {
        cur = generateStamp();
    } while (cur <= lastStamp);
    return cur;
}

在多线程的环境下,序列号使用atomic可以在代码实现上有效减少锁的密度。

参考资料:https://github.com/twitter/snowflake

由于snowflake中最难实践的是工作机器id,原始的snowflake算法需要人工去为每台机器去指定一个机器id,并配置在某个地方从而让snowflake从此处获取机器id。

但是在大厂里,机器是很多的,人力成本太大且容易出错,所以大厂对snowflake进行了改造。

常见的两种snowflake实现。

  • 美团的Leaf

There are no two identical leaves in the world.(世界上没有两片完全相同的树叶。)

一个分布式ID生成框架,支持号段模式(不在本文介绍),支持snowflake模式。

与原始的snowflake算法的不同点在于机器id(workid)的生成,Leaf的workdId是基于ZooKeeper的顺序id生成。每个应用在使用Leaf-snowflake时,在启动时都会都在Zookeeper中生成一个顺序Id,相当于一台机器对应一个顺序节点,也就是一个workId。

附Leaf github地址:https://github.com/Meituan-Dianping/Leaf/blob/master/README_CN.md

  • 百度(uid-generator)

在uid-generator中用户可以自己去定义workId的生成策略,默认提供的策略是:应用启动时由数据库分配。说的简单一点就是:应用在启动时会往数据库表(uid-generator需要新增一个WORKER_NODE表)中去插入一条数据,数据插入成功后返回的该数据对应的自增唯一id就是该机器的workId,而数据由host,port组成。

  • sign(1bit)
    固定1bit符号标识,即生成的UID为正数。

  • delta seconds (28 bits)
    当前时间,相对于时间基点"2016-05-20"的增量值,单位:秒,最多可支持约8.7年

  • worker id (22 bits)
    机器id,最多可支持约420w次机器启动。内置实现为在启动时由数据库分配,默认分配策略为用后即弃,后续可提供复用策略。

  • sequence (13 bits)
    每秒下的并发序列,13 bits可支持每秒8192个并发。

在实现上, UidGenerator通过借用未来时间来解决sequence天然存在的并发限制; 采用RingBuffer来缓存已生成的UID, 并行化UID的生产和消费, 同时对CacheLine补齐,避免了由RingBuffer带来的硬件级「伪共享」问题. 最终单机QPS可达600万。

附uid-genertor github地址:https://github.com/baidu/uid-generator

本文完

勤助手-教育机构好助手。一款专注中小机构教学管理的软件。

网址:https://www.qinzs.com

注册可免费使用。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

myskybeyond

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值