UUID和雪花(Snowflake)算法该如何选择?

news2024/11/16 13:06:04

在这里插入图片描述

  • 博主简介:不写代码没饭吃,一名全栈领域的创作者,专注于研究互联网产品的解决方案和技术。熟悉云原生、微服务架构,分享一些项目实战经验以及前沿技术的见解。
  • 关注我们的主页,探索全栈开发,期待与您一起在移动开发的世界中,不断进步和创造!
  • 本文收录于 不写代码没饭吃 的学习汇报系列,大家有兴趣的可以看一看。
  • 欢迎访问我们的微信公众号:不写代码没饭吃,获取更多精彩内容、实用技巧、行业资讯等。您关注的是我们前进的动力!

UUID和雪花(Snowflake)算法该如何选择?

UUIDSnowflake 都可以生成唯一标识,在分布式系统中可以说是必备利器,那么我们该如何对不同的场景进行不同算法的选择呢,UUID 简单无序十分适合生成 requestIDSnowflake 里面包含时间序列等,可以用于排序,效率都还可以,本文详细介绍了我们选择的使用不同算法的原因,两种算法不同维度的对比。

数据库的主键要如何选择?

数据库中的每一条记录都需要有一个唯一的标识,依据数据库的第二范式,数据库中每一个表中都需要有一个唯一的主键,其他数据元素和主键一一对应。
那么关于主键的选择就成为一个关键点了,一般来讲,你有两种选择方式:

  1. 使用业务字段作为主键,比如说对于用户表来说,可以使用手机号,email 或者身份证号作为主键。
  2. 使用生成的唯一 ID 作为主键。

不过对于大部分场景来说,第一种选择并不适用,比如像评论表你就很难找到一个业务字段作为主键,因为在评论表中,你很难找到一个字段唯一标识一条评论。而对于用户表来说,我们需要考虑的是作为主键的业务字段是否能够唯一标识一个人,一个人可以有多个 email 和手机号,一旦出现变更 email 或者手机号的情况,就需要变更所有引用的外键信息,所以使用 email 或者手机作为主键是不合适的。

身份证号码确实是用户的唯一标识,但是由于它的隐私属性,并不是一个用户系统的必须属性,你想想,你的系统如果没有要求做实名认证,那么肯定不会要求用户填写身份证号码的。并且已有的身份证号码是会变更的,比如在 1999 年时身份证号码就从 15 位变更为 18 位,但是主键一旦变更,以这个主键为外键的表也都要随之变更,这个工作量是巨大的。

因此,我更倾向于使用生成的ID作为数据库的主键。不单单是因为它的唯一性,更是因为一旦生成就不会变更,可以随意引用。

在单库单表的场景下,我们可以使用数据库的自增字段作为 ID,因为这样最简单,对于开发人员来说也是透明的。但是当数据库分库分表后,使用自增字段就无法保证 ID 的全局唯一性了。

想象一下,当我们分库分表之后,同一个逻辑表的数据被分布到多个库中,这时如果使用数据库自增字段作为主键,那么只能保证在这个库中是唯一的,无法保证全局的唯一性。那么假如你来设计用户系统的时候,使用自增 ID 作为用户 ID,就可能出现两个用户有两个相同的 ID,这是不可接受的,那么你要怎么做呢?我建议你搭建发号器服务来生成全局唯一的 ID

UUID与Snowflake对比

从我历年所经历的项目中,我主要使用的是变种的 Snowflake 算法来生成业务需要的 ID 的,本讲的重点,也是运用它去解决 ID 全局唯一性的问题。搞懂这个算法,知道它是怎么实现的,就足够你应用它来设计一套分布式发号器了,不过你可能会说了:“那你提全局唯一性,怎么不提 UUID 呢?”

没错,UUID(Universally Unique Identifier,通用唯一标识码)不依赖于任何第三方系统,所以在性能和可用性上都比较好,我一般会使用它生成 Request ID 来标记单次请求,但是如果用它来作为数据库主键,它会存在以下几点问题。

排序

