一文详解分布式 ID

分布式系统中,我们经常需要对数据、消息等进行唯一标识,这个唯一标识就是分布式 ID,那么我们如何设计它呢?本文将详细讲述分布式 ID 及其生成方案。


一、为什么需要分布式 ID

目前大部分的系统都已是分布式系统,所以在这种场景的业务开发中,经常会需要唯一 ID 对数据进行标识,比如用户身份标识、消息标识等等。

并且在数据量达到一定规模后,大部分的系统也需要进行分库分表,这种场景下单库的自增 ID 已达不到我们的预期。所以我们需要分布式 ID 来对各种场景的数据进行唯一标识。

二、分布式 ID 的特性

主要特性:

  • 全局唯一:分布式 ID 最基本要求,必须全局唯一。
  • 高可用:高并发下要保证 ID 的生成效率,避免影响系统。
  • 易用性:使用简单,可快速接入。

除此之外根据不同场景还有:

  • 有序性:数据库场景下的主键 ID,有序性可便于数据写入和排序。
  • 安全性:无规则 ID,一般用于避免业务信息泄露场景,如订单量。

三、分布式 ID 常见生成方案

(一) UUID

UUID(Universally Unique Identifier),即通用唯一识别码。UUID 的目的是让分布式系统中的所有元素都能有唯一的识别信息。

UUID 是由128位二进制数组成,通常表示为32个十六进制字符,形如:

550e8400-e29b-41d4-a716-446655440000

这个字符串由五个部分组成,以连字符-分隔开,具体如下:

部分

大小

说明

时间戳

32 bits

UUID的前32位表示当前的时间戳。

时钟序列和随机数

16 bits

用于保证在同一时刻生成的UUID的唯一性。

变体标识

4 bits

标识 UUID 的变体,通常为固定值,表示是由 RFC 4122 定义。

版本号

4 bits

标识UUID的版本,常见版本有1、3、4和5

节点

48 bits

在版本 1 中,这部分包含生成 UUID 的计算机的唯一标识。

主要的 UUID 版本及其生成规则:

版本

场景

生成规则

版本 1

基于时间和节点

由当前的时间戳和节点信息生成。包括时间戳、时钟序列、节点标识。

版本 2

基于DCE安全标识符

类似版本 1,但在时间戳部分包含 POSIX UID/GID 信息。

版本 3

基于名字和散列值(MD5 版)

由命名空间和名字的MD5散列生成。

版本 4

完全随机生成

通过随机或伪随机生成128位数字。

版本 5

基于名字和散列值(SHA-1 版)

通过随机或伪随机生成128位数字。

Java中 UUID 对版本 4 进行了实现:

public static void main (String[] args) {// 默认版本 4System.out.println(UUID.randomUUID());// 版本 3,由命名空间和名字的MD5散列生成,相同命名空间结果相同// 如下,"fuxing"返回的UUID一直为8b9b6bc3-90c8-37ef-bbef-0ed0c552718fSystem.out.println(UUID.nameUUIDFromBytes("fuxing".getBytes()));
}

优点:本地生成,没有网络消耗,性能非常高。

缺点:

  • 占用空间大,32 个字符串,128 位。
  • 不安全:版本 1 可能会造成 mac 地址泄露。
  • 无序,非自增。
  • 机器时间不对,可能造成 UUID 重复。

(二) 数据库自增 ID

实现简单,解释通过数据库表中的主键 ID 自增来生成唯一标识。如下,维护一个 MySQL 表来生成 ID。

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

当需要生成分布式 ID 时,向表中插入数据并返回主键 ID,这里 value 无含义,只是为了占位,方便插入数据。

优点:实现简单,基本满足业务需求,且天然有序。

缺点:

  • 数据库自身的单点故障和数据一致性问题。
  • 不安全,比如可根据自增来判断订单量。
  • 高并发场景可能会受限于数据库瓶颈。

那么有没有办法解决数据库自增 ID 的缺点呢?

