揭秘Redis事务:弱原子性的背后逻辑

Java中级开发工程师面试核心考察维度与高频问题解析 Java作为企业级开发的主流语言,其并发编程和集合框架是面试必考重点。HashMap作为核心数据结构,JDK8通过引入红黑树优化了哈希冲突处理,与ConcurrentHashMap的线程安全实现形成对比。在并发领域,线程池参数配置和锁机制选择直接影响系统性能,合理使用CountDownLatch等工具类能有效协调多线程任务。Spring框架的IoC容器初始化过程和AOP代理机制体现了设计模式的精妙应用,而事务传播行为的理解对保证数据一致性至关重要。掌握这些原理不仅能应对技术面试,更能指导实际工程中的架构设计 阅读详情

什么叫事物???

事务(Transaction)本质是一组操作的集合,核心目标是保证这组操作的「完整性」—— 简单说就是:要么这组操作全部执行成功,要么全部不执行,不会出现「部分执行」的情况。

你可以把事务理解成生活中的「转账场景」:A 给 B 转 100 元,需要执行两步:A 扣 100 元、B 加 100 元。这两步必须同时成功(转账完成),或同时失败(钱都不变),绝对不能出现「A 扣了钱但 B 没收到」的情况,事务就是用来保证这种「要么全成、要么全败」的机制。

事物的四个特性:

原子性是手段:保证事务内的操作要么全做,要么全不做,避免 “部分执行” 的中间态。

一致性是目的:保证事务执行前后数据库始终处于合法状态。数据库必须从一个合法的状态转换到另一个合法的状态,始终满足所有预定义的约束和业务规则。

隔离性是手段:多个并发事务同时执行时,彼此之间相互隔离,一个事务的执行不能被其他事务干扰,其内部操作和数据对其他事务是不可见的。

持久性是保障:一旦事务成功提交,其对数据库的修改就是永久性的,即使系统崩溃、断电,数据也不会丢失。

redis事物和mysql事物的区别

特性MySQL 事务Redis 事务
原子性(Atomicity)完全支持:只要有一步失败,所有操作回滚(撤销),回到初始状态仅「弱原子性」:1. 语法错误(如命令写错):EXEC 后全部不执行;2. 运行时错误(如对字符串做哈希操作):错误命令失败,其他命令照常执行(无回滚)
一致性(Consistency)严格保证:事务执行前后,数据符合业务规则(如转账后总金额不变)仅靠业务逻辑保证:Redis 本身不校验数据一致性,需开发者自己控制
隔离性(Isolation)支持 4 种隔离级别(读未提交、读已提交、可重复读、串行化),解决脏读 / 幻读 / 不可重复读仅「串行执行」:事务内命令按顺序执行,执行期间不会被其他客户端打断,但无隔离级别概念
持久性(Durability)可通过 redo/undo 日志、刷盘策略保证:事务提交后数据永久保存依赖 Redis 持久化(RDB/AOF):若未开启持久化,事务提交后重启 Redis 数据会丢失
锁机制悲观锁为主(行锁 / 表锁):事务期间会锁定操作的行 / 表,防止并发修改乐观锁(WATCH 命令):仅监控 key,若事务执行前 key 被修改,事务直接放弃执行,无主动加锁
回滚机制支持回滚(ROLLBACK):失败后撤销所有已执行操作无回滚机制:Redis 认为「运行时错误是开发者的问题」,不提供回滚功能
适用场景复杂业务逻辑(如订单、支付、账务),强一致性要求简单批量操作(如批量设值、计数),高性能优先,弱一致性要求

对于我们熟知的事物是支持ACID的
 

但是redis的事物有点特殊:不支持回滚操作、可以说是弱原子性,无一致性无隔离性无持久性

Redis 事务适合用于命令打包执行,减少网络往返,保证命令的顺序执行。

它不适合用于需要强一致性和复杂业务逻辑的场景(如金融交易),这些场景应该使用支持 ACID 的关系型数据库。

设计者认为在事物当中出现语法错误问题应当是你应用者的问题,你应该自行在业务层面进行管理

以下是以前官方文档的解释,但是后来删除了