首先,生成的 ID 做好具有单调递增性,也就是有序的,而 UUID 不具备这个特点。为什么 ID 要是有序的呢?因为在系统设计时,ID 有可能成为排序的字段。我给你举个例子。

比如,你要实现一套评论的系统时,你一般会设计两个表,一张评论表,存储评论的详细信息,其中有 ID 字段,有评论的内容,还有评论人 ID,被评论内容的 ID 等等,以 ID 字段作为分区键;另一个是评论列表,存储着内容 ID 和评论 ID 的对应关系,以内容 ID 为分区键。

我们在获取内容的评论列表时,需要按照时间序倒序排列,因为 ID 是时间上有序的,所以我们就可以按照评论 ID 的倒序排列。而如果评论 ID 不是在时间上有序的话,我们就需要在评论列表中再存储一个多余的创建时间的列用作排序,假设内容 ID、评论 ID 和时间都是使用 8 字节存储,我们就要多出 50% 的存储空间存储时间字段,造成了存储空间上的浪费。

有序ID会提升数据的写入性能

我们知道 MySQL InnoDB 存储引擎使用 B+ 树存储索引数据,而主键也是一种索引。索引数据在 B+ 树中是有序排列的,就像下面这张图一样,图中 2,10,26 都是记录的 ID,也是索引数据。

20201124094634

这时,当插入的下一条记录的 ID 是递增的时候,比如插入 30 时,数据库只需要把它追加到后面就好了。但是如果插入的数据是无序的,比如 ID13,那么数据库就要查找 13 应该插入的位置,再挪动 13 后面的数据,这就造成了多余的数据移动的开销。

20201124094654

我们知道机械磁盘在完成随机的写时,需要先做 “寻道” 找到要写入的位置,也就是让磁头找到对应的磁道,这个过程是非常耗时的。而顺序写就不需要寻道,会大大提升索引的写入性能。

UUID不具备业务含义

其实现实世界中使用的 ID 中都包含有一些有意义的数据,这些数据会出现在 ID 的固定的位置上。比如说我们使用的身份证的前六位是地区编号;7~14 位是身份证持有人的生日;不同城市电话号码的区号是不同的;你从手机号码的的前三位就可以看出这个手机号隶属于哪一个运营商。而如果生成的 ID 可以被反解,那么从反解出来的信息中我们可以对 ID 来做验证,我们可以从中知道这个 ID 的生成时间,从哪个机房的发号器中生成的,为哪个业务服务的,对于问题的排查有一定的帮助。

最后,UUID 是由 3216 进制数字组成的字符串,如果作为数据库主键使用比较耗费空间。

你能看到,UUID 方案有很大的局限性,也是我不建议你用它的原因,而 twitter 提出的 Snowflake 算法完全可以弥补 UUID 存在的不足,因为它不仅算法简单易实现,也满足 ID 所需要的全局唯一性,单调递增性,还包含一定的业务上的意义。

Snowflake 的核心思想是将 64bit 的二进制数字分成若干部分,每一部分都存储有特定含义的数据,比如说时间戳、机器 ID、序列号等等,最终生成全局唯一的有序 ID。它的标准算法是这样的:

20201124094828

从上面这张图中我们可以看到,41 位的时间戳大概可以支撑 pow(2,41)/1000/60/60/24/365 年,约等于 69 年,对于一个系统是足够了。

如果你的系统部署在多个机房,那么 10 位的机器 ID 可以继续划分为 2~3 位的 IDC 标示(可以支撑 4 个或者 8IDC 机房)和 7~8 位的机器 ID(支持 128-256 台机器);12 位的序列号代表着每个节点每毫秒最多可以生成 4096ID

不同公司也会依据自身业务的特点对 Snowflake 算法做一些改造,比如说减少序列号的位数增加机器 ID 的位数以支持单 IDC 更多的机器,也可以在其中加入业务 ID 字段来区分不同的业务。比方说我现在使用的发号器的组成规则就是:1 位兼容位恒为 0 + 41 位时间信息 + 6IDC 信息(支持 64IDC)+ 6 位业务信息(支持 64 个业务)+ 10 位自增信息(每毫秒支持 1024 个号)

