【从零开始学习Redis | 第七篇】利用Redis构造全局唯一ID(含其他构造方法)

news2024/10/3 6:36:24

目录

前言:

什么是全局唯一ID? 

尝试构造全局唯一ID: 

其他构造全局唯一ID的方法

1.基于数据库自增构造全局唯一ID:

2.基于UUID构造全局唯一ID:

3.基于雪花算法构造全局唯一ID:

总结:


 

前言:

        在各种实际业务中,全局唯一ID是一个重要的存在,它用来标识用户的特定服务,方便用户在后续基于这个ID来进行各种服务。而如何构造全局唯一ID也是一个比较重要的知识点。因此今天来介绍一下如何基于Redis构造全局唯一ID。

什么是全局唯一ID? 

        全局唯一ID(Global Unique Identifier,简称GUID)是在计算机系统中用于唯一标识实体或对象的标识符。它通常由一个128位的数字字符串组成,采用特定的算法生成,以确保在相同的算法和生成器设置下几乎不会重复。


让我们回到业务中:

每个店铺都可下发自己店铺的优惠卷,当用户抢购的时候,就会生成订单到订单表中,并且返回订单号给用户。但是如果只是使用简单的数据库自增就会出现问题:

        1.少量数据下,采用数据库自增的方式,会泄漏信息给用户。用户可以根据订单ID推测出优惠卷的订单数,可能会引发恶意行为。

        2.大量数据下,如果所有的订单都在一个张表中,在进行SQL查询的时候效率会大大降低,并且如果我们进行了分表,那么多个订单表中间的数据库自增是隔离的,并不能保证多表下的ID唯一


        那么我们目前可以知道:如果要追求ID的唯一性,那么就应该避免在数据库中进行ID的构造。基于这种情况,那么我们就把ID的构造放到Redis中进行。

尝试构造全局唯一ID: 

而为了增加ID的安全性,我们也不直接使用Redis的自增数值,而是再拼接一些其他的信息:

由图可看得,我们设计的ID一共分为三部分:

1.符号位:永远为0,标识我们的ID是一个整数。

2.时间戳:从自定义时间开始,按秒计算。那么32位我们大约可以使用60多年。

3.序列号:同一时间内下单进行自增。在时间戳相等的形况下的区分不同的订单。

代码实现:

    public long nextId(String keyPrefix) {
        //1.生成时间戳
        LocalDateTime now = LocalDateTime.now();
        long nowhSecond = now.toEpochSecond(ZoneOffset.UTC);
        long timestamp = nowhSecond - BEGIN_TIMESTAMP;
        //2.生成序列号
        //1.获取日期,精确到天
        String date = now.format(DateTimeFormatter.ofPattern("yyyy:MM:dd"));
        Long count = stringRedisTemplate.opsForValue().increment("icr:" + keyPrefix + ":" + date);
      
        return (timestamp << COUNT_BITS) | count;
    }

 在这里我们唯一需要讲的就是返回结果中的(timestamp << COUNT_BITS) | count

                其实是他就是一个拼接序列号的过程,只不过相比较于符号运算来讲,使用位运算的效率更高。

其他构造全局唯一ID的方法

1.基于数据库自增构造全局唯一ID:

 基于数据库自增构造ID,之前我们讲了主要的难点是:分表之后无法统一构造自增唯一ID,多个订单表在构造ID的时候可能会出现重复。

那么其实解决方案很简单:既然多个表在构造ID的时候会出现重复问题,那么我们就不要在订单表中构造ID了,创建一个订单ID表去专门维护ID

比如我们可以这样设计一个订单ID表:

create table order_test.order_id
(
    id   int auto_increment
        primary key,
    name varchar(20) null,
    constraint name
        unique (name)
);

那么我们就得到了这样一张表:

在查询的时候,我们使用这样一条语句:

begin ;
replace into order_id (name) values ("order_id");
SELECT last_insert_id();
COMMIT ;

 这样我们基于name的唯一性,就做到了对id的自增:

 但是这种创建一张表去维护订单ID的方式仍然是有问题的:高并发场景下,万一这张表挂了怎么办

所以为了优化,我们还可以采取多表的思想:

在新思想中,我们让一个表维护订单id为偶数,一个表维护订单id为奇数。这样的话,我们就是实现了减轻单表压力。而且这种思想是可以不断的改进的,我们可以通过让表维护不同类型的数字来不断的拆表,减轻单表压力