通过水平拆分的方案,将表设置到不同的数据库中,设置不同的起始值和步长,这样可以有效的生成集群中的唯一 ID,也大大降低 ID 生成数据库操作的负载,示例如下。

unique_id_1配置:

set @@auto_increment_offset = 1;     -- 起始值
set @@auto_increment_increment = 2;  -- 步长

unique_id_2配置:

set @@auto_increment_offset = 2;     -- 起始值
set @@auto_increment_increment = 2;  -- 步长

这个还是需要根据自己的业务需求来,水平扩展的集群数量要符合自己的数据量,因为当设置的集群数量不足以满足高并发时,再次进行扩容集群会很麻烦。多台机器的起始值和步长都需要重新配置。

(三) 数据库号段模式

号段模式是当下分布式 ID 生成器的主流实现方式之一,比如美团 Leaf-segment、滴滴 Tinyid、微信序列号等都使用的该方案,下面的大厂中间件中会展开说明。

号段模式也是基于数据库的自增 ID,数据库自增 ID 是一次性获取一个分布式 ID,号段模式可以理解成从数据库批量获取 ID,然后将 ID 缓存在本地,以此来提高业务获取 ID 的效率。

例如,每次从数据库获取 ID 时,获取一个号段,如(1,1000],这个范围表示 1000 个 ID,业务应用在请求获取 ID 时,只需要在本地从 1 开始自增并返回,而不用每次去请求数据库,一直到本地自增到 1000 时,才去数据库重新获取新的号段,后续流程循环往复。

表结构可进行如下设计:

