前言

本篇主要介绍高并发算法Snowflake是怎么应用到实战项目中的。

对于怎么理解Snowflake算法,大家可以从网上搜索‘Snowflake’,大量资源可供查看,这里就不一一详诉,这里主要介绍怎么实战应用。

对于不理解的,可以看看这篇文章  Twitter-Snowflake,64位自增ID算法详解

为什么有Snowflake算法的出现呢?

首先它是Twitter提出来的。

前世今生

以前我们可以用UUID作为唯一标识,但是UUID是无序的,又是英文、数字、横杆的结合。当我们要生成有序的id并且按时间排序时,UUID必然不是最好的选择。

当我们需要有序的id时,可以用数据库的自增长id,但是在当今高并发系统时代下,自增长id速度太慢,满足不了需求。然而,对于有‘有序的id按时间排序’这一需求时,Twitter提出了它的算法,并且用于Twitter中。

需要注意的地方

可达并发量根据不同的配置不同,每秒上万并发量不成问题。

id可用时间:69年

使用限制

使用Snowflake其实有个限制,就是必须知道运行中是哪台机器。比如我们用Azure云,配置了10个实例(机器),要知道这10个机器是哪一台。

开始用Snowflake

首先,直接贴Snowflake算法代码,算法怎么实现就不具体说:(C#版,java版的代码也一样实现)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace ConsoleApp6
{
/// <summary>
/// From: https://github.com/twitter/snowflake
/// An object that generates IDs.
/// This is broken into a separate class in case
/// we ever want to support multiple worker threads
/// per process
/// </summary>
public class IdWorker
{
private long workerId;
private long datacenterId;
private long sequence = 0L; private static long twepoch = 1288834974657L; /// <summary>
/// 机器标识位数
/// </summary>
private static long workerIdBits = 5L; /// <summary>
/// //数据中心标识位数
/// </summary>
private static long datacenterIdBits = 5L; /// <summary>
/// //机器ID最大值
/// </summary>
private static long maxWorkerId = -1L ^ (-1L << (int)workerIdBits); /// <summary>
/// //数据中心ID最大值
/// </summary>
private static long maxDatacenterId = -1L ^ (-1L << (int)datacenterIdBits); /// <summary>
/// //毫秒内自增位
/// </summary>
private static long sequenceBits = 12L; /// <summary>
/// //机器ID偏左移12位
/// </summary>
private long workerIdShift = sequenceBits; /// <summary>
/// //数据中心ID左移17位
/// </summary>
private long datacenterIdShift = sequenceBits + workerIdBits; /// <summary>
/// //时间毫秒左移22位
/// </summary>
private long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
private long sequenceMask = -1L ^ (-1L << (int)sequenceBits); private long lastTimestamp = -1L; private static object syncRoot = new object(); /// <summary>
///
/// </summary>
/// <param name="workerId">机器id,哪台机器。最大31</param>
/// <param name="datacenterId">数据中心id,哪个数据库,最大31</param>
public IdWorker(long workerId, long datacenterId)
{ // sanity check for workerId
if (workerId > maxWorkerId || workerId < )
{
throw new ArgumentException(string.Format("worker Id can't be greater than %d or less than 0", maxWorkerId));
}
if (datacenterId > maxDatacenterId || datacenterId < )
{
throw new ArgumentException(string.Format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
}
this.workerId = workerId;
this.datacenterId = datacenterId;
} public long nextId()
{
lock (syncRoot)
{
long timestamp = timeGen(); if (timestamp < lastTimestamp)
{
throw new ApplicationException(string.Format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
} if (lastTimestamp == timestamp)
{
sequence = (sequence + ) & sequenceMask;
if (sequence == )
{
timestamp = tilNextMillis(lastTimestamp);
}
}
else
{
sequence = 0L;
} lastTimestamp = timestamp; return ((timestamp - twepoch) << (int)timestampLeftShift) | (datacenterId << (int)datacenterIdShift) | (workerId << (int)workerIdShift) | sequence;
}
} protected long tilNextMillis(long lastTimestamp)
{
long timestamp = timeGen();
while (timestamp <= lastTimestamp)
{
timestamp = timeGen();
}
return timestamp;
} protected long timeGen()
{
return (long)(DateTime.UtcNow - new DateTime(, , , , , , DateTimeKind.Utc)).TotalMilliseconds;
}
}
}

怎么用呢?

直接用

 IdWorker idWorker = new IdWorker(, );
long id = idWorker.nextId();

说明

workerId是机器id,表示分布式环境下的那台机器。datacenterId是数据库中心,表示哪个数据库中心。这里的机器id与数据库中心id最大是31。

我们看到nextId方法里面是用锁来生成id的。

然而我们怎么真正地应用到我们实际的项目中呢?

Snowflake运用到项目中

例如,我们分布式有三台机器,1个数据库。

那么workerId分别在机器A/B/C中的值为1/2/3,datacenterId都为0。

这个配置好了之后,那么我们怎么在代码里面编写呢?

比如,对于一个web应用,我们都知道,在客户端请求时,服务器都会生成一个Controller,那么怎么保证IdWorker实例只能在一台服务器中存在一个呢?

答案大家都知道,是静态属性(当然也可以单例)。下面我们用控制台程序来模仿一下controller的请求,当10个线程请求时会发生什么情况。

模仿的Controller如下:

    class TestIdWorkerController
{
private static readonly IdWorker _idWorker = new IdWorker(, ); public void GenerateId(HashSet<long> set)
{ int i = ;
while (true)
{
if (i++ == )
break; long id = _idWorker.nextId();
lock (set)
{
if (!set.Add(id))
Console.WriteLine($"same id={id}");
} }
} }

我们看到,id会生成1000000个,并且如果有相同的时候打印出来相同的id。(这里为什么用锁来锁住HashSet,因为HashSet线程不是安全的,所以要用锁)

下面我在主程序中,开启10个线程,分别来new一次TestIdWorkerController,new一次Thread。

        static void Main(string[] args)
{ //存放id的集合
HashSet<long> set = new HashSet<long>(); //启动10个线程
for (int i = ; i < ; i++)
{
TestIdWorkerController testIdWorker = new TestIdWorkerController();
Thread thread = new Thread(() => testIdWorker.GenerateId(set));
thread.Start();
} //每秒钟打印当前生成的状态
while (true)
{
Console.WriteLine($"set.count={set.Count}");
Thread.Sleep( * );
} }

我们看到,每秒打印输出的集合,如何输出的集合数量=1000000(id数)*10(线程数),也侧面验证了没有重复。

从上图看出,执行完毕,并且没打印same,结果也为1000000(id数)*10(线程数)。所以尽情的所用吧。

可以关注本人的公众号,多年经验的原创文章共享给大家。

高并发之 - 全局有序唯一id Snowflake 应用实战的更多相关文章

  1. 高并发分布式系统如何做到唯一Id

    又一个多月没冒泡了,其实最近学了些东西,但是没有安排时间整理成博文,后续再奉上.最近还写了一个发邮件的组件以及性能测试请看 <NET开发邮件发送功能的全面教程(含邮件组件源码)> ,还弄了 ...

  2. PHP uniqid 高并发生成不重复唯一ID

    http://www.51-n.com/t-4264-1-1.html PHP uniqid()函数可用于生成不重复的唯一标识符,该函数基于微秒级当前时间戳.在高并发或者间隔时长极短(如循环代码)的情 ...

  3. 游戏服务器生成全局唯一ID的几种方法

    在服务器系统开发时,为了适应数据大并发的请求,我们往往需要对数据进行异步存储,特别是在做分布式系统时,这个时候就不能等待插入数据库返回了取自动id了,而是需要在插入数据库之前生成一个全局的唯一id,使 ...

  4. 根据twitter的snowflake算法生成唯一ID

    C#版本 /// <summary> /// 根据twitter的snowflake算法生成唯一ID /// snowflake算法 64 位 /// 0---0000000000 000 ...

  5. C# 根据twitter的snowflake算法生成唯一ID

    C# 版算法: using System; using System.Collections.Generic; using System.Linq; using System.Text; using ...

  6. 分布式系统的唯一id生成算法你了解吗?

    在分库分表之后你必然要面对的一个问题,就是id咋生成? 因为要是一个表分成多个表之后,每个表的id都是从1开始累加自增长,那肯定不对啊. 举个例子,你的订单表拆分为了1024张订单表,每个表的id都从 ...

  7. 高并发分布式系统中生成全局唯一Id汇总

    数据在分片时,典型的是分库分表,就有一个全局ID生成的问题.单纯的生成全局ID并不是什么难题,但是生成的ID通常要满足分片的一些要求:   1 不能有单点故障.   2 以时间为序,或者ID里包含时间 ...

  8. 如何在高并发分布式系统中生成全局唯一Id

    月整理出来,有兴趣的园友可以关注下我的博客. 分享原由,最近公司用到,并且在找最合适的方案,希望大家多参与讨论和提出新方案.我和我的小伙伴们也讨论了这个主题,我受益匪浅啊…… 博文示例: 1.     ...

  9. 高并发分布式环境中获取全局唯一ID[分布式数据库全局唯一主键生成]

    需求说明 在过去单机系统中,生成唯一ID比较简单,可以使用MySQL的自增主键或者Oracle中的sequence, 在现在的大型高并发分布式系统中,以上策略就会有问题了,因为不同的数据库会部署到不同 ...

随机推荐

  1. 从头文件中学习sfr和sbit

    1.reg52.h 头文件,它定义了单片机的一些端口物理地址. #ifndef __REG52_H__ #define __REG52_H__ /* BYTE Registers */ sfr P0 ...

  2. Delphi实现拍照控件的程序代码

    完整的delphi拍照控件代码,实现利用摄像头进行拍照的功能.需要TVideoCap控件支持. procedure Tfrm1.Button2Click(Sender: TObject); Var j ...

  3. 如何实现php手机短信验证功能

    http://www.qdexun.cn/jsp/news/shownews.do?method=GetqtnewsdetailAction&id=1677 下载php源代码 现在网站在建设网 ...

  4. .NET高级代码审计(第五课) .NET Remoting反序列化漏洞

    0x00 前言 最近几天国外安全研究员Soroush Dalili (@irsdl)公布了.NET Remoting应用程序可能存在反序列化安全风险,当服务端使用HTTP信道中的SoapServerF ...

  5. .NetCore 发布到 Centos docker

    [搭建环境] 系统:CentOS 7.0    容器:Docker 1.13.1    .Net Core 版本:.NET Core 2.2     工具:putty.filezilla 等. 一.安 ...

  6. Http/Https抓包工具Charles最新版破解教程(Windows|Mac)

    Charles介绍 Charles是一款强大的http/https抓包工具,可以抓取各种数据请求,查看请求的头信息,请求信息,返回信息等.本文主要介绍Charles的破解过程,包括Windows平台和 ...

  7. adb shell pm list packages的用法

    abd shell pm list packages     ####查看当前连接设备或者虚拟机的所有包 adb shell pm list packages -d    #####只输出禁用的包. ...

  8. [学习笔记]树形dp

    最近几天学了一下树形\(dp\) 其实早就学过了 来提高一下打开树形\(dp\)的姿势. 1.没有上司的晚会 我的人生第一道树形\(dp\),其实就是两种情况: \(dp[i][1]\)表示第i个人来 ...

  9. 控制台API函数----HANDLE、SetConsoleCursorPosition、SetConsoleTextAttribute

    控制台API函数 调用相关文本界面控制的API函数,这些函数可分为三类. 一.用于控制台窗口控制的函数(包括窗口的缓冲区大小.窗口前景字符和背景颜色.窗口标题.大小和位置等): 二.用于控制台输入输出 ...

  10. nginx官方文档 之 http负载均衡 学习笔记

    一.负载均衡 算法 大致可以分两类: (1)不能保证用户的每一次请求都通过负载均衡到达同一服务器. (2)可保证用户的每一次请求都通过负载均衡到达同一服务器. 第二类的应用场景: 1.如果服务器有缓存 ...