但是这种方法得到的订单id,他不一定是逐个递增的,只能说是整体呈现递增趋势

而且这种方式如果要抵抗高并发的话,就要不断的去加数据库,对维护数字进行分类。因此这种方式其实缺点还是比较明显的。

但其实基于数据库构造全局唯一ID是有成熟的方案的:美团的LEAF数据库方案,原文链接我也留在这里:

Leaf——美团点评分布式ID生成系统 - 美团技术团队 (meituan.com)icon-default.png?t=N7T8https://tech.meituan.com/2017/04/21/mt-leaf.html        LEAF数据库方案简而言之就一句话:批量获取ID进行处理。在上文我们简单的对数据库进行优化的时候,优化问题基本都来源于高并发下数据库高频的读写操作。而LEAF数据库方案也是针对这个方面进行优化的

我们可以把图中的leaf简单的理解为是一个生成全局唯一ID的服务。那么整个LEAF数据库的思想就是:leaf服务提前就拿好一批号端,例如从0-1000。那么我在生成唯一ID的时候,压力就从数据库转到了Leaf这个服务里面。

优点:

        1.leaf只是一个简单的web服务,方便进行扩展

        2.ID号也满足趋势递增的要求

        3.容灾性高,由于生成唯一ID的是leaf服务,而且内部有号段缓存,因此即使数据库挂了,短时间内也可以正常对外提供服务

缺点:

        当所有的leaf用完自己的号段之后,就会向数据库再次请求号段,此时leaf服务是不可用的。而如果此时有大量的请求leaf服务,就会引发一段尖刺。

解决方案:

        我们并不会等到号段全部用完之后再去请求新的号段。美团给出的技术方案是当号段消费到某个点时就异步的把下一个号段加载到内存中。而不需要等到号段用尽的时候才去更新号段。

        一开始先用A号段,等 A号段消耗10%的时候,就向数据库请求新号段。之后当前号段消耗完之后就可以进行快速的切换。如此循环往复。

2.基于UUID构造全局唯一ID

虽然基于UUID可以保证构造ID的唯一性,但是UUID是随机生成的数字+字母。这使得UUID构造出的ID不具备递增性,在做数据库索引的时候效率比较慢。

而且UUID也存在安全性问题:UUID有一版是基于MAC地址来构造唯一标识的,可能会有泄漏MAC地址的风险

import java.util.UUID;

public class UUIDExample {
    public static void main(String[] args) {
        // 生成随机的UUID
        UUID uuid = UUID.randomUUID();
        System.out.println("随机生成的UUID: " + uuid.toString());

        // 根据字符串生成UUID
        String uuidString = "38400000-8cf0-11bd-b23e-10b96e4ef00d";
        UUID fromString = UUID.fromString(uuidString);
        System.out.println("从字符串生成的UUID: " + fromString.toString());
    }
}

3.基于雪花算法构造全局唯一ID

六十四位雪花算法构成:

其实可以看出,雪花算法跟我们上文提到的基于Redis生成全局唯一ID的思路是一样的。因此这里不做赘述。

而基于雪花算法,最常问的一个面试问题就是:如何解决时间回拨问题

其实就是说:如果时间戳回拨了,我们要如何处理?因为时间戳回拨之后,生成的id就有可能和以前的重合,那么我们要如何进行处理呢?

大部分的开源版本的雪花算法面对时间回拨问题采用的是抛异常的处理方法,其实我感觉这属于是没处理。

比较好的处理方法是根据回拨时间的长短来个性化处理方案:

        1.回拨时间很短(<=100ms):直接睡眠当前线程,等到时间戳加载正常之后再生成ID

        2.回拨时间适中(>100ms<=1s):寻找当前回拨时间毫秒内的最大id,直接沿着那个id++就可以。

        3.回拨时间较长(>1s<=5s):创建多个雪花算法服务,当前使用的雪花算法服务时间戳发生回拨就换一个。

        4.回拨时间很长(>5s):直接下线服务,人工介入手动调试时间戳。

除此之外,我们还可以提前就预留好一部分的分布式ID,在时间戳回拨之后,我们使用这部分预留的分布式ID,直至时间戳恢复正常。

总结:

当我们在设计分布式系统时,唯一ID的生成是一个非常重要的问题。为了保证分布式环境下ID的唯一性和无序性,我们可以采用雪花算法、UUID、数据库自增等方式生成唯一ID。