CREATE TABLE `id_generator` (`id` int(10) NOT NULL,`max_id` bigint(20) NOT NULL COMMENT '当前最大id',`step` int(10) NOT NULL COMMENT '号段的步长',`version` int(20) NOT NULL COMMENT '版本号',`biz_type` int(20) NOT NULL COMMENT '业务场景',PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

其中max_idstep用于获取批量的 ID,version是一个乐观锁,保证并发时数据的正确性。

比如,我们新增一条表数据如下。

id

max_id

step

version

biz_type

1

100

1000

0

001

然后我们可以使用该号段批量生成的 ID,当max_id = 1000,则执行 update 操作生成新的号段。新的号段的 SQL。

UPDATE 
id_generator 
SET 
max_id = #{max_id+ step}, 
version = version + 1 
WHERE 
version = #{version} AND biz_type = 001;

优点:ID 有序递增、存储消耗空间小。

缺点:

  • 数据库自身的单点故障和数据一致性问题。
  • 不安全,比如可根据自增来判断订单量。
  • 高并发场景可能会受限于数据库瓶颈。

缺点同样可以通过集群的方式进行优化,也可以如Tinyid 采用双缓存进行优化,下面的大厂中间件中会展开说明。

(四) 基于Redis

当使用数据库来生成 ID 性能不够的时候,可以尝试使用 Redis 来生成 ID。原理则是利用 Redis 的原子操作 INCR 和 INCRBY 来实现。

命令示例:

命令

说明

示例

INCR

让一个整形的key自增1

redis> SET mykey "10"

"OK"

redis> INCR mykey

(integer) 11

INCRBY

让一个整形的key自增并指定步长

redis> SET mykey "10"

"OK"

redis> INCRBY mykey 5

(integer) 15

优点:不依赖于数据库,使用灵活,支持高并发。

缺点:

  • 系统须引入 Redis 数据库。
  • 不安全,比如可根据自增来判断订单量。
  • Redis 出现单点故障问题,可能会丢数据导致 ID 重复。

(五) 雪花算法

雪花算法(Snowflake)是 twitter 公司内部分布式项目采用的 ID 生成算法。结果是一个 long 型的 ID。Snowflake 算法将 64bit 划分为多段,分开来标识机器、时间等信息,具体组成结构如下图所示:

结构说明:

结构

大小

说明

符号位

1 bit

0 表示正数,1 表示负数。

时间戳

41 bits

存储的是当前时间戳 - 开始时间戳,最长 69 年。

机器位

10 bits

前 5位 datacenterId,后 5 位 workerId ,最多表示 1024 台。

序列号

12 bits

毫秒内的流水号,意味着每个节点在每毫秒可以产生 4096 个 ID。

优点:

稳定性高,不依赖于数据库等第三方系统。

使用灵活方便,可以根据业务需求的特性来调整算法中的 bit 位。

单机上 ID 单调自增,毫秒数在高位,自增序列在低位,整体上按照时间自增排序。

缺点:

  • 强依赖机器时钟,如果机器上时钟回拨,可能导致发号重复或者服务处于不可用状态。
  • ID 可能不是全局递增,虽然 ID 在单机上是递增的。
  • Redis 出现单点故障问题,可能会丢数据导致 ID 重复。

算法实现(Java):

package util;import java.util.Date;/*** @ClassName: SnowFlakeUtil* @Author: jiaoxian* @Date: 2022/4/24 16:34* @Description:*/
public class SnowFlakeUtil {private static SnowFlakeUtil snowFlakeUtil;static {snowFlakeUtil = new SnowFlakeUtil();}// 初始时间戳(纪年),可用雪花算法服务上线时间戳的值// 1650789964886:2022-04-24 16:45:59private static final long INIT_EPOCH = 1650789964886L;// 时间位取&private static final long TIME_BIT = 0b1111111111111111111111111111111111111111110000000000000000000000L;// 记录最后使用的毫秒时间戳,主要用于判断是否同一毫秒,以及用于服务器时钟回拨判断private long lastTimeMillis = -1L;// dataCenterId占用的位数private static final long DATA_CENTER_ID_BITS = 5L;// dataCenterId占用5个比特位,最大值31// 0000000000000000000000000000000000000000000000000000000000011111private static final long MAX_DATA_CENTER_ID = ~(-1L << DATA_CENTER_ID_BITS);// dataCenterIdprivate long dataCenterId;// workId占用的位数private static final long WORKER_ID_BITS = 5L;// workId占用5个比特位,最大值31// 0000000000000000000000000000000000000000000000000000000000011111private static final long MAX_WORKER_ID = ~(-1L << WORKER_ID_BITS);// workIdprivate long workerId;// 最后12位,代表每毫秒内可产生最大序列号,即 2^12 - 1 = 4095private static final long SEQUENCE_BITS = 12L;// 掩码(最低12位为1,高位都为0),主要用于与自增后的序列号进行位与,如果值为0,则代表自增后的序列号超过了4095// 0000000000000000000000000000000000000000000000000000111111111111private static final long SEQUENCE_MASK = ~(-1L << SEQUENCE_BITS);// 同一毫秒内的最新序号,最大值可为 2^12 - 1 = 4095private long sequence;// workId位需要左移的位数 12private static final long WORK_ID_SHIFT = SEQUENCE_BITS;// dataCenterId位需要左移的位数 12+5private static final long DATA_CENTER_ID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS;// 时间戳需要左移的位数 12+5+5private static final long TIMESTAMP_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS + DATA_CENTER_ID_BITS;/*** 无参构造*/public SnowFlakeUtil() {this(1, 1);}/*** 有参构造* @param dataCenterId* @param workerId*/public SnowFlakeUtil(long dataCenterId, long workerId) {// 检查dataCenterId的合法值if (dataCenterId < 0 || dataCenterId > MAX_DATA_CENTER_ID) {throw new IllegalArgumentException(String.format("dataCenterId 值必须大于 0 并且小于 %d", MAX_DATA_CENTER_ID));}// 检查workId的合法值if (workerId < 0 || workerId > MAX_WORKER_ID) {throw new IllegalArgumentException(String.format("workId 值必须大于 0 并且小于 %d", MAX_WORKER_ID));}this.workerId = workerId;this.dataCenterId = dataCenterId;}/*** 获取唯一ID* @return*/public static Long getSnowFlakeId() {return snowFlakeUtil.nextId();}/*** 通过雪花算法生成下一个id,注意这里使用synchronized同步* @return 唯一id*/public synchronized long nextId() {long currentTimeMillis = System.currentTimeMillis();System.out.println(currentTimeMillis);// 当前时间小于上一次生成id使用的时间,可能出现服务器时钟回拨问题if (currentTimeMillis < lastTimeMillis) {throw new RuntimeException(String.format("可能出现服务器时钟回拨问题,请检查服务器时间。当前服务器时间戳:%d,上一次使用时间戳:%d", currentTimeMillis,lastTimeMillis));}if (currentTimeMillis == lastTimeMillis) {// 还是在同一毫秒内,则将序列号递增1,序列号最大值为4095// 序列号的最大值是4095,使用掩码(最低12位为1,高位都为0)进行位与运行后如果值为0,则自增后的序列号超过了4095// 那么就使用新的时间戳sequence = (sequence + 1) & SEQUENCE_MASK;if (sequence == 0) {currentTimeMillis = getNextMillis(lastTimeMillis);}} else { // 不在同一毫秒内,则序列号重新从0开始,序列号最大值为4095sequence = 0;}// 记录最后一次使用的毫秒时间戳lastTimeMillis = currentTimeMillis;// 核心算法,将不同部分的数值移动到指定的位置,然后进行或运行// <<:左移运算符, 1 << 2 即将二进制的 1 扩大 2^2 倍// |:位或运算符, 是把某两个数中, 只要其中一个的某一位为1, 则结果的该位就为1// 优先级:<< > |return// 时间戳部分((currentTimeMillis - INIT_EPOCH) << TIMESTAMP_SHIFT)// 数据中心部分| (dataCenterId << DATA_CENTER_ID_SHIFT)// 机器表示部分| (workerId << WORK_ID_SHIFT)// 序列号部分| sequence;}/*** 获取指定时间戳的接下来的时间戳,也可以说是下一毫秒* @param lastTimeMillis 指定毫秒时间戳* @return 时间戳*/private long getNextMillis(long lastTimeMillis) {long currentTimeMillis = System.currentTimeMillis();while (currentTimeMillis <= lastTimeMillis) {currentTimeMillis = System.currentTimeMillis();}return currentTimeMillis;}/*** 获取随机字符串,length=13* @return*/public static String getRandomStr() {return Long.toString(getSnowFlakeId(), Character.MAX_RADIX);}/*** 从ID中获取时间* @param id 由此类生成的ID* @return*/public static Date getTimeBySnowFlakeId(long id) {return new Date(((TIME_BIT & id) >> 22) + INIT_EPOCH);}public static void main(String[] args) {SnowFlakeUtil snowFlakeUtil = new SnowFlakeUtil();long id = snowFlakeUtil.nextId();System.out.println(id);Date date = SnowFlakeUtil.getTimeBySnowFlakeId(id);System.out.println(date);long time = date.getTime();System.out.println(time);System.out.println(getRandomStr());}}

四、大厂中间件

(一) 美团 Leaf

Leaf 的官方文档,简介和特性可访问了解,这里我将对 Leaf 的两种方案,Leaf segment 和 Leaf-snowflake 进行。

1. Leaf segment

基于数据库号段模式的 ID 生成方案,上面我们介绍到普通的号段模式有一些缺点:

  • 数据库自身的单点故障和数据一致性问题。
  • 不安全,比如可根据自增来判断订单量。
  • 高并发场景可能会受限于数据库瓶颈。

那么 Leaf 是如何做的呢?Leaf 采用了预分发的方式生成ID,也就是在 DB 之上挂 n 个 Leaf Server,每个Server启动时,都会去 DB 拿固定长度的 ID List。

这样就做到了完全基于分布式的架构,同时因为ID是由内存分发,所以也可以做到很高效,处理流程图如下:

其中:

  • Leaf Server 1:从 DB 加载号段[1,1000]。
  • Leaf Server 2:从 DB 加载号段[1001,2000]。
  • Leaf Server 3:从 DB 加载号段[2001,3000]。

用户通过轮询的方式调用 Leaf Server 的各个服务,所以某一个 Client 获取到的ID序列可能是:1,1001,2001,2,1002,2002。当某个 Leaf Server 号段用完之后,下一次请求就会从 DB 中加载新的号段,这样保证了每次加载的号段是递增的。

为了解决在更新DB号段的时出现的耗时和阻塞服务的问题,Leaf 采用了异步更新的策略,同时通过双缓存的方式,保证无论何时DB出现问题,都能有一个 Buffer 的号段可以正常对外提供服务,只要 DB 在一个 Buffer 的下发的周期内恢复,就不会影响整个 Leaf 的可用性。

除此之外,Leaf 还通过动态调整步长,解决由于步长固定导致的缓存中的 ID 被过快消耗问题,以及步长设置过大导致的号段 ID 跨度过大问题,具体公式可去官方文档中了解。

对于数据一致性问题,Leaf 目前是通过中间件 Zebra 加 MHA 做的主从切换。

2. Leaf Snowflake

Leaf-snowflake 方案沿用 Snowflake 方案的 bit 位设计。

对于 workerID 的分配:当服务集群较小时,通过配置即可;服务集群较大时,基于 zookeeper 持久顺序节点的特性引入 zookeeper 组件配置 workerID。架构如下图所示:

(二) 百度 UidGenerator

开源地址

UidGenerator 方案是基于 Snowflake 算法的 ID 生成器,其对雪花算法的 bit 位的分配做了微调。

结构说明(参数可在 Spring Bean 配置中进行配置):

结构

大小

说明

符号位

1 bit

最高位始终是 0。

增量秒

28 bits

表示自客户纪元(2016-05-20)以来的增量秒。最大时间为 8.7 年。

工作节点

22 bits

表示工作节点 ID,最大值为 4.2 百万个。

序列号

13 bits

表示一秒钟内的序列,默认情况下每秒最多 8192 个。

UidGenerator 方案包含两种实现方式,DefaultUidGenerator 和 CachedUidGenerator ,性能要求高的情况下推荐 CachedUidGenerator。

(三) 滴滴 Tinyid

Tinyid 官方文档

Tinyid 方案是在 Leaf-segment 的算法基础上升级而来,不仅支持了数据库多主节点模式,还提供了 tinyid-client 客户端的接入方式,使用起来更加方便。

Tinyid 也是采用了异步加双缓存策略,首先可用号段加载到内存中,并在内存中生成 ID,可用号段在首次获取 ID 时加载,号段用到一定程度的时候,就去异步加载下一个号段,保证内存中始终有可用号段,则可避免性能波动。

实现原理如下所示:

五、结语

本文对分布式 ID 以及其场景的生成方案做了介绍,还针对一下大厂的中间件进行简单分析,中间件的接入代码本文并没有做详细介绍,但是官方文档的链接都帖子了每个子标题下,其中都有详细介绍。

文中还针对每个生成方案的优缺点作出了说明,具体的使用可针对优缺点加上业务需求来进行选型。


参考:

[1] 腾讯技术工程. 分布式唯一 ID 生成方案浅谈.

[2] 孟斯特. UUID 介绍.

[3] 文丑颜不良啊. 雪花算法(SnowFlake).

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

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

相关文章

Python的三种方式显示图片

from PIL import Image import numpy as np im Image.open("img.png") #方法一&#xff1a;使用PIL库显示图片 a np.array(im) imImage.fromarray(a) im.show() import matplotlib.pyplot as plt #方法二&#xff1a;使用matplotlib库显示图片 plt.imshow(a) plt.s…

人工智能中的监督学习和无监督学习

欢迎来到 Papicatch的博客 目录 &#x1f349;引言 &#x1f349;监督学习 &#x1f348;基本思想 &#x1f348;具体过程 &#x1f34d;数据收集 &#x1f34d;数据预处理 &#x1f34d;模型选择 &#x1f34d;模型训练 &#x1f34d;模型评估 &#x1f34d;模型部署…

示例:WPF中绑定枚举到ComboBox想显示成中文或自定义名称如何实现

一、目的&#xff1a;在开发过程中绑定的枚举不想显示成英文字段怎么办&#xff0c;这里通过TypeConverter的方式来实现绑定的枚举从定义的特性中读取 二、实现 首先定义如下枚举 [TypeConverter(typeof(DisplayEnumConverter))]public enum MyEnum{[Display(Name "无&q…

Flink 资源静态调度

本内容是根据 Flink 1.18.0-Scala_2.12 版本源码梳理而来。本文主要讲述任务提交时&#xff0c;为 Task 分配资源的过程。 以下是具体步骤讲解&#xff1a; TaskManager 资源注册 TaskManager 在启动时&#xff0c;会向 ResourceManager 注册资源。ResourceManager 会将 Tas…

斯坦福CS229机器学习中文速查笔记.pdf

斯坦福CS229是一门经典的机器学习课程&#xff0c;算是机器学习领域的明星课&#xff0c;相信不少人在B站上看过这门课的视频。 这门课主要介绍了机器学习和统计模式识别。内容包括&#xff1a;监督学习&#xff08;生成/鉴别学习&#xff0c;参数/非参数学习&#xff0c;神经…

JavaSE 面向对象程序设计高级 方法引用 2024详解

在编程中&#xff0c;方法引用&#xff08;Method Reference&#xff09;是一种技术&#xff0c;它让你能够直接引用一个现有的函数或方法&#xff0c;而无需通过对象实例来调用。这种方法在函数式编程和高阶函数中非常有用&#xff0c;因为它提供了简洁的方式来传递函数行为&a…

业务谈判的过程中多让客户做选择

之前还在工厂的时候&#xff0c;开分享会&#xff0c;经理会反复强调的一个跟进思路就是一定要学会让客户跟着我们的节奏走&#xff0c;而不是被客户牵着鼻子走。 前者会让客户顺着我们设计好的谈判路径&#xff0c;把客户引导到我们想要的结果上&#xff0c;业务员是主动角色…

模版与策略模式

一&#xff0c;怎么选择 如果需要固定的执行流程&#xff0c;选模版 如果不需要固定的执行流程&#xff0c;只需要对一个方法做具体抽象&#xff0c;选策略 参考文章&#xff1a; 常用设计模式汇总&#xff0c;告诉你如何学习设计模式 二&#xff0c;常用写法 子类 exten…

穿越时空的家书——黑夫与惊的不朽传奇

1975年&#xff0c;湖北云梦县睡虎地的一次考古发掘&#xff0c;揭开了一段尘封的历史&#xff0c;两枚刻有527个字的木牍&#xff0c;成为了我国最早的家书实物。这两枚木牍&#xff0c;记录了战国时期秦国士兵黑夫和惊的家书。 两件木犊出土时被放置在墓地陪葬器物箱子里的中…

Nuxt3 实战 (十):使用 Supabase 实现 RESTful 风格 API 接口

前言 本篇文章我们来使用 Supabase 实现 RESTful 风格的 API 接口&#xff0c;以此来实现网站分类和子站点的 CURD 功能。 表设计 这里需要用到两张表&#xff1a; ds_categorys&#xff1a;存储网站分类 列名类型备注iduuid主键&#xff0c;分类 idnametext分类名称desct…

python 魔术方法备忘录

python 魔术方法备忘录 网上收集了一些&#xff0c;列出了比较常用的&#xff0c;特别是第一张。 Python中的魔术方法&#xff08;Magic Methods&#xff09;&#xff0c;也被称为特殊方法&#xff08;Special Methods&#xff09;或双下划线方法&#xff08;Dunder Methods&a…

渗透测试之存储型跨站脚本攻击(高危)

一、定义 跨站脚本攻击&#xff0c;指的是恶意用户往web页面里插入恶意HTML代码。当普通用户访问该web页面&#xff0c;嵌入其中的HTML代码会被执行&#xff0c;从而达到破坏的效果。 二、风险定级 高危 三、可输入的HTML标签示例 图片标签 <img src"#"> 超…

有监督学习——决策树、集成学习

1. 决策树 熵 在热力学中&#xff0c;熵&#xff08;entropy&#xff09;被用来衡量系统的不稳定程度。香农在论文《通信的数学原理》中提出信息熵的概念&#xff0c;目的是_量化数字信息的价值_。 信息熵的定义 香农提出的量化信息方式&#xff1a; \[H(P_1,P_2,\cdots P…

吴恩达机器学习作业ex3:多类分类和前馈神经网络(Python实现)详细注释

文章目录 1 多类分类1.1数据集1.2 数据可视化1.3 向量化逻辑回归1.3.1 向量化代价函数1.3.2 矢量化梯度下降以及正则化表达1.4 一对多分类 2.神经网络2.1模型表示 总结&#xff08;自己训练求解参数全流程&#xff09; 1 多类分类 在本练习中&#xff0c;您将使用逻辑回归和神…

Redis学习|Jedis、SpringBoot整合Redis

Jedis 我们要使用Java 来操作 Redis,知其然并知其所以然&#xff0c;授人以渔!学习不能急躁&#xff0c;慢慢来会很快!什么是Jedis 是 Redis 官方推荐的java连接开发工具!使用java 操作Redis 中间件!如果你要使用 java操作redis&#xff0c;那么一定要对Jedis 十分的熟悉! 1、…

MySQL之复制(五)

复制 复制的原理 复制文件 3.master.info 这个文件用于保存备库连接到主库所需要的信息&#xff0c;格式为纯文本(每行一个值)&#xff0c;不同的MySQL版本&#xff0c;其记录的信息也可能不同。此文件不能删除&#xff0c;否则备库在重启后无法连接到主库。这个文件以文本的…

电脑ffmpeg.dll丢失原因解析,找不到ffmpeg.dll的5种解决方法

在数字化时代&#xff0c;多媒体文件的处理已经成为我们日常生活和工作中不可或缺的一部分。在计算机使用过程中&#xff0c;丢失ffmpeg.dll文件是一个特定但常见的问题&#xff0c;尤其是对于那些经常处理视频编解码任务的用户来说。下面小编讲全面分析ffmpeg.dll丢失原因以及…

Python数据分析与建模库之从入门到四大库(Numpy、Pandas、Matplotl、Seaborn )教学课程下载

第一阶段课程-Python快速入门&#xff1a; 含&#xff1a;1.系列课程环境配置&#xff1b;2.Python快速入门&#xff1b;3.变量类型&#xff1b;4.LIST基础&#xff1b;5.List索引&#xff1b;6.循环结构&#xff1b;7.判断结构&#xff1b;8.字典&#xff1b;9.文件处理&#…

哪些好用的AI绘画生成软件?建议你试试这四款

哪些好用的AI绘画生成软件&#xff1f;随着人工智能技术的飞速发展&#xff0c;AI绘画生成软件逐渐走入大众的视野&#xff0c;为艺术创作领域带来了革命性的变革。今天&#xff0c;就让我们一起探索四款备受推崇的AI绘画生成软件&#xff0c;看看它们如何以独特的魅力&#xf…

202483读书笔记|《牵牛花浮世无篱笆:千代尼俳句250》——被红叶染红的只有一侧山坡之山 啊,单恋

202483读书笔记|《牵牛花浮世无篱笆&#xff1a;千代尼俳句250》——被红叶染红的只有一侧山坡之山 啊&#xff0c;单恋 春之句夏之句秋之句冬之句 历史读过的俳句列表: 202318读书笔记|《芭蕉芜村一茶&#xff1a;俳句三圣新译300》——樱花——让一整个春夜亮起来&#xff0…