电商项目part05 分布式ID服务实战

背景

日常开发中,需要对系统中的各种数据使用 ID 唯一表示,比如用户 ID
对应且仅对应一个人,商品 ID 对应且仅对应一件商品,订单 ID 对应且仅对应
一个订单。现实生活中也有各种 ID,比如身份证 ID 对应且仅对应一个人,
简单来说,ID 就是数据的唯一标识。
一般情况下,会使用数据库的自增主键作为数据 ID,但是在大数量的情况下,往往会引入分布式、分库分表等手段来应对,很明显对数据分库分表后依然需要有一个唯一 ID 来标识一条数据或消息,数据库的自增 ID 已经无法满足需求。此时一个能够生成全局唯一 ID 的系统是非常必要的。
概括下来业务系统对 ID 号的要求有
全局唯一性:不能出现重复的 ID 号,既然是唯一标识,这是最基本的要求。
趋势递增、单调递增:保证下一个 ID 一定大于上一个 ID。
信息安全:如果 ID 是连续的,恶意用户的扒取工作就非常容易做了,直接
按照顺序下载指定 URL 即可;如果是订单号就更危险了,竞对可以直接知道一天的单量。所以在一些应用场景下,会需要 ID 无规则、不规则。
同时除了对 ID 号码自身的要求,业务还对 ID 号生成系统的可用性要求极高,
想象一下,如果 ID 生成系统不稳定,大量依赖 ID 生成系统,比如订单生成等关
键动作都无法执行。所以一个 ID 生成系统还需要做到平均延迟和 TP999 延迟都
要尽可能低;可用性 5 个 9;高 QPS。

常见方法介绍

UUID

UUID(Universally Unique Identifier)的标准型式包含 32 个 16 进制数字,以连
字号分为五段,形式为 8-4-4-4-12 的 36 个字符,示例:
550e8400-e29b-41d4-a716-446655440000,到目前为止业界一共有 5 种方式生成
UUID,详情见 IETF 发布的 UUID 规范 A Universally Unique IDentifier (UUID) URN
Namespace。
优点:
性能非常高:本地生成,没有网络消耗。
缺点:
不易于存储:UUID 太长,16 字节 128 位,通常以 36 长度的字符串表示,
很多场景不适用。
信息不安全:基于 MAC 地址生成 UUID 的算法可能会造成 MAC 地址泄露
这个漏洞曾被用于寻找梅丽莎病毒的制作者位置。

ID作为主键时在特定的环境会存在一些问题,比如做DB主键的场景下,UUID
就非常不适用:
① MySQL官方有明确的建议主键要尽量越短越好[4],36个字符长度的UUID
不符合要求。
② 对 MySQL 索引不利:如果作为数据库主键,在 InnoDB 引擎下,UUID 的
无序性可能会引起数据位置频繁变动,严重影响性能。在 MySQL InnoDB 引擎中使用的是聚集索引,由于多数 RDBMS 使用 B-tree 的数据结构来存储索引数据,在主键的选择上面应该尽量使用有序的主键保证写入性能。可以直接使用 jdk 自带的 UUID,原始生成的是带中划线的,如果不需要,可自行去除

在这里插入图片描述

雪花算法

Snowflake 是 Twitter 开源的分布式 ID 生
成算法。Snowflake 把 64-bit 分别划分成多段,分开来标示机器、时间等,比如
在 snowflake 中的 64-bit 分别表示如下图所示:(时间戳+机房号+序列号)
在这里插入图片描述
第 0 位: 符号位(标识正负),始终为 0,没有用,不用管。
第 1~41 位 :一共 41 位,用来表示时间戳,单位是毫秒,可以支撑 2 ^41
毫秒(约 69 年)
第 42~52 位 :一共 10 位,一般来说,前 5 位表示机房 ID,后 5 位表
示机器 ID(实际项目中可以根据实际情况调整),这样就可以区分不同集群/机
房的节点,这样就可以表示 32 个 IDC,每个 IDC 下可以有 32 台机器。
第 53~64 位 :一共 12 位,用来表示序列号。 序列号为自增值,代表单
台机器每毫秒能够产生的最大 ID 数(2^12 = 4096),也就是说单台机器每毫秒最
多可以生成 4096 个 唯一 ID。
理论上 snowflake 方案的 QPS 约为 409.6w/s,这种分配方式可以保证在任何
一个 IDC 的任何一台机器在任意毫秒内生成的 ID 都是不同的。

