分布式 ID 生成方案实战指南:从选型到落地的全场景避坑手册(二) - 指南

news/2025/9/24 18:18:34/文章来源:https://www.cnblogs.com/yxysuanfa/p/19109747

三、方案 2:数据库分段 ID—— 分库分表场景的稳定选择

3.1 问题场景:电商订单分库分表

某电商订单系统,日均订单 100 万,分 3 个订单库(order_db_1order_db_2order_db_3),每个库分 10 个表(order_0-order_9)。需生成全局唯一的订单 ID,且支持按 ID 排序(查询用户最近订单)。

3.2 方案原理:从 “单库自增” 到 “分段自增”

数据库分段 ID 基于 “步长 + 偏移量” 实现,核心思路:

  1. 多个数据库实例(或表)共享自增 ID,但设置不同 “步长”(如 3 个库,步长 = 3);

  2. 每个库的 ID 起始值不同(偏移量),确保 ID 不重复;

  3. 例如:库 1 生成 ID=1、4、7…,库 2 生成 ID=2、5、8…,库 3 生成 ID=3、6、9…。

3.2.1 架构图:数据库分段 ID 生成架构

在这里插入图片描述

3.3 实战代码:数据库分段 ID 配置与实现

3.3.1 1. 数据库配置(MySQL 分库设置)

为 3 个订单库分别配置自增步长和起始值:

\-- order\_db\_1(库1):起始值1,步长3
ALTER TABLE order\_0 MODIFY COLUMN id BIGINT AUTO\_INCREMENT PRIMARY KEY;
ALTER TABLE order\_0 AUTO\_INCREMENT = 1;
SET GLOBAL auto\_increment\_increment = 3;
-- 全局步长(所有表生效)
SET GLOBAL auto\_increment\_offset = 1;
-- 全局起始偏移量
\-- order\_db\_2(库2):起始值2,步长3
ALTER TABLE order\_0 MODIFY COLUMN id BIGINT AUTO\_INCREMENT PRIMARY KEY;
ALTER TABLE order\_0 AUTO\_INCREMENT = 2;
SET GLOBAL auto\_increment\_increment = 3;
SET GLOBAL auto\_increment\_offset = 2;
\-- order\_db\_3(库3):起始值3,步长3
ALTER TABLE order\_0 MODIFY COLUMN id BIGINT AUTO\_INCREMENT PRIMARY KEY;
ALTER TABLE order\_0 AUTO\_INCREMENT = 3;
SET GLOBAL auto\_increment\_increment = 3;
SET GLOBAL auto\_increment\_offset = 3;
3.3.2 2. 分库路由实现(Java + MyBatis)

通过 MyBatis 的Interceptor实现订单按用户 ID 路由到对应库:

import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.plugin.\*;
import java.sql.Connection;
import java.util.Properties;
// 分库路由插件:按用户ID取模路由到3个库
@Intercepts({
@Signature(type = StatementHandler.class, method =
"prepare", args = {
Connection.class,
Integer.class
})
})
public class OrderShardingPlugin
implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
// 1. 获取当前执行的SQL(如INSERT INTO order\_0 (user\_id, ...) VALUES (123, ...))
StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
String sql = statementHandler.getBoundSql().getSql();
// 2. 提取用户ID(假设SQL中user\_id是第1个参数)
Long userId = (Long) statementHandler.getBoundSql().getParameterObject();
// 3. 按用户ID取模3,确定路由库(0→db1,1→db2,2→db3)
int dbIndex = (int) (userId % 3);
String targetDb = "order\_db\_" + (dbIndex + 1);
// 4. 修改数据库连接(切换到目标库)
Connection connection = (Connection) invocation.getArgs()\[0];
connection.setCatalog(targetDb);
// 切换数据库
// 5. 执行原SQL(此时会在目标库生成自增ID)
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
}
}
3.3.3 3. 进阶优化:号段模式(减少数据库访问)

数据库分段 ID 的痛点是 “每次生成 ID 都需访问数据库”,高并发下数据库压力大。优化方案:号段模式(预申请一段 ID 缓存到本地,用完再申请)。

号段模式原理

  • 从数据库申请一段 ID(如start=1,end=1000),缓存到订单服务本地;

  • 服务生成 ID 时从本地缓存取,无需访问数据库;

  • 当本地 ID 用到阈值(如 800)时,异步申请下一段 ID(start=1001,end=2000),避免断流。