EXEC 命令会触发事务中所有命令的执行,因此如果客户端在事务过程中、调用 EXEC 命令之前与服务器断开连接,那么所有操作都不会被执行;反之,如果 EXEC 命令被调用,则所有操作都会被执行。在使用 AOF(仅追加文件)时,Redis 会确保通过一次 write(2) 系统调用将整个事务写入磁盘。然而,如果 Redis 服务器崩溃,或者被系统管理员强制终止,就有可能只注册了部分操作。Redis 在重启时会检测到这种情况,并以错误状态退出。使用 redis-check-aof 工具可以修复 AOF 文件,移除不完整的事务,从而让服务器能够重新启动。

以上exec命令就是开始执行事物

redis事物的实现

基本操作

操作目标对应命令作用说明
开启事务MULTI标记当前客户端进入「事务状态」,后续命令入队而非立即执行(仅对当前客户端生效)
执行并关闭事务EXEC执行队列中的所有命令,执行完毕后自动清空队列、退出事务状态
放弃并关闭事务DISCARD清空队列、直接退出事务状态,不执行任何命令
被动关闭客户端断开连接客户端连接关闭(如超时、网络中断),Redis 会自动清理该客户端的事务队列,重置状态

一个客户端在开启事物,一个在查询完全查不到,因为事物中的命令还未执行,只是插入队列

执行完之后才可以查到

源码实现+理解

// 客户端结构体(server.h)
typedef struct client {
    // 事务相关核心字段
    multiState mstate;      // 事务状态(包含命令队列)
    int flags;              // 客户端标记,如 CLIENT_MULTI(是否进入事务)
    // 其他字段:套接字、当前数据库、命令缓冲区等
} client;

// 事务状态结构体(server.h)—— 存储事务队列
typedef struct multiState {
    multiCmd *commands;     // 指向事务命令数组的指针
    int count;              // 队列中命令的数量
    int minreplicas;        // 事务持久化相关(min-replicas-to-write)
    time_t minreplicas_timeout; // 超时时间
} multiState;

// 单个事务命令节点(server.h)—— 队列中的每个元素
typedef struct multiCmd {
    robj **argv;            // 命令参数数组(如 SET k1 v1 的 argv[0]="SET", argv[1]="k1", argv[2]="v1")
    int argc;               // 参数个数
    struct redisCommand *cmd; // 指向命令对应的 redisCommand 结构体(如 setCommand 函数)
} multiCmd;

每个客户端都有一个multiState结构体,相当于每个客户端都有一个队列

客户端采用multi命令进入队列,相当于进入事物,接下来的命令就是打包,但未执行

// t_multi.c - MULTI 命令处理函数
void multiCommand(client *c) {
    // 1. 检查是否已在事务中,避免重复执行 MULTI
    if (c->flags & CLIENT_MULTI) {
        addReplyError(c,"MULTI calls can not be nested");
        return;
    }
    // 2. 标记客户端进入事务状态
    c->flags |= CLIENT_MULTI;
    // 3. 返回成功响应
    addReply(c,shared.ok);
}
  • CLIENT_MULTI 是客户端 flags 中的一个位标记,只要置位,后续命令就会进入「入队逻辑」而非「立即执行」;
  • 这一步不创建队列,只是标记状态,队列会在第一条命令入队时动态初始化。
// server.c - 处理客户端命令的核心函数
int processCommand(client *c) {
    // ... 省略命令解析、权限检查等逻辑 ...

    // 判断是否处于事务状态,且当前命令不是事务控制命令
    if (c->flags & CLIENT_MULTI && 
        c->cmd->proc != execCommand &&
        c->cmd->proc != discardCommand &&
        c->cmd->proc != multiCommand &&
        c->cmd->proc != watchCommand) 
    {
        // 执行命令入队逻辑
        queueMultiCommand(c);
        // 入队成功,返回 "QUEUED" 响应
        addReply(c,shared.queued);
        return C_OK;
    }

    // 非事务状态:直接执行命令
    call(c,CMD_CALL_FULL);
    return C_OK;
}