我选择这个组成规则,主要是因为我在单机房只部署一个发号器的节点,并且使用 KeepAlive 保证可用性。业务信息指的是项目中哪个业务模块使用,比如用户模块生成的 ID,内容模块生成的 ID,把它加入进来,一是希望不同业务发出来的 ID 可以不同,二是因为在出现问题时可以反解 ID,知道是哪一个业务发出来的 ID

实现方式

那么了解了 Snowflake 算法的原理之后,我们如何把它工程化,来为业务生成全局唯一的 ID 呢?

嵌入到业务代码里,也就是分布在业务服务器中

这种方案的好处是业务代码在使用的时候不需要跨网络调用,性能上会好一些,但是就需要更多的机器 ID 位数来支持更多的业务服务器。另外,由于业务服务器的数量很多,我们很难保证机器 ID 的唯一性,所以就需要引入 ZooKeeper 等分布式一致性组件来保证每次机器重启时都能获得唯一的机器 ID

作为独立的发号器服务部署

业务在使用发号器的时候就需要多一次的网络调用,但是内网的调用对于性能的损耗有限,却可以减少机器 ID 的位数,如果发号器以主备方式部署,同时运行的只有一个发号器,那么机器 ID 可以省略,这样可以留更多的位数给最后的自增信息位。即使需要机器 ID,因为发号器部署实例数有限,那么就可以把机器 ID 写在发号器的配置文件里,这样即可以保证机器 ID 唯一性,也无需引入第三方组件了。微博和美图都是使用独立服务的方式来部署发号器的,性能上单实例单 CPU 可以达到两万每秒。

Snowflake 算法设计的非常简单且巧妙,性能上也足够高效,同时也能够生成具有全局唯一性、单调递增性和有业务含义的 ID,但是它也有一些缺点,其中最大的缺点就是它依赖于系统的时间戳,一旦系统时间不准,就有可能生成重复的 ID。所以如果我们发现系统时钟不准,就可以让发号器暂时拒绝发号,直到时钟准确为止。

另外,如果请求发号器的 QPS 不高,比如说发号器每毫秒只发一个 ID,就会造成生成 ID 的末位永远是 1,那么在分库分表时如果使用 ID 作为分区键就会造成库表分配的不均匀。这一点,也是我在实际项目中踩过的坑,而解决办法主要有两个:

  1. 时间戳不记录毫秒而是记录秒,这样在一个时间区间里可以多发出几个号,避免出现分库分表时数据分配不均。
  2. 生成的序列号的起始号可以做一下随机,这一秒是 21,下一秒是 30,这样就会尽量的均衡了。

我在开头提到,自己的实际项目中采用的是变种的 Snowflake 算法,也就是说对 Snowflake 算法进行了一定的改造,从上面的内容中你可以看出,这些改造:一是要让算法中的 ID 生成规则符合自己业务的特点;二是为了解决诸如时间回拨等问题。

其实,大厂除了采取 Snowflake 算法之外,还会选用一些其他的方案,比如滴滴和美团都有提出基于数据库生成 ID 的方案。这些方法根植于公司的业务,同样能解决分布式环境下 ID 全局唯一性的问题。对你而言,可以多角度了解不同的方法,这样能够寻找到更适合自己业务目前场景的解决方案,不过我想说的是,方案不在多,而在精,方案没有最好,只有最适合,真正弄懂方法背后的原理,并将它落地,才是你最佳的选择

总结

Snowflake 的算法并不复杂,你在使用的时候可以不考虑独立部署的问题,先想清楚按照自身的业务场景,需要如何设计 Snowflake 算法中的每一部分占的二进制位数。比如你的业务会部署几个 IDC,应用服务器要部署多少台机器,每秒钟发号个数的要求是多少等等,然后在业务代码中实现一个简单的版本先使用,等到应用服务器数量达到一定规模,再考虑独立部署的问题就可以了。这样可以避免多维护一套发号器服务,减少了运维上的复杂度。

在这里插入图片描述

