JavaEE企业级开发 延迟双删+版本号机制(乐观锁) 事务保证redis和mysql的数据一致性 示例
提醒
要求了解或者熟练掌握以下知识点
- spring 事务
- mysql 脏读
- 如何保证缓存和数据库数据一致性
- 延迟双删
- 分布式锁
- 并发编程 原子操作类
前言
在起草这篇博客之前
我做了点功课
这边我写的是一个示例代码
数据层都写成了 mock 的形式(来源于 JUnit5)
// Dduo
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit; // 数据服务类
public class DataService { // 模拟缓存(实际使用Redis等实现) private static final ConcurrentHashMap<String, String> cache = new ConcurrentHashMap<>(); // 延迟双删线程池 private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); // 模拟数据库,使用一个 Map 来存储数据记录 private static final ConcurrentHashMap<Integer, DataRecord> mockDatabase = new ConcurrentHashMap<>(); // 数据记录类,包含数据的基本信息和版本号 private static class DataRecord { private int id; private String content; private int version; public DataRecord(int id, String content, int version) { this.id = id; this.content = content; this.version = version; } public int getId() { return id; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public int getVersion() { return version; } public void setVersion(int version) { this.version = version; } } // 模拟从数据库获取数据 private static DataRecord mockDatabaseGet(int id) { return mockDatabase.get(id); } // 模拟数据库更新操作,更新数据并更新版本号 private static boolean mockDatabaseUpdate(int id, String content, int expectedVersion) { DataRecord record = mockDatabase.get(id); if (record == null) { return false; } // 检查版本号是否匹配 if (record.getVersion() != expectedVersion) { return false; } // 更新数据内容 record.setContent(content); // 更新版本号 record.setVersion(expectedVersion + 1); mockDatabase.put(id, record); return true; } // 初始化数据库数据 public void initData(int id, String content) { mockDatabase.put(id, new DataRecord(id, content, 1)); } // 获取数据(带缓存逻辑) public String getData(int id) { String cacheKey = "data_" + id; // 1. 先查缓存 String cached = cache.get(cacheKey); if (cached != null) { return cached; } // 2. 缓存未命中,查询数据库 DataRecord record = mockDatabaseGet(id); if (record == null) { return null; } // 3. 写入缓存(包含版本号信息) String value = record.getContent() + "|v" + record.getVersion(); cache.put(cacheKey, value); return value; } // 更新数据(带延迟双删和版本控制) public boolean updateData(int id, String newContent) { String cacheKey = "data_" + id; // 获取当前数据的版本号 DataRecord record = mockDatabaseGet(id); if (record == null) { return false; } int expectedVersion = record.getVersion(); try { // 1. 第一次删除缓存 cache.remove(cacheKey); // 2. 更新数据库(带版本校验) boolean updateSuccess = mockDatabaseUpdate(id, newContent, expectedVersion); if (!updateSuccess) { return false; } // 3. 提交后安排延迟删除 scheduler.schedule(() -> { try { // 二次删除前的二次校验(可选) DataRecord current = mockDatabaseGet(id); if (current != null && current.getVersion() > expectedVersion) { cache.remove(cacheKey); // 只删除旧版本缓存 } } catch (Exception e) { // 处理异常,可添加重试逻辑 e.printStackTrace(); } }, 1, TimeUnit.SECONDS); // 延迟时间根据主从同步时间调整 return true; } catch (Exception e) { // 处理异常,可添加补偿逻辑 e.printStackTrace(); return false; } } public static void main(String[] args) { DataService service = new DataService(); // 初始化数据 service.initData(1, "Initial Content"); // 获取数据 System.out.println("Initial Data: " + service.getData(1)); // 更新数据 boolean result = service.updateData(1, "Updated Content"); System.out.println("Update Result: " + result); // 再次获取数据 System.out.println("Updated Data: " + service.getData(1)); }
}
要点
- mockDatabaseUpdate 方法中,当更新数据时,会先检查传入的期望版本号与数据库中记录的版本号是否一致。如果一致,会更新数据内容并将版本号加 1。
- getData 方法会先从缓存中查找数据,如果缓存中没有,则从数据库中获取数据,并将数据内容和版本号拼接后存入缓存。
- updateData 方法会先获取当前数据的版本号,然后执行延迟双删操作。在更新数据库时,会携带版本号进行校验,确保数据的一致性。
运行示例
在 main
方法中,我们演示了如何初始化数据、获取数据、更新数据和再次获取数据。运行程序后,你可以看到数据的初始状态、更新结果和更新后的数据。
通过这种方式,版本号和延迟双删机制可以协同工作,保证数据的一致性和缓存的正确性。
- 延迟双删处理缓存层面的最终一致性
- 第二次删除前的版本检查避免过度删除
典型时序:
- 请求A删除缓存
- 请求A更新数据库(版本2)
- 请求B读取缓存未命中,查询数据库(版本1)并填充缓存
- 延迟任务执行二次删除,发现数据库版本已更新,删除旧版本缓存
- 后续请求获取最新数据(版本2)并更新缓存
注意实际需要:
- 替换mock数据库操作为真实DAO操作
- 调整延迟时间(通常500ms-1s)
- 添加缓存空值处理
- 添加重试机制和监控
为什么要进行延迟双删
缓存和数据库数据的一致性一直是我们在后端开发中探讨的问题
先删除缓存再更新数据库情况
现在有两个线程
线程 1 是 写线程
线程 2 是 读线程
如果线程 1 是先删除缓存再更新数据库
在这个时间间隙 就是线程 1 写线程删除缓存和更行数据库的这个间隙
线程 2 读线程进来了
因为缓存已经被删除了 读线程尝试去数据库读取数据
脏数据就这样被写入了缓存
下次读的时候 因为缓存存在 所以一直读取的是旧数据
发生的几率比较大的原因往往是因为
更新数据库的数据是比较慢的
先更新数据库再删除缓存的情况
线程 1 是读线程 线程 1 首先去数据库读取到了旧数据
在写回缓存的这个间隙
线程 2 是写线程 更新了数据库为新数据
之后线程 1 才写入缓存
这样缓存里依旧是旧数据
但这种情况发生情况很小
应为缓存的写入很快
所以很难出现 读线程在写线程更改了数据库数据后再把数据写入缓存
而且另一种情况
线程 1 读线程 执行完毕后
线程 2 写线程 也最终会进行一次删除缓存的操作
思考
● 一种做法是在更新数据时也更新缓存,只是在更新缓存前先加一个分布式锁。因为这样在同一时间只允许一个线程更新缓存,就不会产生并发问题了。当然这么做对于写入的性能会有一些影响;
● 另一种做法同样也是在更新数据时更新缓存,只是给缓存加一个较短的过期时间。这样即使出现缓存不一致的情况,缓存的数据也会很快过期,对业务的影响也是可以接受。
延时双删实现
伪代码
# 延迟双删代码的实现# 删除缓存
redis.delKey(X)# 更新数据库
db.update(X)# 睡眠
Thread.sleep(N)# 再删除缓存
redis.delKey(X)
思考
在延迟双删策略中
我们需要在更新数据库之前
就先把缓存删掉
这样是为了防止在这个间隙有其他请求读取到了缓存
拿到的是失效的缓存数据
清除缓存后 在这个期间 其他请求是不会命中缓存的 会直接去数据库中读取最新数据
这样保证了数据的一致性和缓存的即时更新
在我看来延迟双删是在对比了先删除缓存再更新数据库还是先更新数据库的基础上 选择出了先更新数据库再删除缓存的基础上 的改进
更新数据库数据是一个很慢的过程
这样做可以高效的提高数据的一致性
再高并发读取的情况下 减轻数据库的读取压力 提高读取性能和响应速度
进一步优化
一、使用读写锁优化数据库并发控制
原理:通过区分读锁(共享锁)和写锁(排他锁),确保写操作期间独占资源,避免脏读和不可重复读问题。
示例场景:电商库存扣减
- 写锁应用:当用户下单扣减库存时,事务对库存记录加写锁(
SELECT ... FOR UPDATE
),阻止其他事务同时修改或读取未提交的库存数据。 - 读锁应用:商品详情页展示库存时,事务加读锁(
SELECT ... LOCK IN SHARE MODE
),允许其他读操作共享数据,但阻塞写操作。 - 效果:写锁独占期间,其他读请求需等待写锁释放,确保扣减操作的原子性,避免超卖。
二、高效缓存淘汰算法降低缓存失效影响
原理:通过动态调整缓存过期策略,减少因缓存集中失效导致的数据库瞬时压力。
示例场景:新闻热点数据缓存
- LRU算法优化:传统LRU可能误淘汰热点数据,可升级为 LRU-K(记录最近K次访问时间),优先保留高频访问数据。
- 时间窗口分散:为缓存键的过期时间添加随机值(如基础30分钟 + 随机0-10分钟),避免大量缓存同时失效引发雪崩。
- 主动更新机制:结合读写锁,在缓存失效前异步刷新数据(如后台线程检测过期前5分钟的热点Key,提前加载新数据)。
三、综合应用案例:社交平台评论系统
- 写锁控制评论发布
-
- 用户发布评论时,事务对评论区数据加写锁,阻塞其他用户同时修改同一帖子,确保评论顺序和完整性。
- 读锁允许其他用户持续加载已有评论,仅写操作短暂阻塞。
- LFU算法管理缓存
-
- 使用 LFU(Least Frequently Used) 算法缓存热门帖子,自动淘汰低频访问的旧数据。
- 结合 布隆过滤器 拦截无效查询(如已删除的帖子ID),减少缓存穿透。
四、注意事项
- 锁粒度选择:优先使用行级锁(如InnoDB的间隙锁)而非表锁,减少阻塞范围。
- 缓存一致性:采用 延迟双删策略(更新数据库后先删缓存,短暂延迟后再次删除),避免并发更新导致脏数据。
- 性能监控:通过工具(如Prometheus)监控锁等待时间和缓存命中率,动态调整锁策略和淘汰算法参数。
通过上述方法,可在高并发场景下平衡数据一致性与系统性能,减少因锁竞争或缓存失效导致的业务风险。
具体代码
我们现在要更新数据库
具体业务是插入数据
添加
/*** 添加句子** @param addSentenceDTO 注意提交是一个事务 如果失败则回滚 我们这边使用的是spring的事务框架*/@Override@Transactional(rollbackFor = Exception.class, timeout = 10) // todo 如果插入标签过多 可能会导致事务回滚public void addSentenceWithTags(AddSentenceDTO addSentenceDTO) throws Exception {// 主记录插入AddSentenceReq addSentenceReq = addSentenceDTO.getAddSentenceReq();tSentencesMapper.addSentence(addSentenceReq);Long sentenceId = addSentenceReq.getSentenceId();// 关联标签插入List<AddTagsReq> tagsList = addSentenceDTO.getTagsList();AddSentenceTagReq addSentenceTagReq = new AddSentenceTagReq();addSentenceTagReq.setSentenceId(sentenceId);addSentenceTagReq.setTagsList(tagsList);int size = tagsList.size();if (size == 0) return;else {int i = tSentencesMapper.batchInsertTags(addSentenceTagReq); // 数据库插入标签并返回改变的标签数量if (i != size) {throw new Exception("传入了无效标签");}}// 此时已经更新了数据库 并且提交了事务(事务未回滚) 延迟双删 更新版本号TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {@Overridepublic void afterCommit() {DATA_VERSION.incrementAndGet(); // 版本号自增String cacheKey = "balloonSentences:all" + DATA_VERSION;delayDoubleDelete(cacheKey, 5, TimeUnit.SECONDS); // 执行延时双删List<GetAllContentResp> dbData = tSentencesMapper.getAll(); // 更新elasticsearchelasticsearchService.saveProduct(dbData); // 写到elasticsearch里面去}});}
我们把代码逻辑进行了事务管理
当完成提交后
我们自增版本号
这边是使用的一个原子类
// 原子类 版本号 这边表示的是当前数据版本的版本号private static final AtomicInteger DATA_VERSION = new AtomicInteger(1);
版本号机制重新构造缓存的 key
进行延迟双删
这边为什么又要有版本号机制又要进行双删
因为防止多个线程同时更新 所以要以最近的一次更新来刷新缓存
如果加锁的话 效率就会降低太多了
/*** 更新缓存中全部句子的数据策略:延迟双删* 策略 先删除缓存 然后更新数据库 然后休眠 再删除缓存* 要求用分布式锁方式多线程进入操作数据库环境** @param cacheKey* @param delay* @param unit*/private void delayDoubleDelete(String cacheKey, int delay, TimeUnit unit) {RLock lock = redissonClient.getLock("lock:" + cacheKey);try {lock.lock();// 第一次删除(立即执行)redisService.deleteObject(cacheKey);// 延迟队列二次删除ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();executor.schedule(() -> {redisService.deleteObject(cacheKey);// 强制刷新缓存refreshCacheWithVersion(DATA_VERSION);}, delay, unit);} finally {lock.unlock();}}
之后再强制刷新缓存一遍
验证了我们刚才的想法
我们使用的要是最新的数据
缓存里面的也要是最新数据
/*** 强制刷新缓存** @param currentVersion*/
private void refreshCacheWithVersion(AtomicInteger currentVersion) {String cacheKey = "balloonSentences:all" + currentVersion;RLock lock = redissonClient.getLock("refresh:" + cacheKey);try {lock.lock();// 版本校验(防止旧版本覆盖)List<GetAllContentResp> newData = tSentencesMapper.getAll();// 删除缓存redisService.deleteObject(cacheKey);// 随机化TTL防雪崩 随机化过期时间redisService.setList(cacheKey, newData, RandomUtil.randomInt(30, 60), TimeUnit.MINUTES);} finally {lock.unlock();}
}
如何确定延时的时间
1.数据库性能
如果数据库更新快
可以选择较短的更新时间
2.缓存过期的时间
如果缓存过期的时间较长
可以选择缩短更新时间
以免过早的删除缓存导致数据不一致
思考
假设在延时双删策略中,第一次删除缓存后,会有一段时间的延时,然后再进行第二次删除缓存。如果此时缓存的过期时间设置得很短,比如只有几秒钟,那么在第二次删除缓存之前,缓存可能已经过期,而应用程序在读取缓存时会发现缓存已失效,从而不得不去数据库中查询最新数据。
为了避免这种情况,延时双删的延时时长应该要大于缓存的过期时间,确保在第二次删除缓存之前,缓存还是有效的,这样可以保证应用程序读取到的数据是一致的。
同时还需要考虑数据更新的频率和缓存的使用情况。如果数据更新较为频繁,那么延时双删的延时时长应该要适当缩短,以便及时更新缓存;如果缓存的使用率很低,可以适当延长延时时长,以减少对缓存服务的压力。
相关文章:
JavaEE企业级开发 延迟双删+版本号机制(乐观锁) 事务保证redis和mysql的数据一致性 示例
提醒 要求了解或者熟练掌握以下知识点 spring 事务mysql 脏读如何保证缓存和数据库数据一致性延迟双删分布式锁并发编程 原子操作类 前言 在起草这篇博客之前 我做了点功课 这边我写的是一个示例代码 数据层都写成了 mock 的形式(来源于 JUnit5) // Dduo import java.u…...
SCI一区 | Matlab实现DBO-TCN-LSTM-Attention多变量时间序列预测
SCI一区 | Matlab实现DBO-TCN-LSTM-Attention多变量时间序列预测 目录 SCI一区 | Matlab实现DBO-TCN-LSTM-Attention多变量时间序列预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.【SCI一区级】Matlab实现DBO-TCN-LSTM-Attention多变量时间序列预测(程…...
【Python】天气数据可视化
1. Python进行数据可视化 在数据分析和科学计算领域,Python凭借其强大的库和简洁的语法,成为了众多开发者和科研人员的首选工具。数据可视化作为数据分析的重要环节,能够帮助我们更直观地理解数据背后的规律和趋势。本文将详细介绍如何使用P…...
c#的.Net Framework 的console 项目找不到System.Window.Forms 引用
首先确保是建立的.Net Framework 的console 项目,然后天健reference 应用找不到System.Windows.Forms 引用 打开对应的csproj 文件 在第一个PropertyGroup下添加 <UseWindowsForms>true</UseWindowsForms> 然后在第一个ItemGroup 下添加 <Reference Incl…...
Ubuntu 重置密码方法
目录 修改过 root 密码,重置密码的方法没改过 root 密码,重置密码的方法 修改过 root 密码,重置密码的方法 Ubuntu 默认禁用root用户,意思就是安装好Ubuntu系统后,root用户默认是没有密码的,普通用户通过…...
电机控制常见面试问题(二十)
文章目录 一.整流电路绕组接法二.电机为什么需要转速器三.电机转矩产生原理四.电机控制中载波频率大小的确定五.开关周期 Tpwm 一.整流电路绕组接法 为了引出直流的输出,一定要在整流变压器的二次侧引出零线,所以二次侧绕组必须接成星形 一次绕组必须要…...
Linux系统之yum本地仓库创建
目录 一.Linux软件安装 1.Rpm包安装 2.yum本地仓库安装 二.yum本地仓库建立 三.编译 一.Linux软件安装 软件安装共2种安装方式,通过rpm包安装或通过yum仓库库安装。 先下载安装包命令的方式去安装软件包安装结束 得到一个可以执行程序 绝对路径下的程序 1.…...
未来技术的发展趋势与影响分析
区块链技术在版权中的应用越来越受到关注。它的基本原理是通过分布式账本将每一份作品的版权信息储存起来,确保这些信息不可篡改、不可删除。这就意味着,当创作者发布作品时,可以在区块链上登记相关信息。这样,任何人都能验证版权…...
ROS2 架构梳理汇总整理
文章目录 前言正文机器人平台整体架构(ROS2)图一、个人理解整体架构 ROS2架构图一、个人理解ROS2整体架构图二、开发者整理ROS2整体架构图三、Intel整理ROS2整体架构图四、DDS具体架构说明 ROS2 Control架构图一、官方整整理ROS2 Control整体架构 总结 前…...
蓝桥杯算法精讲:二分查找实战与变种解析
适合人群:蓝桥杯备考生 | 算法竞赛入门者 | 二分查找进阶学习者 目录 一、二分查找核心要点 1. 算法思想 2. 适用条件 3. 算法模板 二、蓝桥杯真题实战 例题:分巧克力(蓝桥杯2017省赛) 三、二分查找变种与技巧 1. 查找左边…...
多层感知机实现
激活函数 非线性 ReLU函数 修正线性单元 rectified linear unit relu(x)max(0,x) relu的导数: sigmoid函数 s i g m o i d ( x ) 1 1 e − x sigmoid(x)\frac{1}{1e^{-x}} sigmoid(x)1e−x1 是一个早期的激活函数 缺点是: 幂运算相对耗时&…...
Linux进程控制--进程创建 | 进程终止 | 进程等待 | 进程替换
1.进程创建 现阶段我们知道进程创建有如下两种方式,起始包括在以后的学习中有两种方式也是最常见的: 1、命令行启动命令(程序、指令)。 2、通过程序自身,使用fork函数创建的子进程。 1.1 fork函数 在linux操作系统中,fork函数是…...
Linux 网络编程(二)——套接字编程简介
文章目录 2 Socket 套接字 2.1 什么是 Socket 2.2 Socket编程的基本操作 2.3 地址信息的表示 2.4 网络字节序和主机字节序的转换 2.4.1 字节序转换 2.4.2 网络地址初始化与分配 2.5 INADDR_ANY 2.6 Socket 编程相关函数 2.7 C标准中的 main 函数声明 2.8 套接字应用…...
串行通信 与 并行通信 对比
总目录 一、并行通信 1. 定义与核心特点 1) 定义 并行通信是指通过多条数据线同时传输一组数据的各个位(如8位、16位或更多),以字节或字为单位进行数据交换的通信方式。 2)核心特点 特点描述传输速度快多位同时传…...
基于springboot+vue的北部湾地区助农平台
开发语言:Java框架:springbootJDK版本:JDK1.8服务器:tomcat7数据库:mysql 5.7(一定要5.7版本)数据库工具:Navicat11开发软件:eclipse/myeclipse/ideaMaven包:…...
Docker技术系列文章,第七篇——Docker 在 CI/CD 中的应用
在当今快速发展的软件开发领域,持续集成与持续部署(CI/CD)已经成为提高软件交付效率和质量的关键实践。而 Docker 作为一种流行的容器化技术,为 CI/CD 流程提供了强大的支持。通过将应用及其依赖项打包成容器,Docker 确…...
Hive SQL中 ?+.+ 的用法,字段剔除
一、含义 ?. 的用法代表剔除表中的特定字段,建议按照字段顺序列出以确保正确性。 二、参数设置 -- 首先需要设置一个参数: set hive.support.quoted.identifiersNone; --然后指定要剔除哪个字段 select (dateline)?. from test.dm_user_add三、举例…...
Vue学习笔记集--pnpm包管理器
pnpm包管理器 官网: https://www.pnpm.cn/ pnpm简介 pnpm全称是performant npm,意思为“高性能的npm”,它通过硬链接和符号链接共享依赖,提升安装速度并减少存储占用。 功能特点 节省磁盘空间:依赖包被存放在一个统…...
游戏交易系统设计与实现(代码+数据库+LW)
摘 要 在如今社会上,关于信息上面的处理,没有任何一个企业或者个人会忽视,如何让信息急速传递,并且归档储存查询,采用之前的纸张记录模式已经不符合当前使用要求了。所以,对游戏交易信息管理的提升&#x…...
为什么视频文件需要压缩?怎样压缩视频体积即小又清晰?
在日常生活中,无论是为了节省存储空间、便于分享还是提升上传速度,我们常常会遇到需要压缩视频的情况。本文将介绍为什么视频需要压缩,压缩视频的好处与坏处,并教你如何使用简鹿视频格式转换器轻松完成MP4视频文件的压缩。 为什么…...
腾讯pcg客户端一面
Java 基本引用类型 常见异常以及怎么处理 所有类的父类是什么,有哪些常用方法 常用线程池有哪些 线程池的创建参数 如何实现线程同步 常用锁有哪些 Lock和reentrantlock有什么不一样 Reentrantlock要手动释放锁吗 数据结构 数组和链表的区别 队列和栈的区别 为什么…...
解决vscode终端和本地终端python版本不一致的问题
🌿 问题描述 本地终端: vscode终端: 别被这个给骗了,继续往下看: 难怪我导入一些包的时候老提示找不到,在本地终端就不会这样,于是我严重怀疑vscode中的python版本和终端不一样,…...
常见几种网络攻击防御方式
xss跨站脚本攻击 反射型 XSS(Reflected XSS): 恶意脚本是通过 URL 参数或者表单提交直接传递给服务器的,并且立即在响应页面中反射返回给用户。 假设有一个登录页面,用户可以通过 URL 参数传递一个消息: &…...
操作系统之输入输出
🧑 博主简介:CSDN博客专家,历代文学网(PC端可以访问:https://literature.sinhy.com/#/literature?__c1000,移动端可微信小程序搜索“历代文学”)总架构师,15年工作经验,…...
TCP/IP的网络连接设备
TCP/IP层物理层网卡、集线器、中继器数据链路层网桥、交换机网络层路由器传输层网关应用层 1.网桥:网桥主要功能是将一个网络的数据沿通信线路复制到另一个网络中去,可以有效的连接两个局域网 2.网关:网关又称协议转换器,是将两…...
记一次feign调用400,参数过长导致,修改解决
feign客户端PostMapping("/website/checkChooseColumn") boolean checkChooseColumn(RequestParam("chooseColumn") String chooseColumn);服务端 PostMapping("/checkChooseColumn") public boolean checkChooseColumn(RequestParam("cho…...
【大模型基础_毛玉仁】4.3 参数选择方法
目录 4.3 参数选择方法4.3.1 基于规则的方法4.3.2 基于学习的方法1)公式:2)Child-tuning 的两种变体模型3)Child-tuning总结 4.3 参数选择方法 参数选择方法: 对预训练模型中部分参数微调,不添加额外参数以避免推理时…...
企业级Linux服务器初始化优化全流程
实战指南:企业级Linux服务器初始化优化全流程 本文基于某电商平台百万级并发服务器的真实调优案例整理,所有操作均在Rocky Linux8.5验证通过,不同发行版请注意命令差异 一、服务器安全加固(Situation-Task-Action-Resultÿ…...
亿级分布式系统架构演进实战(十一)- 垂直拆分(服务治理体系、安全架构升级)
亿级分布式系统架构演进实战(一)- 总体概要 亿级分布式系统架构演进实战(二)- 横向扩展(服务无状态化) 亿级分布式系统架构演进实战(三)- 横向扩展(数据库读写分离&#…...
飞速(FS)InfiniBand解决方案助力领先科技公司网络升级
国家:越南 行业:信息技术 网络类型:InfiniBand网络 方案类型:HPC网络 案例亮点 通过真实使用场景的全面测试,确保出色兼容性和高可用性,显著降低部署风险和运营成本。 借助飞速(FS…...
[Qt5] QMetaObject::invokeMethod使用
📢博客主页:https://loewen.blog.csdn.net📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正!📢本文由 丶布布原创,首发于 CSDN,转载注明出处🙉📢现…...
深入理解垃圾收集算法:从分代理论到经典回收策略
垃圾收集(Garbage Collection, GC)是现代虚拟机自动内存管理的核心机制。它不仅能自动回收不再使用的对象,还能极大减轻开发者在内存管理上的负担。本文将详细讲解垃圾收集算法的基本思想、分代收集理论以及几种经典的垃圾收集算法。 注&…...
数据降维——PCA与LDA
特征选择和特征提取 特征选择和特征提取是数据降维的重要步骤。 1. 定义与目标 特征提取: 目标:通过变换(如投影、编码)将原始高维特征映射到新的低维空间,新特征是原始特征的组合(线性或非线性ÿ…...
机器学习中的 K-均值聚类算法及其优缺点
K-均值聚类是一种常用的无监督学习算法,用于将数据集中的样本分为 K 个簇。其工作原理是通过迭代优化来确定簇的中心点,实现样本的聚类。 算法步骤如下: 随机选择 K 个样本作为初始簇中心。根据每个样本和簇中心的距离将样本归类到最近的簇…...
RAID原理
一、RAID 0 原理 核心特点 条带化(Striping):数据被分割成块(Block),交替写入多个磁盘(至少2块)。无冗余:不提供数据备份或校验,依赖所有磁盘同…...
2025系统分析师---软件工程:深度剖析常见软件开发方法
在软件工程这一复杂而精妙的领域中,软件开发方法的选择与实施无疑是项目成功的关键所在。作为一名资深软件技术专家,我深知不同的开发方法适用于不同的业务场景,各自具备独特的优缺点。本文将深入探讨几种常见的软件开发方法,包括…...
中文字符计数器,助力所有python对齐业务(DeepSeek代笔)
编码制式反推双宽,精准字宽库力推中文对齐。 笔记模板由python脚本于2025-03-26 23:49:24创建,本篇笔记适合为中文终端显示和文本输出对齐烦恼的coder翻阅。 【学习的细节是欢悦的历程】 博客的核心价值:在于输出思考与经验,而不仅…...
扫描注解指定路径
10.扫描注解 在 Spring Boot 中,EnableConfigurationProperties 和 ConfigurationPropertiesScan 是两个用于显式启用和管理 ConfigurationProperties 类的注解。它们提供了更灵活的方式来注册和扫描 ConfigurationProperties 类,尤其是在某些复杂场景或…...
像素到数据:Selenium,OpenCV,Tesseract,Python构建的智能解析系统
基于Selenium与OCR技术的网页信息智能提取方案 一、应用场景解析 在Web自动化测试和数据分析领域,经常需要处理动态渲染的网页信息,特别是当页面元素以图像形式呈现时。本文介绍的解决方案结合了浏览器自动化与图像识别技术,有效解决了以下典型场景: 动态渲染的可视化数据…...
徘徊检测:视觉分析技术的安防新方向
利用视觉分析的方式检测徘徊检测 背景 随着时代的发展,失业率上升导致社会不稳定因素增加,安保问题愈发突出。特别是在住宅区、工厂、办公园区等公共场所,对于徘徊人员的检测成为确保安全的关键一环。传统的安保手段如人工巡逻、监控录像回…...
CentOS 7 挂载与卸载文件系统
一、挂载文件系统 1. 查看系统磁盘与分区情况 在挂载文件系统之前,需要先了解系统中的磁盘和分区信息。使用fdisk -l命令,可列出所有磁盘和分区的详细信息,示例如下: [rootlocalhost ~]# fdisk -lDisk /dev/sda: 53.7 GB, …...
MySQL实战(尚硅谷)
要求 代码 # 准备数据 CREATE DATABASE IF NOT EXISTS company;USE company;CREATE TABLE IF NOT EXISTS employees(employee_id INT PRIMARY KEY,first_name VARCHAR(50),last_name VARCHAR(50),department_id INT );DESC employees;CREATE TABLE IF NOT EXISTS departments…...
JavaScript 改变 HTML 内容
JavaScript 改变 HTML 内容 JavaScript 改变 HTML 内容的核心在于通过 DOM(文档对象模型)操作实现动态更新,以下是主要方法及场景解析: 一、直接修改元素内容 1. innerHTML 属性 用于获取或设置元素的 HTML 内容(包…...
第十四届蓝桥杯大赛软件赛省赛C/C++ 大学 B 组(部分题解)
文章目录 前言日期统计题意: 冶炼金属题意: 岛屿个数题意: 子串简写题意: 整数删除题意: 总结 前言 一年一度的🏀杯马上就要开始了,为了取得更好的成绩,好名字写了下前年2023年蓝桥…...
机器学习——Bagging、随机森林
相比于Boosting的集成学习框架,Bagging(Bootstrap Sampling,自助聚集法,又称为自助采样)作为一种自助聚集且并行化的集成学习方法,其通过组合多个基学习器的预测结果来提高模型的稳定性和泛化能力。其中随机森林是Bagging学习框架…...
数据库——MySQL基础操作
一、表结构与初始数据 假设存在以下两张表: 1. student 表 字段名数据类型描述idINT学生唯一标识符nameVARCHAR(100)学生姓名ageINT学生年龄sexVARCHAR(10)学生性别 初始数据: idnameagesex1张三20男2李四22女3王五21男 2. course 表 字段名数据类…...
存储过程、存储函数与触发器详解(MySQL 案例)
存储过程、存储函数与触发器详解(MySQL 案例) 一、存储过程(Stored Procedure) 定义 存储过程是预先编译好并存储在数据库中的一段 SQL 代码集合,可以接收参数、执行逻辑操作(如条件判断、循环)…...
2025年注册安全工程师考试练习题
注册安全工程师练习题,涵盖了不同的知识点和题型: 单选题 某机械制造企业委托具有相应资质的中介服务机构的专业技术人员为其提供安全生产管理服务。依据《安全生产法》,保证该企业安全生产的责任由( )负责。 A. 专业…...
Photoshop 2025安装包下载及Photoshop 2025详细图文安装教程
文章目录 前言一、Photoshop 2025安装包下载二、Photoshop 2025安装教程1.解压安装包2.运行程序3.修改安装路径4.设安装目录5.开始安装6.等安装完成7.关闭安装向导8.启动软件9.安装完成 前言 无论你是专业设计师,还是初涉图像处理的小白,Photoshop 2025…...
ESP32通过WiFi获取网络时间(NTP)
代码部分 代码由station_example_main的官方例程修改 /* WiFi station ExampleThis example code is in the Public Domain (or CC0 licensed, at your option.)Unless required by applicable law or agreed to in writing, thissoftware is distributed on an "AS IS&…...