// t_multi.c - 命令入队的核心函数
void queueMultiCommand(client *c) {
    multiCmd *mc;
    int j;

    // 1. 扩展事务命令数组(动态扩容,初始大小为4,满了则翻倍)
    c->mstate.commands = zrealloc(c->mstate.commands,
            sizeof(multiCmd)*(c->mstate.count+1));
    // 2. 指向新的命令节点
    mc = c->mstate.commands + c->mstate.count;
    // 3. 复制命令参数(Redis 会复制参数对象,避免外部修改)
    mc->argc = c->argc;
    mc->argv = zmalloc(sizeof(robj*)*c->argc);
    for (j = 0; j < c->argc; j++) {
        mc->argv[j] = incrRefCount(c->argv[j]); // 增加引用计数
    }
    // 4. 关联命令对应的执行函数(如 SET 对应 setCommand)
    mc->cmd = c->cmd;
    // 5. 队列计数+1
    c->mstate.count++;
}
// t_multi.c - EXEC 命令处理函数
void execCommand(client *c) {
    int j;
    multiCmd *mc;
    int old_flags = c->flags;
    // 1. 检查是否处于事务状态
    if (!(c->flags & CLIENT_MULTI)) {
        addReplyError(c,"EXEC without MULTI");
        return;
    }

    // 2. 检查 WATCH 的 key 是否被修改(乐观锁逻辑)
    if (c->flags & CLIENT_DIRTY_CAS) {
        // 有 key 被修改,事务取消,返回空
        addReply(c,shared.nullmultibulk);
        discardTransaction(c); // 清空队列、重置状态
        return;
    }

    // 3. 遍历事务队列,按顺序执行所有命令
    addReplyArrayLen(c,c->mstate.count); // 先返回结果数组的长度
    for (j = 0; j < c->mstate.count; j++) {
        mc = c->mstate.commands+j;
        // 3.1 重置客户端的 argv/argc,指向当前要执行的命令
        c->argc = mc->argc;
        c->argv = mc->argv;
        c->cmd = mc->cmd;
        // 3.2 执行命令(核心:call 函数调用命令的执行逻辑)
        call(c,CMD_CALL_FULL);

        // 3.3 执行后恢复参数(避免内存泄漏)
        c->argv = c->orig_argv;
        c->argc = c->orig_argc;
    }

    // 4. 执行完毕,清理事务状态
    discardTransaction(c);
    // 5. 恢复客户端标记
    c->flags = old_flags & ~(CLIENT_MULTI|CLIENT_DIRTY_CAS);
}

// t_multi.c - 清理事务状态(清空队列、释放内存)
void discardTransaction(client *c) {
    int j;
    multiCmd *mc;

    // 1. 释放所有入队命令的参数内存
    for (j = 0; j < c->mstate.count; j++) {
        mc = c->mstate.commands+j;
        for (int k = 0; k < mc->argc; k++) {
            decrRefCount(mc->argv[k]); // 减少引用计数,释放对象
        }
        zfree(mc->argv);
    }
    // 2. 释放命令数组内存
    zfree(c->mstate.commands);
    // 3. 重置事务状态
    c->mstate.commands = NULL;
    c->mstate.count = 0;
}
  • 执行前先检查 WATCH 的 key 是否被修改(CLIENT_DIRTY_CAS 标记),如果被修改则直接取消事务,返回 nil;
  • 遍历队列时,会将客户端的 argv/argc/cmd 临时替换为队列中当前命令的信息,然后调用 call 函数执行命令(和非事务命令的执行逻辑完全一致);
  • 执行过程中不会处理其他客户端的命令(Redis 单线程模型保证),因此实现了串行执行;
  • 执行完毕后调用 discardTransaction 清空队列、释放内存,重置事务状态;
  • 执行结果会按入队顺序打包成数组返回,这就是 EXEC 后看到的多个结果的原因。

MULTI SET k1 v1            # 入队成功

SETT k2 v2                     # 入队失败,返回错误

EXEC                              # Redis 检测到入队错误,直接返回错误,所有命令都不执行

MULTI SET k1 v1            # 入队成功(String 类型)

HGET k1 name               #入队成功(语法合法)

SET k2 v2                       # 入队成功

EXEC # 执行:

                                        # 1. SET k1 v1 成功

                                        # 2. HGET k1 name 失败(类型不匹配)

                                        # 3. SET k2 v2 成功

redis的超卖场景