如果这份博客对大家有帮助,希望各位给作者一个免费的点赞👍作为鼓励,并评论收藏一下⭐,谢谢大家!!!
制作不易,如果大家有什么疑问或给作者的意见,欢迎评论区留言。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1077328.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

嵌入式养成计划-38----C++--匿名对象--友元--常成员函数和常对象--运算符重载

八十七、匿名对象 概念&#xff1a;没有名字对象格式 &#xff1a;类名&#xff08;&#xff09;;作用 用匿名对象给有名对象初始化的用匿名对象给对象数组初始化的匿名对象作为函数实参使用 示例 : #include <iostream> using namespace std; class Dog { private:s…

小程序如何设置各种时间参数

在小程序管理员后台->基本设置处&#xff0c;可以设置各种时间。例如待支付提醒时间、待支付取消时间、自动发货时间、自动收货时间、自动评价时间等等。下面具体解释一下各个时间的意思。 1. 待支付提醒时间&#xff1a;在用户下单后&#xff0c;如果一段时间内没有完成支付…

IDEA的使用(一)代码模块的导入、快捷使用、自定义 (IntelliJ IDEA 2022.1.3版本)

目录 1. IDEA项目结构 2. 模块的导入操作 2.1 正规操作 2.2 取巧操作 2.3 出现乱码 2.4 模块改名 3. 代码模板的使用 后缀补全&#xff08;Postfix Completion&#xff09;、实时模板&#xff08;Live Templates&#xff09;菜单里面什么介绍都有&#xff0c;可以自学&a…

C#(Csharp)我的基础教程(四)(我的菜鸟教程笔记)-Windows项目结构分析、UI设计和综合事件应用的探究与学习

目录 windows项目是我们.NET学习一开始必备的内容。 1、窗体类&#xff08;主代码文件窗体设计器后台代码文件&#xff09; 主窗体对象的创建&#xff1a;在Program类里面&#xff1a; Application.Run(new FrmMain());这句代码就决定了&#xff0c;当前窗体是项目的主窗体。…

Python Opencv实践 - 车辆识别(1)读取视频,移除背景,做预处理

示例中的图像的腐蚀、膨胀和闭运算等需要根据具体视频进行实验得到最佳效果。代码仅供参考。 import cv2 as cv import numpy as np#读取视频文件 video cv.VideoCapture("../../SampleVideos/Traffic.mp4") FPS 10 DELAY int(1000 / FPS) kernel cv.getStructu…

Python数据容器——字典的常用操作(增、删、改、查)

作者&#xff1a;Insist-- 个人主页&#xff1a;insist--个人主页 本文专栏&#xff1a;Python专栏 专栏介绍&#xff1a;本专栏为免费专栏&#xff0c;并且会持续更新python基础知识&#xff0c;欢迎各位订阅关注. 目录 一、理解字典 1. Python字典是什么&#xff1f; 2. 字…

隆重宣布:.NET 8 RC1 现已推出

作者&#xff1a;Leslie Richardson 排版&#xff1a;Alan Wang .NET 8 RC1 现已推出。这是我们两个候选版本中的第一个。此版本包括适用于 Android 和 WASM 的新 AOT 模式、System.Text.Json 改进以及对容器的 Azure Managed Identity 支持。如果您还没有开始学习和测试 .NET …

基于Java+SpringBoot+Vue民宿管理系统的设计与实现 前后端分离【Java毕业设计·文档报告·代码讲解·安装调试】

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…

android Google官网 :支持不同的语言和文化 rtl / ltr : 本地化适配:RTL(right-to-left) 适配

参考 google官网&#xff1a; 支持不同的语言和文化 应用包含可能专门针对特定文化而设计的资源。例如&#xff0c;应用可以包含针对特定文化的字符串&#xff0c;这些字符串将转换为当前语言区域的语言。 将具有文化特异性的资源与应用的其他资源分开是一种很好的做法。And…

Apache Doris 数据建模之 Aggregate Key 模型

了解 Doris 数据模型对于我们使用 Doris 来解决我们业务问题非常重要&#xff0c;这个系列我们将详细介绍 Doris 的三种数据模型及 Doris 数据分区分桶的一些策略&#xff0c;帮助用户更好的使用 Doris 。 这个系列我会讲解 Doris 的三种数据模型及在这三种数据模型之上的 Rol…