号段模式实战代码(Java + Spring Boot)

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.concurrent.atomic.AtomicLong;
@Service
public class OrderIdGenerator
{
// 本地缓存的号段(start:起始ID,end:结束ID)
private volatile long currentStart;
private volatile long currentEnd;
// 原子类:确保多线程下ID生成线程安全
private AtomicLong currentId;
// 阈值:当用到80%时申请下一段(避免断流)
private static final double THRESHOLD = 0.8;
@Autowired
private IdSegmentMapper idSegmentMapper;
// MyBatis mapper,操作号段表
// 初始化号段(服务启动时调用)
public void initSegment() {
// 从数据库申请第一段号段(假设order业务的segment\_type=1)
IdSegment segment = idSegmentMapper.getNextSegment(1);
currentStart = segment.getStart();
currentEnd = segment.getEnd();
currentId = new AtomicLong(currentStart);
}
// 生成订单ID(核心方法)
public Long generateOrderId() {
// 1. 原子递增获取当前ID
long id = currentId.getAndIncrement();
// 2. 检查是否达到阈值,若达到则异步申请下一段
if (id >= currentStart + (currentEnd - currentStart) \* THRESHOLD) {
asyncApplyNextSegment();
}
// 3. 检查ID是否超出当前号段(异常情况,如申请下一段失败)
if (id > currentEnd) {
throw new RuntimeException("订单ID生成失败:号段耗尽");
}
return id;
}
// 异步申请下一段号段(避免阻塞ID生成)
private void asyncApplyNextSegment() {
// 用线程池异步执行(避免单线程阻塞)
IdGeneratorThreadPool.execute(() ->
{
// 从数据库申请下一段(数据库通过乐观锁确保号段不重复)
IdSegment nextSegment = idSegmentMapper.getNextSegment(1);
// 更新本地号段
currentStart = nextSegment.getStart();
currentEnd = nextSegment.getEnd();
// 重置currentId(从新start开始)
currentId = new AtomicLong(currentStart);
});
}
}

号段表结构(MySQL)