对于平时的多线程编程,我们采用加锁去访问数据库

如果这里不加锁,读取个数的时候,先返回>0,然后再if语句里面,另一个又去读取就会导致商品个数等于负数,不符合一致性

那么是否可以单单利用事物的特点去支持呢???

仅仅需要把操作放在事物之间,但是if语句这里redis是没有这种命令操作的,那么需要通过lua脚本(前面提到过lua脚本是redis的可扩展性,redis支持lua编写原子性操作)

-- 扣减库存的Lua脚本(核心逻辑)
-- KEYS[1]:库存key(如 stock:iphone)
-- 返回值:1=扣减成功,0=库存不足
local stock_key = KEYS[1]
-- 1. 获取当前库存(转为数字,避免nil)
local current_stock = tonumber(redis.call('GET', stock_key))
-- 2. 库存不存在/库存不足,返回0
if not current_stock or current_stock <= 0 then
    return 0
end
-- 3. 库存充足,扣减1,返回1
redis.call('DECR', stock_key)
return 1
// 定义Lua脚本(解决超卖的核心逻辑)
const std::string SECKILL_LUA = R"(
local stock_key = KEYS[1]
local current_stock = tonumber(redis.call('GET', stock_key))
-- 库存不存在或不足,返回0
if not current_stock or current_stock <= 0 then
    return 0
end
-- 库存充足,扣减并返回1
redis.call('DECR', stock_key)
return 1
)";

// Redis执行Lua脚本的函数
int executeLuaScript(redisContext* ctx, const std::string& luaScript, const std::string& key) {
    // 方式1:直接用EVAL执行脚本字符串(简单易上手)
    // 参数说明:
    // EVAL + 脚本内容 + KEYS数组长度 + 具体key
    redisReply* reply = (redisReply*)redisCommand(
        ctx, 
        "EVAL %s %d %s", 
        luaScript.c_str(), 
        1,  // KEYS数组的长度(这里只有1个key)
        key.c_str()
    );

    if (ctx->err) {
        std::cerr << "Redis命令执行失败:" << ctx->errstr << std::endl;
        return -1;
    }

    int result = -1;
    // 判断返回值类型(脚本返回1/0,是整数类型)
    if (reply->type == REDIS_REPLY_INTEGER) {
        result = reply->integer;
    } else {
        std::cerr << "脚本返回值类型错误" << std::endl;
    }

    // 释放reply资源(必须!否则内存泄漏)
    freeReplyObject(reply);
    return result;
}

让用户把多个 Redis 命令封装成一个 “原子脚本”,执行期间不被其他命令打断,同时支持自定义逻辑(如条件判断、循环)。这样通过lua脚本解决了redis天然没有判断命令的缺陷,通过这样的操作,把命令打包成事物,可以判断,这样就能解决超卖问题

watch监控事物

之前提到过源码解析的时候,watch,如果发现数据有被修改则放弃执行事物

在事物还未被exec的时候,客户端2去set key 333,导致key被修改,至此版本号变更

当事物被exec的时候,先判断watch的key是否被修改了,当前被修改了,所以取消事物

watch的原理

这里相当于乐观锁,对应的就是悲观锁,乐观锁和悲观锁值得是某一类锁的一种特性

锁类型核心思想通俗比喻
悲观锁假设一定会发生并发冲突,提前加锁,锁住资源不让别人用,直到自己操作完释放去洗手间先反锁门,怕有人进来
乐观锁假设不会发生并发冲突,不加锁,操作时只检查资源是否被修改过,冲突则重试去洗手间不锁门,进门先看有没有人,有人就等
对于悲观锁,成本肯定高,乐观锁成本低,redis的watch就是基于版本号的形式实现的乐观锁
  • 悲观锁:“先锁后做”,假设冲突一定会发生,主动加锁阻塞,适合冲突多、要求高一致性的场景(如 MySQL);
  • 乐观锁:“先做后验”,假设冲突不会发生,不加锁只校验,适合冲突少、要求高性能的场景(如 Redis);
  • 实际开发中,Redis 优先用 Lua 脚本(原子性乐观锁),MySQL 优先用悲观锁(行锁),核心是 “按场景选锁”。