有很多基于 Snowflake 算法的开源实现比如美团的 Leaf、百度的
UidGenerator(自 18 年后,UidGenerator 就基本没有再维护了,
https://github.com/baidu/uid-generator/blob/master/README.zh_cn.md),并且这
些开源实现对原有的 Snowflake 算法进行了优化。在实际项目中,一般也
会对 Snowflake 算法进行改造,最常见的就是在算法生成的 ID 中加入业务类型
信息。
关于自行实现 Snowflake 算法,可以参考 tulingmall-unqid 下的
com.tuling.tulingmall.service.snowflake 下的代码。
Snowflake 优缺点是:
优点:
毫秒数在高位,自增序列在低位,整个 ID 都是趋势递增的。
不依赖数据库等第三方系统,以服务的方式部署,稳定性更高,生成 ID 的
性能也是非常高的。
可以根据自身业务特性分配 bit 位,非常灵活。
缺点:
强依赖机器时钟,如果机器上时钟回拨,会导致发号重复或者服务会处于不
可用状态。
当然,在项目中如果不想自行实现唯一性 ID,还可以利用外部中
间件,比如 Mongdb objectID,它也可以算作是和 snowflake 类似方法,通过“时
间+机器码+pid+inc”共 12 个字节,通过 4+3+2+3 的方式最终标识成一个 24 长度的十六进制字符。
其次 Seata 内置了一个分布式 UUID 生成器,用于辅助生成全局事务 ID 和分
支事务 ID,同样可以拿来使用,完整类名为: io.seata.common.util.IdWorker

数据库生成

MYSQL

以 MySQL 举例,
1.创建一个数据库表。

CREATE TABLE `sequence_id` (`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,`stub` char(10) NOT NULL DEFAULT '',PRIMARY KEY (`id`),UNIQUE KEY `stub` (`stub`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; 

stub字段无意义,只是为了占
位,便于我们插入或者修改数据。并且,给 stub 字段创建了唯一索引,保证其
唯一性。
2.通过 replace into 来插入数据。
BEGIN;
REPLACE INTO sequence_id (stub) VALUES (‘stub’);
SELECT LAST_INSERT_ID();
COMMIT;
插入数据这里,我们没有使用 insert into 而是使用 replace into 来插入数据。
replace 是 insert 的增强版,replace into 首先尝试插入数据到表中,1. 如果发现
表中已经有此行数据(根据主键或者唯一索引判断)则先删除此行数据,然后插
入新的数据。 2. 否则,直接插入新数据。
数据库方案的优缺点如下:
优点:
非常简单,利用现有数据库系统的功能实现,成本小,有 DBA 专业维护。ID
号单调自增,存储消耗空间小。
缺点:
支持的并发量不大、存在数据库单点问题(可以使用数据库集群解决,不过
增加了复杂度)、ID 没有具体业务含义、安全问题(比如根据订单 ID 的递增
规律就能推算出每天的订单量,商业机密啊! )、每次获取 ID 都要访问一次
数据库(增加了对数据库的压力,获取速度也慢)

Redis

通过 Redis 的 incr 命令即可实现对 id 原子顺序递增,例如:

127.0.0.1:6379> incr sequence_id_biz_type
(integer) 2

为了提高可用性和并发,我们可以使用 Redis Cluster。
除了高可用和并发之外,我们知道 Redis 基于内存,我们需要持久化数据,
避免重启机器或者机器故障后数据丢失。很明显,Redis 方案性能很好并且生成
的 ID 是有序递增的。
不过,我们也知道,即使 Redis 开启了持久化,不管是快照(snapshotting,
RDB)、只追加文件(append-only file, AOF)还是 RDB 和 AOF 的混合持久化依然存在着丢失数据的可能,那就意味着产生的 ID 存在着重复的概率。

弱依赖 ZooKeeper

除了每次会去 ZK 拿数据以外,也会在本机文件系统上缓存一个 workerID 文件。当 ZooKeeper 出现问题,恰好机器出现问题需要重启时,能保证服务能够正常启动。这样做到了对三方组件的弱依赖。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

openCV实战-系列教程9:傅里叶变换(傅里叶概述/频域变换结果/低通与高通滤波)、原理解析、源码解读

OpenCV实战系列总目录 打印图像直接用这个函数: def cv_show(img,name):cv2.imshow(name,img)cv2.waitKey()cv2.destroyAllWindows()1、傅里叶变换 在生活中,我们的大部分事情都是以时间为参照的,用时间为参照的为时域分析,在频…

Batbot电力云平台在智能配电室中的应用

智能配电室管理系统是物联网应用中的底层应用场景,无论是新基建下的智能升级,还是双碳目标下的能源管理,都离不开智能配电运维对传统配电室的智慧改造。Batbot智慧电力(运维)云平台通过对配电室关键电力设备部署传感器…

怎么对App进行功能测试

测试人员常被看作是bug的寻找者,但你曾想过他们实际是如何开展测试的吗?你是否好奇他们究竟都做些什么,以及他们如何在一个典型的技术项目中体现价值?本文将带你经历测试人员的思维过程,探讨他们测试app时的各种考虑. …

STM32 CubeMX (H750)RGB屏幕 LTDC

STM32 CubeMX STM32 RGB888 LTDC STM32 CubeMX一、STM32 CubeMX 设置时钟树LTDC使能设置屏幕参数修改RGB888的GPIO 二、代码部分效果 RGB屏幕线束定义: 一、STM32 CubeMX 设置 时钟树 这里设置的时钟,关于刷新速度 举例子:LCD_CLK24MHz 时…

使用树莓派Pico、DHT11和SSD1306搭建一个温度湿度计(只使用官方库,以及官方案例代码的错误之处和解决方案)

最近想树莓派 Pico、DHT11 温湿度传感器和 SSD1306 OLED 屏幕做一个温度湿度计,树莓派官方案例也分别有这两个设备的案例,我就想做个简单的温度湿度计作为学习微控制器的开始,结果遇到了一个大坑,所以写本文记录一下整个过程。 本…

图论(基础)

知识: 顶点,边 | 权,度数 1.图的种类: 有向图 | 无向图 有环 | 无环 联通性 基础1:图的存储(主要是邻接矩阵和邻接表) 例一:B3643 图的存储 - 洛谷 | 计算机科学教育新生态 (…

spring boot 项目整合 websocket

1.业务背景 负责的项目有一个搜索功能,搜索的范围几乎是全表扫,且数据源类型贼多。目前对搜索的数据量量级未知,但肯定不会太少,不仅需要搜索还得点击下载文件。 关于搜索这块类型 众多,未了避免有个别极大数据源影响整…

深入理解回调函数qsort:从入门到模拟实现

💓博客主页:江池俊的博客⏩收录专栏:C语言进阶之路👉专栏推荐:✅C语言初阶之路 ✅数据结构探索💻代码仓库:江池俊的代码仓库​🎪 社区:GeekHub社区 ​🎉欢迎大…

微信开发之一键修改群聊名称的技术实现

修改群名称 修改群名后,如看到群名未更改,是手机缓存问题,可以连续点击进入其他群,在点击进入修改的群,再返回即可看到修改后的群名 请求URL: http://域名地址/modifyGroupName 请求方式: …

基于SpringBoot+MybatisPlus+Shiro+mysql+redis智慧云智能教育平台

基于SpringBootMybatisPlusShiromysqlredis智慧云智能教育平台 一、系统介绍二、功能展示三.其他系统实现五.获取源码 一、系统介绍 声明:Java智慧云智能教育平台源码 前后端分离、 开发语言:JAVA 数据库:MySQL5.7以上 开发工具&#xff…

从哈希表到红黑树:探讨 epoll 是如何管理事件的?

揭开pkill的秘密:在Linux中杀死进程的完整指南 一、引言二、 传统事件管理的局限性三、epoll 概述3.1、epoll 的基本概念和工作原理3.2、epoll 在 Linux 内核中的实现方式 四、哈希表在事件管理中的挑战五、 红黑树在 epoll 中的应用六、epoll 中的事件注册与触发七…

功能强大的网站检测工具Web-Check

什么是 Web-Check ? Web-Check是一款功能强大的一体化工具,用于查找有关网站/主机的信息。目前仪表版上可以显示:IP 信息、SSL 信息、DNS 记录、cookie、请求头、域信息、搜索爬虫规则、页面地图、服务器位置、开放端口、跟踪路由、DNS 安全扩…

自定义Chronometer实现定时器

概述 自定义Chronometer实现定时器,引用方便,操作简单。 详细 前言 在Android开发过程中,计时控件是经常回使用到的,在Android控件库中有一个能快捷实现计时功能的控件,它就是Chronometer,今天我们基于它自定义实现…

DataFrame.set_index()方法--Pandas

1.函数功能 为DataFrame重新设置索引(行标签) 2. 函数语法 DataFrame.set_index(keys, *, dropTrue, appendFalse, inplaceFalse, verify_integrityFalse)3. 函数参数 参数含义keys作为行标签的列名,可以DataFrame中的是单个列或者多列组…

C语言——指针进阶(一)

目录 ​编辑 一.字符指针 1.1 基本概念 1.2 面试题 二.指针数组 三.数组指针 3.1 数组指针的定义 3.2 &数组名VS数组名 3.3 数组指针的使用 四.数组参数、指针参数 4.1 一维数组传参 ​编辑 4.2 二维数组传参 4.3 一级指针传参 4.4 二级指针传参 ​编辑 五.…

好用的可视化大屏适配方案

1、scale方案 优点&#xff1a;使用scale适配是最快且有效的&#xff08;等比缩放&#xff09; 缺点&#xff1a; 等比缩放时&#xff0c;项目的上下或者左右是肯定会有留白的 实现步骤 <div className"screen-wrapper"><div className"screen"…

点亮一颗LED灯

TOC LED0 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//使能APB2的外设时钟GPIO_InitTypeDef GPIO_Initstructure;GPIO_Initstructure.GPIO_Mode GPIO_Mode_Out_PP;//通用推挽输出GPIO_Initstructure.GPIO_Pin GPIO_Pin_5;GPIO_Initstructure.GPIO_Speed GPIO_S…

剑指 Offer 19. 正则表达式匹配(C++实现)

剑指 Offer 19. 正则表达式匹配https://leetcode.cn/problems/zheng-ze-biao-da-shi-pi-pei-lcof/ 动态规划&#xff1a;通过dp数组剪枝 只需要对各种情况进行分类处理即可 vector<vector<int>> dp;bool helper(const string& s, const int i, const string&am…

【Go 基础篇】Go语言数组遍历:探索多种遍历数组的方式

数组作为一种基本的数据结构&#xff0c;在Go语言中扮演着重要角色。而数组的遍历是使用数组的基础&#xff0c;它涉及到如何按顺序访问数组中的每个元素。在本文中&#xff0c;我们将深入探讨Go语言中多种数组遍历的方式&#xff0c;为你展示如何高效地处理数组数据。 前言 …

【leetcode 力扣刷题】双指针///原地扩充线性表

双指针///原地扩充线性表 剑指 Offer 05. 替换空格定义一个新字符串扩充字符串&#xff0c;原地替换思考 剑指 Offer 05. 替换空格 题目链接&#xff1a;剑指 Offer 05. 替换空格 题目内容&#xff1a; 这是一道简单题&#xff0c;理解题意&#xff0c;就是将字符串s中的空格…