其中,雪花算法是目前应用最广泛的分布式ID生成算法之一。它通过使用时间戳、机器ID和序列号来生成64位的唯一ID,可以在多个节点上生成ID而不会重复。

除了ID的生成算法外,还需要考虑时钟回拨问题、ID的长度、ID的可读性等问题。为了解决时钟回拨问题,我们可以采取物理时钟、NTP同步、预留ID范围等方式;

如果我的内容对你有帮助,请点赞,评论,收藏。创作不易,大家的支持就是我坚持下去的动力!

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

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

相关文章

从0开始学前端第三天

学习内容&#xff1a; CSS&#xff1a; JavaScript&#xff1a; 遇到的问题&#xff1a; 1、JavaScript使用console.log无输出&#xff1a; 仔细检查了一下&#xff0c;在script标签中&#xff0c;加入了typemoudle以后&#xff0c;代码连高亮都没有了&#xff0c;应该是没有…

Mysql深度分页优化的一个实践

问题简述: 最近在工作中遇到了大数据量的查询场景, 日产100w左右明细, 会查询近90天内的数据, 总数据量约1亿, 业务要求支持分页查询与导出. 无论是分页或导出都涉及到深度分页查询, mysql通过limit/offset实现的深度分页查询会存在全表扫描的问题, 比如offset1000w, limit10…

elasticsearch 中热词使用遇到的坑