对于redis,高并发场景下lua脚本比watch更好,因为watch你需要不断的重复

  • 重试风暴:高并发下(如 10w+/s 请求),几乎每个请求的 EXEC 都会返回 nil(因为库存 key 被频繁修改),导致大量请求陷入 “WATCH→EXEC→重试→再 WATCH” 的循环;
  • 资源浪费:每次重试都会占用 Redis 的连接、CPU 资源,最终导致 Redis 负载飙升,响应延迟增加;
维度WATCH + 事务(乐观锁)Lua 脚本
原子性依赖乐观锁,可能被打断(需重试)天然原子性,执行期间不可打断
网络开销多次往返(WATCH→MULTI→EXEC→重试)1 次往返(发送脚本 / 执行)
重试成本高并发下重试率极高,资源浪费无重试,一次执行完成
性能上限低(1w/s 左右开始性能下降)高(10w+/s 仍稳定)
代码复杂度需处理重试逻辑(如循环、休眠)逻辑封装在脚本,客户端只需处理结果
集群兼容性WATCH 不支持 Redis 集群支持集群(规范使用 KEYS 数组即可)
错误率高(重试失败、连接超时)低(仅脚本语法 / 参数错误)

延申问题:CAC的ABA问题

总结

对于一致性,数据库redis本身是无法保证的,需要程序员在业务层面手动去维护,redis就像一个无情的机器,执行合法的命令

Java分布式系统设计与面试核心要点解析 分布式系统设计是Java高级开发的核心能力,其关键在于理解CAP理论、分布式锁实现及高并发场景下的架构权衡。以Redis为例,通过SETNX指令和Lua脚本可实现原子性分布式锁,这是解决秒杀系统库存一致性的关键技术。在工程实践中,系统设计需要结合分层架构(如接入层限流、缓存层削峰)与异步处理机制(如消息队列),同时需关注JVM性能调优和代码规范。对于Java开发者而言,掌握HashMap扩容机制、并发容器选型等底层原理,以及熟练使用Arthas等性能分析工具,是应对大厂技术面试的重要基础。本文以秒杀系统为案 阅读详情

相关推荐

AI智能体技能不可跨框架移植?揭秘Hermes、OpenClaw与QwenPaw底层架构差异

AI智能体(Agent)并非功能模块的简单集合,其核心是运行时环境所定义的认知范式。理解Agent框架的本质,需从执行模型(状态/事件/意图驱动)、技能注册机制(静态扫描/元编程注入/YAML编排)和上下文管理(内存快照/数据库/认知图谱)三重原理切入。这种底层设计差异直接决定技术价值:Hermes强调高保真意图理解与记忆协同,OpenClaw侧重多渠道接入鲁棒性,QwenPaw专注工作流工程化。在金融客服、政务问答、跨境电商等真实场景中,强行复制技能文件将引发ABI不兼容、上下文对象缺失、异步执行冲突等系

weixin_33888907的博客 359

揭秘数据库管理系统,一文教你轻松读懂背后逻辑

数据库管理系统是连接用户与操作系统的数据管理软件,负责高效组织、存储和管理数据,确保数据的完整性、安全性和可靠性。它不仅是数据存储工具,更是一个具备定义、查询、更新和管控等综合功能的系统。文章对比了数据库、数据仓库及主数据管理平台的区别,并介绍了MySQL、PostgreSQL等主流关系型数据库和MongoDB、Redis等非关系型数据库,是一篇非常适合新手小白入门了解专业知识的文章。

renzhendaimayuan的博客 1163

面试趣谈:互联网大厂Java全栈技术栈大揭秘 [特殊字符]

李小梅,今天问题比较多,回答也挺有趣的。你对基础技术有不错理解,但复杂问题需要加强。回去好好准备下,等我们通知哈!😊。

weixin_47523390的博客 147

揭秘程序员节交流群爆火背后:8大关键技术话题首次公开

揭秘程序员节交流群爆火背后:8大关键技术话题首次公开。聚焦高并发、微服务等实战场景,分享架构优化与团队协作秘诀,提升开发效率。程序员节交流群汇聚一线技术干货,值得收藏。

VarLens的博客 680

李小梅的互联网大厂面试日记:Java全栈技术大揭秘!