算法练习11——买卖股票的最佳时机 II

122. 买卖股票的最佳时机 II 给你一个整数数组 prices &#xff0c;其中 prices[i] 表示某支股票第 i 天的价格。 在每一天&#xff0c;你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买&#xff0c;然后在 同一天 出售。 返回 你能获得…

【移植代码】matlab.engine报错、numpy+mkl安装、Qt platform plugin报错总结

文章目录 numpy报错numpy安装PyQt5报错matlab.engine无法加载确认配置版本进行配置 matlab文件路径缺失vscode无法debug3.7以下版本总结 今天的任务是复现师姐的代码&#xff0c;代码在服务器的环境下可以跑&#xff0c;而我要做的&#xff0c;就是将环境和源码配置好&#xff…

Python库学习(九):Numpy[续篇三]:数组运算

NumPy是用于数值计算的强大工具&#xff0c;提供了许多数组运算和数学函数&#xff0c;允许你执行各种操作&#xff0c;包括基本运算、统计计算、线性代数、元素级操作等 1.基本运算 1.1 四则运算 NumPy数组支持基本的四则运算&#xff08;加法、减法、乘法和除法&#xff09;…

mysql面试题31:一条SQL语句在MySQL中如何执行的

该文章专注于面试,面试只要回答关键点即可,不需要对框架有非常深入的回答,如果你想应付面试,是足够了,抓住关键点 面试官:一条SQL语句在MySQL中如何执行的 以下是一条SQL语句在MySQL中的详细执行步骤: 语法分析:MySQL首先对SQL语句进行语法分析,检查SQL语句是否符合…

算法题:分发饼干(典型的贪心算法问题)

这个题目是一个典型的贪心算法问题&#xff0c;解决思路是排序双指针贪心法&#xff0c;先将两个数组分别排序&#xff0c;优先满足最小胃口的孩子。&#xff08;本题完整题目附在了最后面&#xff09; 代码如下&#xff1a; class Solution(object):def findContentChildren(…

如何使用 ONLYOFFICE API 转换办公文档格式

作者&#xff1a;天哥 上一期我们介绍了 ONLYOFFICE 的文档生成器API接口函数库。这一期我们继续介绍ONLYOFFICE 的文件转换API接口函数库。 为什么要使用 ONLYOFFICE 转换API ONLYOFFICE 转换 API 有助于转换大部分类型的Office文档&#xff1a;文本、表格、幻灯片、表单、P…

Visual Studio 2022新建项目时没有ASP.NET项目

一、Visual Studio 2022新建项目时没有ASP.NET项目 1、打开VS开发工具&#xff0c;选择工具菜单&#xff0c;点击“获取工具和功能” 2、选择“ASP.NET和Web开发”和把其他项目模板&#xff08;早期版本&#xff09;勾选上安装即可

C/C++实现简单高并发http服务器

基础知识 html&#xff0c;全称为html markup language&#xff0c;超文本标记语言。 http&#xff0c;全称hyper text transfer protocol&#xff0c;超文本传输协议。用于从万维网&#xff08;WWW&#xff1a;World Wide Web&#xff09;服务器传输超文本到本地浏览器的传送…

网络安全(黑客)—小白自学

前言 一、什么是网络安全 网络安全可以基于攻击和防御视角来分类&#xff0c;我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术&#xff0c;而“蓝队”、“安全运营”、“安全运维”则研究防御技术。 无论网络、Web、移动、桌面、云等哪个领域&#xff0c;都有攻与防…

什么牌子洗地机最好最实用?口碑最好的洗地机排名

洗地机具备吸拖扫洗一体的特点&#xff0c;专治各种懒病&#xff0c;面对众多的洗地机&#xff0c;对于一些新手来说一时之间无从选择&#xff0c;今天笔者给大家介绍几款近期口碑比较好的家用洗地机。 洗地机洗地机合适的人群&#xff1a; 家里有小宝宝&#xff1a;一般2-10…