CREATE TABLE id\_segment (
id BIGINT AUTO\_INCREMENT PRIMARY KEY,
segment\_type INT NOT NULL COMMENT '业务类型(1=订单,2=物流)',
start BIGINT NOT NULL COMMENT '号段起始ID',
end BIGINT NOT NULL COMMENT '号段结束ID',
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
create\_time DATETIME NOT NULL DEFAULT CURRENT\_TIMESTAMP,
UNIQUE KEY uk\_segment\_type (segment\_type)
) COMMENT '号段表';
\-- 初始化订单业务号段(start=1end=1000INSERT INTO id\_segment (segment\_type, start, end, version) VALUES (1, 1, 1000, 0);

号段获取 SQL(MyBatis Mapper)

\<
update id="getNextSegment">
UPDATE id\_segment
SET start = end + 1,
end = end + 1000, -- 每次申请1000个ID(可根据并发调整)
version = version + 1
WHERE segment\_type = #{segmentType}
AND version = #{version} -- 乐观锁:避免多服务节点重复申请
\<
/update>

3.4 故障案例:号段模式 “ID 重复” 排查

3.4.1 问题背景

某支付系统用号段模式生成支付流水号,部署 3 个服务节点。大促期间发现 2 笔支付流水 ID 重复(ID=1567),导致下游对账系统报错。

3.4.2 根因分析
  1. 号段表的version字段未加乐观锁,3 个服务节点同时申请号段时,均执行UPDATE id_segment SET start=end+1...

  2. 由于无版本控制,3 个节点均获取到相同号段(start=1501,end=2500),导致生成重复 ID;

  3. 服务节点本地缓存未设置 “防重复校验”,无法发现重复号段。

3.4.3 解决方案
  1. 在号段表UPDATE语句中添加乐观锁(WHERE version = #{version}),确保同一时间只有一个节点能申请号段;

  2. 服务节点申请号段后,记录本地号段的startend,并与其他节点通过配置中心(如 Nacos)同步,避免重复;

  3. 生成 ID 时添加 “节点标识”(如在 ID 末尾加 1 位节点编号),即使号段重复,ID 也不会重复(如1567-11567-2)。

3.5 避坑总结

适用场景:分库分表、中低并发(日均 100 万以内)、需强有序 ID 的场景(订单 / 物流);

不适用场景:超高并发(秒杀 10 万 QPS)、无数据库依赖的场景;

⚠️ 必避坑点

  1. 分库步长需等于库数量(如 3 个库→步长 3),避免 ID 断层;

  2. 号段模式必须用乐观锁控制号段申请,防止重复;

  3. 号段大小需合理(并发高→号段大,如 1 万 / 段;并发低→号段小,如 1 千 / 段),避免内存浪费或频繁申请。

四、方案 3:雪花算法(Snowflake)—— 高并发场景的首选

4.1 问题场景:秒杀系统订单 ID 生成

某电商秒杀系统,峰值 QPS 达 5 万,需生成全局唯一、有序、高性能的订单 ID,且支持水平扩容(新增服务器节点)。此时雪花算法是最优选择。

4.2 方案原理:雪花算法的结构设计

雪花算法生成 64 位 Long 类型 ID,结构如下(从高位到低位):

1位(符号位):固定为0(确保ID为正数)
41位(时间戳):当前时间戳 - 起始时间戳(单位:毫秒),可支持约69年(2^41 / 365 / 24 / 3600 / 10006910位(机器ID):分为“数据中心ID(5位)+ 机器节点ID(5位)”,支持32个数据中心×32个节点=1024个节点
12位(序列号):同一毫秒内的ID序号,支持每毫秒生成4096个ID(2^12=4096
4.2.1 结构示意图:雪花算法 ID 组成

在这里插入图片描述

4.3 实战代码:雪花算法实现(含时钟回拨处理)

雪花算法的核心痛点是 “时钟回拨”(服务器时钟因同步导致时间倒退,生成重复 ID),以下代码包含时钟回拨解决方案。

4.3.1 雪花算法核心实现(Java)
import java.util.concurrent.atomic.AtomicInteger;
public class SnowflakeIdGenerator
{
// 1. 常量配置
private static final long SIGN\_BIT = 0L;
// 符号位(固定0)
private static final int TIMESTAMP\_BITS = 41;
// 时间戳位数
private static final int DATA\_CENTER\_BITS = 5;
// 数据中心ID位数
private static final int WORKER\_BITS = 5;
// 机器节点ID位数
private static final int SEQUENCE\_BITS = 12;
// 序列号位数
// 2. 位移量计算
private static final long DATA\_CENTER\_SHIFT = SEQUENCE\_BITS + WORKER\_BITS;
private static final long WORKER\_SHIFT = SEQUENCE\_BITS;
private static final long TIMESTAMP\_SHIFT = SEQUENCE\_BITS + WORKER\_BITS + DATA\_CENTER\_BITS;
// 3. 最大值限制(避免溢出)
private static final long MAX\_DATA\_CENTER\_ID = (1L << DATA\_CENTER\_BITS) - 1;
// 31
private static final long MAX\_WORKER\_ID = (1L << WORKER\_BITS) - 1;
// 31
private static final long MAX\_SEQUENCE = (1L << SEQUENCE\_BITS) - 1;
// 4095
// 4. 起始时间戳(自定义:2020-01-01 00:00:00)
private static final long START\_TIMESTAMP = 1577836800000L;
// 5. 实例变量(数据中心ID、机器ID、序列号、上次生成时间戳)
private final long dataCenterId;
private final long workerId;
private final AtomicInteger sequence = new AtomicInteger(0);
private volatile long lastTimestamp = -1L;
// 6. 构造方法(校验数据中心ID和机器ID)
public SnowflakeIdGenerator(long dataCenterId, long workerId) {
if (dataCenterId <
0 || dataCenterId > MAX\_DATA\_CENTER\_ID) {
throw new IllegalArgumentException("数据中心ID超出范围(0-31):" + dataCenterId);
}
if (workerId <
0 || workerId > MAX\_WORKER\_ID) {
throw new IllegalArgumentException("机器节点ID超出范围(0-31):" + workerId);
}
this.dataCenterId = dataCenterId;
this.workerId = workerId;
}
// 7. 生成ID(核心方法,线程安全)
public synchronized long generateId() {
// 7.1 获取当前时间戳(毫秒)
long currentTimestamp = System.currentTimeMillis();
// 7.2 处理时钟回拨(当前时间 < 上次时间)
if (currentTimestamp < lastTimestamp) {
// 方案1:等待时钟追赶(适合回拨时间短,如<10ms)
long waitTime = lastTimestamp - currentTimestamp;
if (waitTime <= 10) {
// 回拨<10ms,等待
try {
Thread.sleep(waitTime);
currentTimestamp = System.currentTimeMillis();
} catch (InterruptedException e) {
throw new RuntimeException("时钟回拨等待被中断", e);
}
} else {
// 方案2:回拨时间长,抛出异常(避免重复ID)
throw new RuntimeException("时钟回拨超出阈值(10ms):当前时间=" + currentTimestamp + ",上次时间=" + lastTimestamp);
}
}
// 7.3 同一毫秒内:序列号递增
if (currentTimestamp == lastTimestamp) {
sequence.compareAndSet(MAX\_SEQUENCE, 0);
// 序列号超出4095,重置为0
int currentSequence = sequence.getAndIncrement();
// 同一毫秒内序列号耗尽(理论上4096个/ms,高并发下可能发生)
if (currentSequence > MAX\_SEQUENCE) {
throw new RuntimeException("同一毫秒内ID生成超出上限(4096个)");
}
} else {
// 7.4 不同毫秒:序列号重置为0
sequence.set(0);
}
// 7.5 更新上次时间戳
lastTimestamp = currentTimestamp;
// 7.6 拼接ID(按位或运算)
return SIGN\_BIT
\| ((currentTimestamp - START\_TIMESTAMP) << TIMESTAMP\_SHIFT)
\| (dataCenterId << DATA\_CENTER\_SHIFT)
\| (workerId << WORKER\_SHIFT)
\| sequence.get();
}
// 测试:生成10个ID
public static void main(String\[] args) {
SnowflakeIdGenerator generator = new SnowflakeIdGenerator(1, 2);
// 数据中心1,机器2
for (int i = 0; i <
10; i++) {
System.out.println(generator.generateId());
// 输出示例:1234567890123456789(64位Long,有序递增)
}
}
}
4.3.2 机器 ID 分配方案(ZooKeeper 实现)

雪花算法的机器 ID(数据中心 ID + 节点 ID)需手动分配,避免重复。生产环境推荐用 ZooKeeper 自动分配:

  1. 服务启动时,向 ZooKeeper 的/snowflake/worker节点下创建临时顺序节点(如/snowflake/worker/worker-0000000001);

  2. 取节点的顺序号(如 1)作为机器 ID,确保全局唯一;

  3. 服务下线时,临时节点自动删除,释放机器 ID。

ZooKeeper 机器 ID 分配代码(Java + Curator)

import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.CreateMode;
public class ZkWorkerIdAllocator
{
// ZooKeeper地址(集群用逗号分隔)
private static final String ZK\_ADDRESS = "192.168.1.100:2181,192.168.1.101:2181";
// 机器ID节点路径
private static final String WORKER\_ID\_PATH = "/snowflake/worker/worker-";
// 会话超时时间
private static final int SESSION\_TIMEOUT = 5000;
// 连接超时时间
private static final int CONNECTION\_TIMEOUT = 3000;
// 分配机器ID(返回0-31的ID)
public static int allocateWorkerId() throws Exception {
// 1. 创建ZooKeeper客户端
CuratorFramework client = CuratorFrameworkFactory.builder()
.connectString(ZK\_ADDRESS)
.sessionTimeoutMs(SESSION\_TIMEOUT)
.connectionTimeoutMs(CONNECTION\_TIMEOUT)
.retryPolicy(new ExponentialBackoffRetry(1000, 3)) // 重试策略
.build();
client.start();
// 2. 创建临时顺序节点(自动释放)
String nodePath = client.create()
.creatingParentsIfNeeded() // 父节点不存在则创建
.withMode(CreateMode.EPHEMERAL\_SEQUENTIAL) // 临时顺序节点
.forPath(WORKER\_ID\_PATH);
// 3. 提取机器ID(如nodePath=/snowflake/worker/worker-0000000001 → ID=1)
String nodeName = nodePath.substring(WORKER\_ID\_PATH.length());
int workerId = Integer.parseInt(nodeName);
// 4. 校验机器ID范围(0-31)
if (workerId <
0 || workerId >
31) {
throw new RuntimeException("机器ID超出范围(0-31):" + workerId);
}
System.out.println("ZooKeeper分配机器ID成功:" + workerId);
return workerId;
}
// 测试:分配机器ID
public static void main(String\[] args) throws Exception {
allocateWorkerId();
}
}

4.4 故障案例:雪花算法时钟回拨导致 ID 重复

4.4.1 问题背景

某支付系统用雪花算法生成支付流水号,部署 10 个服务节点。某天凌晨 2 点,由于服务器同步 NTP 时间,2 个节点的时钟回拨了 50ms,导致生成 100 + 个重复流水号,引发对账异常。

4.4.2 根因分析
  1. 雪花算法依赖服务器本地时间戳,时钟回拨后,currentTimestamp < lastTimestamp

  2. 原代码未处理时钟回拨,直接用回拨后的时间戳生成 ID,导致与回拨前的 ID 重复;

  3. 2 个节点的机器 ID 相同(手动分配时配置错误),进一步加剧重复问题。

4.4.3 解决方案
  1. 在雪花算法中添加时钟回拨处理(如代码中 “等待时钟追赶” 或 “抛出异常”);

  2. 改用 ZooKeeper 自动分配机器 ID,避免手动配置错误;

  3. 服务启动时校验本地时间与 NTP 服务器时间差,若差 > 10ms 则拒绝启动;

  4. 生成 ID 后添加 “冗余校验”(如存入 Redis,检查 ID 是否已存在,避免重复)。

4.5 避坑总结

适用场景:高并发(秒杀 / 大促)、需有序 ID、水平扩容的场景(订单 / 支付 / 秒杀);

不适用场景:无 ZooKeeper / 配置中心、对时钟敏感的场景;

⚠️ 必避坑点

  1. 必须处理时钟回拨(核心风险点),避免 ID 重复;

  2. 机器 ID 需全局唯一(推荐用 ZooKeeper/Nacos 分配);

  3. 起始时间戳需固定(避免不同服务节点起始时间不同导致 ID 重复);

  4. 高并发下需注意 “同一毫秒序列号耗尽”(可增大序列号位数或拆分业务)。

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

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

相关文章

公钥密码与可证安全概述

公钥密码与可证安全概述 Generated By 小艺(doge公钥密码与可证安全摘要:本报告系统阐述了公钥密码学与可证安全理论的核心关系及其演进。公钥密码学基于非对称密钥机制(公钥加密、私钥解密/签名),其安全性根植于…

溧阳 招网站开发深圳网站建设制作订做

上一课我们了解了Dreamweaver的工作界面&#xff0c;从这节课开始&#xff0c;我们由浅入深的开始学习网页的制作。首先打开Dreamweaver&#xff0c;制作网页首先要新建一个文档&#xff0c;就像我们写文稿需要一张纸一样。只有有了载体&#xff0c;你才能在上面进行创作&#…

Python标准库enum模块实现枚举类

一、介绍 Python标准库中的enum模块提供了一种创建枚举类型的方式,枚举类型是一种特殊的类,用于定义一组命名的常量。以下是enum模块的主要特点和实现方式:‌基本用法‌:使用Enum类可以定义一个枚举类型,枚举成员…

程序员的旅行神器:用AI提示词工程解决选择困难症

作为程序员,我们习惯了用技术解决工作中的复杂问题,但面对旅行规划时却常常陷入选择困难。今天分享一个实用的AI工具——专业的旅行目的地推荐提示词,让我们把解决问题的技术思维应用到生活中。作为程序员,我们习惯…

修改人大金仓V8数据库时间

sed -i "s#^timezone.*#timezone = Asia/Shanghai#" /home/kingbase/userdata/data/kingbase.conf控红色部分替换为数据库配置文件位置,在数据库中执行 SHOW config_file; 可查看位置路径然后执行 sed …

BUUCTF-babyheap_0ctf_2017题解(含详细过程与思路分析)

BUUCTF-babyheap_0ctf_2017题解(含详细过程与思路分析)一、题目来源 BUUCTF-Pwn-babyheap_0ctf_2017二、信息搜集 将题目给的可执行文件丢入Linux虚拟机中 通过file命令查看文件类型:通过checksec命令查看本题采用的…

洛阳制作网站的公司哪家好16岁做分期网站

力扣题目链接 编写一个程序&#xff0c;通过填充空格来解决数独问题。 一个数独的解法需遵循如下规则&#xff1a; 数字 1-9 在每一行只能出现一次。 数字 1-9 在每一列只能出现一次。 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。 空白格用 . 表示。 解题思路 …

自己电脑做网站教程制作京东网站建设

技术框架 框架作用 前端 React构建用户界面的 JavaScript 库ReduxJavaScript 应用的状态管理库,管理组件间的状态共享Webpack前端资源打包工具BabelJavaScript 编译器D3.js数据可视化的 JavaScript 库后端 Flask轻量级的 Python web 框架,处理 HTTP 请求、路由以及其他 Web 服…

做的很好的画册网站运维工程师主要做什么

CSS样式如何在页面中使用&#xff0c;包含三种方式&#xff1a;行内样式、内嵌式样式表、链接式样式表。 1、行内样式 行内样式是比较直接的一种样式&#xff0c;直接定义在 HTML 标签之内&#xff0c;并通过 style 属性来实现。这种方式比较容易学习&#xff0c;但是灵活性不…

油猴(Tampermonkey)插件从安装到使用

一、安装油猴插件 首先,打开chrome浏览器-设置-扩展程序,然后打开谷歌商店,搜索油猴插件,安装到浏览器上。接着,回到拓展程序-我的拓展程序页面,就可以看到我们刚才安装好的油猴插件了! 最后,启动插件,将浏览…

【EF Core】框架底层的数据库连接管理

在开始水文章前,老周补充说明一下,在前一篇水文中,老周扯了跟 Owned 关系相关的一些话题,这里补充一句:Owned 关系是不存在“多对多”的,如果 A 实体 Own B 实体,那么,A 对 B 是完全占有,B只属于A,数据不共享…

iNeuOS工业互联网操作系统,更新计量数据处理方式和在线报表,实现能源管理基础功能

本次iNeuOS工业互联网操作系统升级内容主要包括:对计量数据点的处理方式和在线报表增加时间格式化配置。主要应用场景针对能源管理的在线监测、数据处理和在线(日、月、年)报表的整体应用,完成能源管理系统的基础应…

React 展示Markdown内容 - 教程

React 展示Markdown内容 - 教程pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco&quo…

江门网站推广哪家好长沙建网

使用Benchmark.NET对C# 代码进行基准测试的简介在我以前的文章中[10]&#xff0c;我介绍了该系列文章[11]&#xff0c;在其中我将分享我的经验&#xff0c;同时了解C&#xff03;和.NET Core&#xff08;corefx&#xff09;框架的新性能。在本文中&#xff0c;我想着重于对现有…

西电PCB设计指南第4章学习笔记

西电PCB设计指南第4章学习笔记 四、电源路径分析电源路径的基本原理回路路径与感抗 感抗与路径有关,信号线看似很短很直,但是引入了较大的环路感抗。多路信号线的环路之间的互感,造成相互干扰 所以,我们不能但按照…

图像处理去除噪点验证码的识别逻辑实践

验证码设计中常见的防护手段之一就是加入随机噪点,让字符边缘不清晰,从而干扰自动识别。本文介绍一种基于图像滤波与形态学操作的处理流程,帮助提取出清晰的字符区域。 一、问题分析 噪点验证码的典型特征是: 图像…

读取zip包中的文件

private List<String> readFile(String zipFilePath,String fileName){File file = new File(zipFilePath);List<String> list;System.out.println(file.getName());//20250709-5427327003468403533.ziptr…

网站开发合同知识产权手机在线制作图片加字

来源&#xff1a;图灵人工智能作者&#xff1a;王健宗等https://wwwihcm/people/euu-lin-jun-7原文链接&#xff1a;https://kns.cnki.net/kcms/detail/31.1289.tp.20201123.1641.002.html摘要&#xff1a;随着计算机行业和互联网时代的不断发展与进步&#xff0c;图神经网络已…

网站营销的优缺点网站视觉

2010/2/8号 星期一 1.决定记录下每天学到的东西和感悟 2.看老赵的博客&#xff0c;学到一句话&#xff1a;Apple告诉我们的铁律是&#xff1a;表面功夫一定要做足。 3.看到一个笑话&#xff1a;你属什么&#xff1f;我属 于你。 2010/2/9号 星期二 1.减少页面中独立的请求数&…

轻松建站网站建设图片如何加载

本文将综合运用 C++11 中的新的基础设施(主要是多线程、锁、条件变量)来阐述一个经典问题——生产者消费者模型,并给出完整的解决方案。 生产者消费者问题是多线程并发中一个非常经典的问题,相信学过操作系统课程的同学都清楚这个问题的根源。本文将就四种情况分析并介绍生产…