在一家知名互联网大厂,面试官老李正严肃认真地对候选人李小梅进行技术面试。李小梅性格幽默,回答时常带点“水”,但也能答出些简单问题。通过三轮深入提问,揭开Java及相关技术栈在多个业务场景中的应用。

weixin_47523390的博客 206

Redis操作封装完整示例与实战设计

简介:Redis是一款高性能的键值存储系统,广泛应用于缓存、消息队列等场景,支持字符串、哈希、列表、集合和有序集合等多种数据类型。本文提供一个完整的Redis操作封装示例,通过构建RedisClient类,统一管理各类基本操作与高级功能,如数据读写、事务处理及发布/订阅机制。该封装方式提升了代码可读性、复用性和可维护性,便于在实际项目中快速集成与扩展。同时涵盖连接管理、异常处理与自动重连等可靠性设计,助力开发者高效构建稳定的应用系统。

weixin_30591519的博客 261

电商千万级交易的金手指:分布式事务管理

摘要:从古至今,我们的交易与消费过程 发生着翻天覆地的变化。今天,带大家一起 解密持续千年那些买买买背后的故事。 本文分享自华为云社区《揭秘买买买千万级交易背后的那些事》,原文作者:华为云头条 。 上云总动员干货进行时,带你一起揭秘买买买背后的那些事! 从古至今,我们的交易与消费过程 发生着翻天覆地的变化 今天,云宝想带大家一起 解密持续千年那些买买买背后的故事 遥想远古时期 人们通过以物易物的方式实现消费需求 但常因物品价值不对等而不欢而散 后来出现货币,从贝壳到铜钱 “买买买”

华为云官方博客 2570

终端效率神器:Tmux 安装与使用全指南

终端效率神器:Tmux 安装与使用全指南

身在被窝_心在编程 1360

Java大厂面试实战:JVM、Spring Boot与Redis深度解析

JVM内存模型与GC调优是Java工程师必须掌握的核心技术,其原理涉及堆内存分代设计、垃圾收集器选择及性能优化。理解G1收集器的Remembered Set机制或ZGC的彩色指针实现,能有效解决生产环境中的高延迟问题。Spring Boot的自动配置通过条件注解实现组件动态加载,而自定义Starter需遵循特定规范以避免类冲突。Redis作为高性能缓存,其单线程模型与IO多路复用技术支撑了高并发场景,但缓存雪崩、击穿等问题需结合双层缓存与分布式锁应对。本文通过真实面试案例,剖析大厂对JVM、Spring框架

weixin_34241036的博客 425

U-Net图像分割实战:从论文解析到PyTorch代码实现

本文深入解析了U-Net图像分割模型的经典架构与核心思想,并提供了从零开始的PyTorch代码实现教程。通过详细拆解编码器-解码器结构、跳跃连接等关键组件,手把手指导读者构建完整的U-Net网络,涵盖数据准备、模型训练、推理部署全流程,是掌握图像分割实战技能的实用指南。

fire9的博客 341

全本地AI代理实战:零数据外泄的本地化Agent工作流搭建

全本地AI代理是一种将大语言模型推理、长期记忆与工具调用完全运行于用户本地设备的智能体架构,其核心原理在于通过进程隔离与标准化协议实现模型层、记忆层和工具层的解耦协同。相比依赖云端API的传统方案,它在数据主权、响应确定性与环境复现性上具备不可替代的技术价值。典型应用场景包括法务合规文档处理、离线工业现场辅助决策、以及高敏感度企业知识管理。本文聚焦Qwen2+Ollama+SurrealDB+Agno技术栈,详解如何构建可落地的全本地AI代理系统,覆盖从硬件适配、向量记忆建模到多模态插件协同的完整链路。

weixin_34194964的博客 650

为什么选SQLite?Fun-ASR历史存储技术细节揭秘

本文介绍了如何在星图GPU平台上自动化部署Fun-ASR钉钉联合通义推出的语音识别大模型语音识别系统 构建by科哥镜像,实现会议录音转写、客服语音整理等典型语音识别应用场景,依托SQLite轻量级历史存储方案,兼顾高性能、易部署与终端用户友好性。

weixin_42465140的博客 87