在使用es检索时,一般会创建索引以及索引下mapping和setting一样配置,如下: 命令创建配置方式: PUT /my_index { "settings": { "number_of_shards": 1 }, "mappings": { "properties": { "title": { …

python数字图像处理基础(八)——harris角点检测、图像尺度空间、SIFT算法

目录 harris角点检测原理函数 图像尺度空间概念局部不变性局部不变特征SIFT算法 harris角点检测 原理 Harris 角点检测是一种用于在图像中检测角点的算法。角点是图像中局部区域的交叉点或者突出的特征点。Harris 角点检测算法旨在寻找图像中对于平移、旋转和尺度变化具有不变…

C++设计模式(李建忠)笔记2

C设计模式&#xff08;李建忠&#xff09; 本文是学习笔记&#xff0c;如有侵权&#xff0c;请联系删除。 参考链接 Youtube: C设计模式 Gtihub源码与PPT&#xff1a;https://github.com/ZachL1/Bilibili-plus 豆瓣: 设计模式–可复用面向对象软件的基础 文章目录 C设计模…

vite和webpack的区别和作用

前言 Vite 和 Webpack 都是现代化的前端构建工具&#xff0c;它们可以帮助开发者优化前端项目的构建和性能。虽然它们的目标是相似的&#xff0c;但它们在设计和实现方面有许多不同之处。 一、Vite详解和作用 vite 是什么 vite —— 一个由 vue 作者尤雨溪开发的 web 开发工…

第二百七十三回

文章目录 1. 概念介绍2. 方法与信息2.1 获取方法2.2 详细信息 3. 示例代码4. 内容总结 我们在上一章回中介绍了"蓝牙综合示例"相关的内容&#xff0c;本章回中将介绍如何获取设备信息.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1. 概念介绍 我们在本章回中获…

鸿蒙开发之手势Pan

Entry Component struct OfficialPanGesturePage {State message: string 默认只左右移动State offsetX: number 0State offsetY: number 0State positionX: number 0State positionY: number 0//默认pan的参数&#xff0c;1根手指&#xff0c;左右方向private panOption:…

提纲框架写作方法

论文提纲 论文提纲的意义 有利于检查构思有利于调整修改和写作 拟定提纲的目的 拟标题写总论点做总安排&#xff1a;几个方面&#xff0c;什么顺序做下位论点&#xff1a;每个项目的下位论点&#xff0c;直到段一级&#xff0c;写段的论点句考虑各段安排&#xff0c;把材料…

Visual Studio 与 SQL Server 常见报错解决方案(工作向)

前言 这篇文章从今天创建开始&#xff0c;会一直更新下去&#xff0c;以后遇到常见但是比较容易解决的报错会在本文进行更新&#xff0c;有需要的朋友可以收藏再看 目录 Visual Studio lc.exe已退出&#xff0c;代码为-1无法导入以下密钥文件xxx.pfx&#xff0c;该密钥文件…

RFID涉密文件载体管控系统

1.1 系统简介 RFID涉密文件载体管控系统是一种基于远距离射频识别技术的解决方案&#xff0c;通过非接触式采集射频卡的信息&#xff0c;实现对涉密文件载体的自动识别和监管&#xff0c;该系统集成了计算机软硬件、信息采集处理、数据传输、网络通讯、机械电子、自动控制和智…

uniapp踩坑之项目:canvas第一次保存是空白图片

在ctx.draw()回调生成图片&#xff0c;参考canvasToTempFilePath接口文档 // data imgFilePath: null,// 缓存二维码图片canvas路径//js // 首先在draw&#xff08;&#xff09;里进行本地存储 ...... ctx.draw(false, () >{uni.canvasToTempFilePath({ // 把画布转化成临时…

算法练习-反转一个单链表(思路+流程图+代码)

难度参考 难度&#xff1a;简单 分类&#xff1a;链表 难度与分类由我所参与的培训课程提供&#xff0c;但需要注意的是&#xff0c;难度与分类仅供参考。以下内容均为个人笔记&#xff0c;旨在督促自己认真学习。 题目 反转一个单链表&#xff08;不带头节点&#xff09; …

hub汉语有轮毂的意思吗?

问题描述&#xff1a;hub汉语有轮毂的意思吗&#xff1f; 问题解答&#xff1a; 是的&#xff0c;"hub"&#xff08;中文翻译为"轮毂"&#xff09;是指机械装置中的一个中心部分&#xff0c;通常用于连接或支持其他部分。在车辆的轮胎系统中&#xff0c;…

STC8H8K蓝牙智能巡线小车——1. 环境搭建(基于RTX51操作系统)

1. 基本介绍 开发环境准备&#xff1a;Keil uVision5 烧录软件&#xff1a;STC-ISP&#xff08;V6.92A&#xff09; 芯片&#xff1a; STC8H8K64U-45I-LQFP64 芯片引脚&#xff1a; 2.创建项目 打开Keil&#xff0c;点击【Project】&#xff0c;选择【new uVersion proje…

C语言——大头记单词

归纳编程学习的感悟&#xff0c; 记录奋斗路上的点滴&#xff0c; 希望能帮到一样刻苦的你&#xff01; 如有不足欢迎指正&#xff01; 共同学习交流&#xff01; &#x1f30e;欢迎各位→点赞 &#x1f44d; 收藏⭐ 留言​&#x1f4dd; 每一发奋努力的背后&#xff0c;必有加…

【​电力电子在电力系统中的应用​】6 滞环电流控制的PWM整流器 + STATCOM整流器 + APF仿真

【仅供参考】 【2023.06西南交大电力电子在电力系统中的应用】 目录 步骤一&#xff1a;基于滞环电流控制的PWM整流器仿真 1.1 仿真要求 1.2 仿真电路原理及设计 1.2.1 主电路的搭建 1.2.2 控制电路的搭建 1.3 波形分析 步骤二&#xff1a;从PWM整流器到STATCOM仿真 2…

ARM day5、day6 硬件编程

一、硬件 fs4412 sd卡 串口线 电源 二、根据原理图点灯 1、确定需求&#xff1a; 点灯&#xff08;亮 or 灭&#xff09; 2、查看原理图 2.1 外设原理图 devboard 查找LED2->CHG_COK(核心板) 2.2 核心板原理图 coreboard 查找CHG_COK->XEINT23/KP_ROW7/ALV_DBG…

Redis分布式锁存在的问题以及解决方式

☆* o(≧▽≦)o *☆嗨~我是小奥&#x1f379; &#x1f4c4;&#x1f4c4;&#x1f4c4;个人博客&#xff1a;小奥的博客 &#x1f4c4;&#x1f4c4;&#x1f4c4;CSDN&#xff1a;个人CSDN &#x1f4d9;&#x1f4d9;&#x1f4d9;Github&#xff1a;传送门 &#x1f4c5;&a…

ROS---传感器集成

ROS—传感器集成 对于ROS机器人&#xff0c;如果想让其启动&#xff0c;我们需要逐一启动底盘控制与激光雷达&#xff0c;操作有点冗余。并且如果只是简单启动这些节点&#xff0c;那么在在 rviz 中显示时&#xff0c;会发现出现了TF转换异常&#xff0c;比如参考坐标系设置为o…