100个Python代码大全:学完这篇从Python小白到架构师

Python是一种广泛使用的高级编程语言,以其简洁优雅、易于学习和强大的库而闻名。无论你是一位经验丰富的程序员还是初学者,都可以从这种语言的丰富资源中受益。在本篇博客中,我将分享100个实用的Python代码示例,旨在帮助你掌握Python编程,并在日常工作中提高效率。

penggerhe的博客 1万+

分布式计算实战:从原理陷阱到工业级落地

分布式计算是现代高并发、大规模服务的底层基石,其本质是在网络不可靠、时钟不同步、节点随机宕机等现实约束下,通过计算分布、数据分布与状态分布协同实现系统可扩展性与韧性。它并非简单‘加机器’,而是涉及RPC通信、一致性权衡(强一致vs最终一致)、服务发现、容错自愈等关键技术决策。理解阿姆达尔定律可避免盲目扩容,掌握Saga事务、幂等设计、健康探针、混沌工程等实践方法,才能真正构建稳定可用的分布式系统。本文聚焦真实生产环境中的典型问题与解法,覆盖电商订单、库存、日志等高频场景。

weixin_34159110的博客 512

XDS110 USB Debug Probe连接错误-260:CCS Theia环境下的诊断与修复指南

本文详细解析了CCS Theia环境下XDS110 USB Debug Probe连接错误-260的诊断与修复方法。从硬件连接到驱动状态、固件更新到软件配置,提供了全面的排查指南,帮助开发者快速解决这一常见的调试器通信障碍问题,确保嵌入式开发工作流畅进行。

hope8的博客 285

保姆级教程:用NX机电概念设计与RobotStudio实现ModbusTCP联合仿真(附完整代码)

本文提供了一份详细的NX机电概念设计与RobotStudio联合仿真实战教程。通过ModbusTCP协议构建数据桥梁,实现机械行为与机器人控制逻辑的双向实时同步。文章从环境搭建、信号配置、RAPID代码编写到联合调试,逐步拆解了实现数字孪生核心链路的关键步骤,并分享了常见问题排查方法,助力工程师在虚拟环境中提前验证产线协同。

sat99的博客 1099

spring

在应用开发中用的非常多, @Component注解是一个通用注解,可以用在于任何类上。包括普通java类,业务逻辑组件,持久化对象等等。通过@Component注解spring自动创建该类的实例注入SpringIOC容器中。@Bean是用于配置类中声明一个Bean的,它通常是用于配置类的方法上面,表示把这个方法的返回对象注册到Spring IOC容器中。通过@Bean自定义Bean的创建和初始化过程,包括指定Bean的名称、作用域、依赖关系等。1.用途不同@Component用于标识普通的类。

黑冰[̲̅V̲̅I̲̅P̅] 818

全面解析量化价值投资多因子模型的原理

明明知道“买便宜的好公司”是价值投资的核心,但面对3000+只A股,如何快速筛选出“便宜又好”的标的?主观判断总被情绪干扰——明明PE很低的股票,买了却不涨;或者因为“感觉”错过牛股?想规模化投资,但手动分析100只股票就要花一周,根本无法覆盖全市场?量化价值投资的多因子模型,正是解决这些问题的钥匙。它将价值投资的核心逻辑(“便宜”“好公司”)转化为可量化的因子,用数学模型替代主观判断,实现系统化筛选、可回测验证、规模化复制的投资决策。为什么多因子模型能有效预测股票收益?

AI天才研究院 1058

Java面试深度解析:HashMap线程安全与Spring Boot自动配置

在Java开发中,HashMap作为核心数据结构,其线程安全问题是并发编程的经典议题。从JUC包的ConcurrentHashMap分段锁设计,到JDK8引入的CAS优化,反映了Java并发模型向细粒度锁发展的趋势。Spring Boot的自动配置机制通过@Conditional系列注解实现条件化装配,其背后的Starter规范与自动配置类加载流程,体现了约定优于配置的设计哲学。这些技术不仅关系到系统的高并发能力,也直接影响着微服务架构下的组件集成效率。通过分析HashMap死循环问题与Spring Boo

weixin_34152820的博客 334

2025年GESP12月认证C++四级真题解析

摘要 本文包含11道C++编程题目,涵盖指针、数组、函数参数传递、结构体和排序算法等知识点。每题2分,总分30分,主要考察学生对C++基础语法的掌握程度。题目包括指针定义与解引用(第1-4题)、二维/三维数组遍历(第4-5题)、变量作用域(第6题)、函数传参方式(第7题)、结构体操作(第8题)、递归算法(第9题)和排序稳定性判断(第10题)。最后第11题要求补全选择排序算法代码片段。每道题均提供标准答案和详细解析,适合用于检验C++基础知识的掌握情况。

lan_in的博客 317

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

软件工程实务学习心得与实践应用

一、理论筑基:构建完整的知识体系​在学习之初,我对软件工程的认知仅仅停留在编程开发的层面。然而,通过课程学习,我才明白软件工程是一门融合了计算机科学、数学、管理学等多学科知识的综合性学科。从需求分析、设计、编码、测试到维护,每一个阶段都有其严谨的方法论和规范。例如,在需求分析阶段,通过学习使用用例图、用户故事等工具,我学会了如何准确捕捉用户需求,避免因需求模糊而导致后续开发工作的混乱。而在设计阶段,模块化设计、分层架构等理念,让我认识到良好的系统设计对于提高软件的可维护性、可扩展性和可复用性的重要性。这些理

m0_67933720的博客 507

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

Python数据结构实战:从源码到生产级调优

数据结构是程序性能的底层基石,其选择与使用直接影响内存占用、CPU缓存效率、GC压力与并发稳定性。理解动态数组扩容律、哈希表开放寻址机制、堆的惰性删除等原理,才能突破‘理论复杂度’陷阱,在真实业务中规避列表扩容抖动、字典内存膨胀、heapq无法删任意元素等高频问题。本文聚焦Python内置结构(list/dict/heapq)在千万级QPS、实时秒杀、日志聚合等典型场景中的行为差异与调优路径,结合CPython源码剖析、压测数据对比和线上救火案例,揭示从`my_list = []`执行那一刻起,解释器内部发

weixin_30697239的博客 403

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

Java EE 进阶:8.MyBatis 操作数据库(入门)(第一弹)

Java EE 进阶:8.MyBatis 操作数据库(入门)(第一弹)

要努力去发光,而不是被照亮~ 141

Spring Boot 数据源与 MyBatis(-Plus) 自定义配置详解

本文详解 Spring Boot 中单数据源环境下 HikariCP 与 MyBatis-Plus 的自定义配置。通过手动声明 SqlSessionFactory,实现下划线转驼峰、日志输出、一级缓存策略等精细化控制。结合 HikariCP 核心参数调优(如最大连接数、连接生命周期),确保高性能与稳定性。适用于仅需访问单一数据库的场景,避免多数据源复杂性,兼顾性能与可维护性。

BADAO_LIUMANG_QIZHI的博客 205

Docker Compose:搭建本地多服务开发环境

从一个 Java API、PostgreSQL 和 Redis 开始,配置服务名、端口、环境变量、健康检查、数据卷和依赖关系。理解 Compose 文件如何被解析、项目如何隔离,以及哪些数据不能依赖“重建容器”保留,能够把个人开发环境整理成团队可复用的启动入口。

m0_62221377的博客 462

AI全栈实战 | 3.9-01 爬虫入门:requests 加 BeautifulSoup 三分钟跑通,但 90% 的人栽在这 4 个“礼貌“问题上

⚠️:本文内容仅用于。请遵守目标站点的服务条款与相关法律法规,不得对目标服务造成负担,不得采集、传播个人隐私数据。技术是中性的,边界在你手里。爬虫的入门技术(requests 发请求 + BeautifulSoup 解析)三分钟就能学会。真正区分"会用爬虫"和"会做爬虫的人"的,是这篇我们跑通"请求→解析→存储"的最小闭环,然后重点把"合规与礼貌"这根弦给你绷上。

源码获取 | 运行部署 | 定制二开修改,如果回复不及时请耐心等待,请见谅。 419
上一篇: Redis持久化策略与实战指南
下一篇: Redis集群:从单点故障到高可用的进阶之路
Fcw0551
博客等级 码龄2年 330粉丝 115原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值