自定义博客皮肤VIP专享

*博客头图:

格式为PNG、JPG,宽度*高度大于1920*100像素,不超过2MB,主视觉建议放在右侧,请参照线上博客头图

请上传大于1920*100像素的图片!

博客底图:

图片格式为PNG、JPG,不超过1MB,可上下左右平铺至整个背景

栏目图:

图片格式为PNG、JPG,图片宽度*高度为300*38像素,不超过0.5MB

主标题颜色:

RGB颜色,例如:#AFAFAF

Hover:

RGB颜色,例如:#AFAFAF

副标题颜色:

RGB颜色,例如:#AFAFAF

自定义博客皮肤

-+
  • 博客(71)
  • 收藏
  • 关注

原创 从感知机到 Attention:我用 PyTorch 打穿 CS188 机器学习终章 CS188 Proj5 学习笔记

本文围绕 CS188 Project 5 的机器学习部分展开,以 PyTorch 基础为起点,依次梳理 Tensor、Shape、Batch、DataLoader 等核心概念,并完成从感知机、非线性回归、MNIST 数字分类、语言识别 RNN、手写卷积函数到 Attention 机制的完整实现。文章不仅记录代码解法,也重点解释输入输出维度、训练流程、损失函数、激活函数选择和模型结构设计,适合 PyTorch 零基础学习者借助项目实践建立机器学习整体框架。

2026-06-04 23:26:50 770 5

原创 一次 IO 到底经历了什么?一篇讲清操作系统的五种 IO 模型

一次IO分为“等数据就绪”和“拷贝数据”两个阶段。五种IO模型的区别正源于对这两个阶段的处理方式:阻塞IO全程阻塞;非阻塞IO轮询,浪费CPU;多路复用(如epoll)让内核批量通知就绪事件,高效支持高并发;信号驱动IO通过信号通知,但复杂且受限;异步IO则将两阶段全交由内核,进程无需等待,真正实现异步。关键在于:只有异步IO在拷贝阶段不阻塞进程,其余均为同步IO。

2026-10-14 22:13:45 341

原创 进程之间如何“对话”?一篇讲清六种进程间通信方式

进程间隔离确保安全,但通信需依赖内核。六种经典IPC机制各具特点:管道与FIFO为单向字节流,适用于亲缘或任意本地进程;消息队列支持有边界消息和按类型接收,适合结构化通信;共享内存零拷贝、性能最优,但需配合信号量实现同步;信号量仅用于同步控制;Socket是唯一跨机器方案,支持全双工通信,通用性强。选择时需权衡性能、同步需求与跨域能力。

2026-10-14 22:04:23 372

原创 程序为什么会卡在死锁里?从四个必要条件到预防与解除

两个线程因锁顺序不一致导致死锁:A持l1等l2,B持l2等l1,形成循环等待。死锁四条件(互斥、持有等待、不可抢占、循环等待)必须同时满足。预防法中最有效的是统一加锁顺序,破坏“循环等待”;避免法用银行家算法动态判断安全性,但需预知最大需求;检测恢复法如数据库通过资源分配图发现环并回滚事务。实际开发中,jstack可定位死锁,修复关键在规范锁获取顺序。

2026-10-13 22:41:03 325

原创 程序里的地址是真实内存地址吗?从虚拟内存到地址转换

先想一下如果不用虚拟内存会怎样,也就是程序里直接写物理地址:两个程序想同时跑,得提前商量好各自占哪块物理内存,谁也别碰谁的。程序一多,这种"排座位"根本排不过来。程序编译的时候就得知道自己会被加载到哪个物理地址。可物理内存上哪里有空洞是运行的时候才知道的。用一个共享库,每个进程都得在自己的物理内存里放一份拷贝,浪费。

2026-10-13 22:23:14 377

原创 用户态与内核态到底怎么切换?从系统调用到上下文切换

write(fd, buf, n) 一次系统调用,触发特权级从用户态(ring3)切换至内核态(ring0),硬件通过 syscall 指令完成栈切换、寄存器保存与跳转。此过程称为 mode switch,仅改变执行环境,不更换线程。而 context switch 是内核调度器主动切换线程,发生在内核态内部。二者本质不同:前者是“进门”,后者是“换人”。阻塞系统调用才会引发 context switch,普通调用仅含 mode switch,因此“系统调用慢因上下文切换”是误解。

2026-10-12 22:48:54 309

原创 进程、线程、协程有什么区别?从资源分配到调度,一篇理清

进程、线程、协程分属资源分配、调度、用户态调度三个层次。进程独占资源,线程共享进程资源,协程在用户态切换,无内核开销。线程切换仅换栈和寄存器,进程切换需换页表致TLB失效,开销大;协程切换纳秒级,栈小且协作式,可支持数十万并发。但协程不提升并行能力,仅优化切换效率。选型应依隔离性、共享需求与并发规模而定。

2026-10-12 22:45:01 323

原创 一个 Java 类是如何进入 JVM 的?一篇讲透类加载机制

JVM 只识别 .class 文件,类加载机制负责将字节码转化为内存中的可执行类。整个过程分为加载、链接(验证、准备、解析)和初始化三阶段。加载时通过全限定名定位字节流,生成 Class 对象;链接确保字节码安全并完成符号引用解析;初始化执行静态变量赋值与静态代码块。类仅在主动引用(如 new、反射、子类初始化等)时加载,被动引用不触发。类加载器采用双亲委派机制保障安全与避免重复加载,但可通过上下文类加载器、自定义加载器(如 Tomcat)或 SPI 等方式打破。最终,类的唯一性由加载器与全限定名共同决定。

2026-10-11 23:17:45 234

原创 Redis 断电后,数据还在吗?一篇讲透 RDB 与 AOF

Redis 的数据全在内存里,进程一挂内存就没了。所以问题很直接:断电、kill -9、机器重启之后,重新起来,原来的 key 还在不在?答案取决于有没有配持久化。RDB和AOF,都是把内存里的数据落到磁盘,重启时再读回内存。

2026-10-11 23:09:01 309

原创 本地缓存已经很快了,为什么还要用 Redis?

本地缓存虽快(纳秒级),但受限于多实例一致性、内存占用、重启丢失及跨服务共享等问题,难以满足分布式系统需求。Redis 因数据集中、支持共享、持久化和高可用,成为更可靠的选择。实际应用中常采用“本地缓存 + Redis”多级缓存架构:本地缓存拦截热点请求,降低 Redis 压力;Redis 保障全局一致与容灾。仅当系统单实例、数据静态且对一致性容忍度高时,本地缓存才足够。

2026-10-11 23:04:27 269

原创 直接调用大模型 API 不就行了,为什么还需要 AI 网关?

本文探讨应用如何高效连接模型,指出直接调用API在多服务、多模型场景下面临代码冗余、密钥分散、成本难控、监控重复等问题。提出AI网关作为统一入口,实现协议标准化、密钥集中管理、限流配额控制、负载均衡与容灾、请求缓存及安全审计,显著提升可维护性与安全性。与MCP(模型连接工具)不同,AI网关聚焦“应用→模型”链路的统一管控,适用于多模型、多团队协作场景,单服务小规模使用时无需引入。

2026-10-11 22:59:56 203

原创 MySQL索引失效的场景总结

索引失效主要因条件破坏排序或优化器判断全表扫描更优。常见原因包括:索引列被函数/运算包裹、隐式类型转换、联合索引未用最左前缀、范围查询后接等值条件、LIKE '%xxx'、OR 一侧无索引、否定条件(如 !=)、IS NULL 被误判,以及表小或区分度低导致优化器放弃索引。关键通过 EXPLAIN 查看 type(非 ALL)、key(非 NULL)和 rows 判断是否走索引。避免失效需保持条件“干净”,合理设计索引顺序,并关注执行计划。

2026-10-10 23:46:13 697

原创 一条 SQL 发出去之后,MySQL 到底做了什么?

一条SQL从客户端发出到返回结果,需穿越MySQL的Server层与存储引擎层。流程为:连接器建立连接并认证权限;查询缓存(8.0已移除)尝试命中;分析器进行词法与语法解析;优化器生成执行计划;执行器依据计划调用存储引擎接口取数,决定是否使用索引或全表扫描,并处理回表问题;最终数据由引擎层返回。分层设计使存储引擎可替换,且清晰划分日志归属,为两阶段提交提供基础。

2026-10-10 23:40:09 247

原创 MCP 到底是什么?大模型如何连接外部工具与数据?

大模型本身无法直接访问外部数据,其能力依赖外部程序通过“函数调用”(Tool Use)机制实现。当前各平台接口不统一,导致应用与工具对接成本高,形成M×N的适配困境。为此,Anthropic推出的MCP(Model Context Protocol)以标准化协议统一通信,将对接成本降至M+N。MCP基于JSON-RPC 2.0,定义了三类核心原语:由模型决定调用的Tools、由应用控制读取的Resources,以及用户触发的Prompts。通过stdio或Streamable HTTP传输,实现本地或远程服

2026-10-09 23:07:11 409

原创 WebSocket 和 SSE 有什么区别?从实时通信到 AI 流式输出

HTTP 的“一问一答”模型无法支持服务器主动推送,催生了 SSE 与 WebSocket。SSE 以普通 HTTP 响应为基础,实现服务器单向流式推送,天然兼容现有基础设施,自动重连,适合纯单向场景。而 WebSocket 提供双向全双工通信,适用于需实时交互的复杂应用。在 AI 流式输出中,因只需服务器向客户端推送文本,且追求简单高效,SSE 成为首选;但当进入需用户实时干预的 agent 场景时,WebSocket 优势显现。选型核心:单向用 SSE,双向则上 WebSocket。

2026-10-09 22:54:05 422

原创 从 Serializable 到 JSON:理解 Java 的序列化与反序列化

JDK序列化依赖Serializable与ObjectOutputStream,将对象转为字节流,但存在跨语言、跨版本脆弱及反序列化漏洞风险。反序列化不走构造器,字段初始化失效,需用readResolve保障单例。serialVersionUID显式声明可避免兼容问题,transient和static字段不参与序列化。相比之下,Jackson等JSON序列化基于属性(如getter),transient不影响结果,更灵活安全,且支持跨语言、容错性强。

2026-10-08 22:29:42 449

原创 Java 反射到底是什么?一篇讲清原理、应用与优缺点

反射将方法调用从编译期推迟到运行期,实现动态调用,但带来性能损耗、编译检查缺失与封装破坏。其核心是Class对象,可通过.class、getClass()或Class.forName()获取,后者会触发类初始化。反射可访问私有成员(需setAccessible(true)),但需承担装箱、访问检查及无法内联的开销,性能约为直接调用的十几倍。广泛用于框架中解析注解、动态代理等场景,如Spring、MyBatis、Jackson等,但需权衡灵活性与性能、安全性。

2026-10-08 22:11:58 443

原创 Spring Boot的核心配置文件一览及其加载顺序

Spring Boot 启动时按固定顺序加载配置,优先级从低到高:默认属性、@PropertySource、配置文件(classpath及file路径)、random、环境变量、系统属性、JNDI、ServletContext/ServetConfig参数、SPRING_APPLICATION_JSON、命令行参数、测试注解配置等。配置文件按位置(如classpath:/、file:./config/*)和类型(.properties > .yml,profile专用 > 普通)叠加,同名键后生效。boot

2026-10-07 23:55:28 570

原创 Spring中过滤器和拦截器的区别是什么?

Filter 与 Interceptor 均可实现横切逻辑,但层级不同:Filter 属于 Servlet 规范,由 Tomcat 调用,位于 DispatcherServlet 外;Interceptor 是 Spring MVC 机制,由 DispatcherServlet 调用,位于其内。Filter 可拦截原始请求/响应,需手动包装 HttpServletRequestWrapper 等,且请求体仅能读一次;Interceptor 可访问 HandlerMethod,支持基于注解的精细控制(如权限校

2026-10-07 23:54:45 539

原创 签到、UV、附近的人:Redis 这三种结构你用对了吗?

签到、统计 UV、找附近的人,三个需求看起来风马牛不相及,Redis 各给了一个专门的结构:Bitmap、HyperLogLog、GEO。但它们其实都不是新类型。用TYPEBitmap 和 HyperLogLog 的TYPE都是string,底层就是 SDS;GEO 的TYPE是zset,底层是跳表。它们是 Redis 在已有类型上做的一层包装,把同一份数据换一种角度解释。搞清这层关系,用起来就不会觉得它们是天上掉下来的东西。

2026-10-04 22:15:27 406

原创 Redis 也能做消息队列?从 Stream 的存储讲到消费确认

Redis Stream 作为消息队列,解决了 List 和 Pub/Sub 的三大缺陷:支持持久化、ACK 机制与消费组。每条消息带唯一递增 ID,支持回溯与精确控制;底层用 radix tree + listpack 高效存储;通过 XGROUP 实现多消费者分摊,结合 PEL 与 XACK 保证至少一次投递,配合 XCLAIM 处理失败重试。虽不支持跨机房强一致,但适合已有 Redis、对可靠性要求中等的场景,是轻量级可靠消息队列的理想选择。

2026-10-04 22:14:48 444

原创 Redis ZSet 为什么选择跳表?排行榜背后的实现原理

Redis 的 ZSet 通过跳表(skiplist)和哈希表(dict)双结构实现高效排序与快速查找:跳表支持按 score 排序及范围查询,哈希表实现 member 到 score 的 O(1) 查找。元素少时采用 listpack 编码以节省内存。跳表层高随机生成,插入删除无需平衡,实现简单且性能稳定。相比平衡树,跳表代码更简洁,范围查询更直观。典型应用包括排行榜、延时队列等,凭借 score 排序与去重特性,成为 Redis 中核心的有序集合数据结构。

2026-10-04 22:08:16 335

原创 Redis 哈希表如何边扩容边服务?讲透渐进式 Rehash

Redis 的 Hash 编码在字段少时使用 listpack 节省内存,字段多或值长则转为 dict 哈希表。dict 采用渐进式 Rehash 机制,通过双哈希表(ht[0] 和 ht[1])分批迁移数据,避免扩容卡顿。插入、删除、查找操作中顺带搬迁,结合 serverCron 定期主动推进,确保 rehash 不停滞。负载因子 ≥1 触发扩容(取最近 2 的幂),<0.1 触发缩容。哈希冲突用链地址法解决,4.0 后改用 SipHash 抗碰撞。但大 Hash 内存开销显著:dictEntry 指针与

2026-10-04 21:51:48 404

原创 Redis Set 如何节省内存?从整数集合到哈希表的设计取舍

Redis Set 通过 intset、listpack、hashtable 三种编码实现内存优化。纯整数集合优先使用紧凑的 intset(O(1) 查找,但插入需移动元素),超限或含非整数则转为 listpack,再超限转为 hashtable。编码转换单向不可逆,以平衡内存与性能。集合运算中,SINTER 复杂度取决于最小集合大小和编码类型,SUNION/SDIFF 为线性;SINTERCARD 可快速估算交集数量。适用于去重、关系查询、抽奖等场景,但大规模计数建议用 HyperLogLog 节省内存。

2026-10-03 23:46:38 470

原创 Redis List 是链表吗?从 Ziplist 到 Quicklist 揭开底层实现

List 不是传统意义上的链表,而是 3.2 后引入的 quicklist:由双向链表连接多个 listpack(或 ziplist)节点构成。每个节点为紧凑数组,既保留链表的分段灵活性,又通过连续内存提升缓存效率。其设计规避了 ziplist 的连锁更新问题(因 prevlen 改为 backlen),并平衡了指针开销与插入性能。两端操作(如 LPUSH/RPOP)为 O(1),中间访问则为 O(n),适合队列/栈场景,不适用于频繁中间定位。

2026-10-03 23:34:53 537

原创 Redis String 为什么不直接用 C 字符串?从 SDS 到三种编码

Redis String 底层使用 SDS(Simple Dynamic String)解决 C 字符串的长度查询慢、二进制不安全和拼接复杂等问题。SDS 支持 O(1) 长度获取、二进制安全存储,并通过智能扩容策略(小于 1MB 翻倍,大于等于 1MB 每次加 1MB)提升性能。根据值的类型与大小,String 采用三种编码:int(整数)、embstr(≤44 字节短字符串,内存连续)、raw(长字符串或被修改后)。编码可动态切换,适用于缓存、计数器、分布式锁和 Session 共享等场景。单个 Str

2026-09-30 23:36:49 438

原创 Redis 的数据到底怎么存?一篇理清数据类型与底层结构

Redis 的五种数据类型提供逻辑接口,而实际内存存储由编码决定。OBJECT ENCODING 可揭示底层结构:如 embstr 用于短字符串,listpack 优化小集合,quicklist 统一列表实现。通过 redisObject 结构,类型、编码与底层数据分离,支持高效内存管理与对象共享。不同编码根据数据特征自动切换,提升性能与空间利用率。

2026-09-30 23:16:01 229

原创 Bean的生命周期

Spring Bean 创建过程包含六个核心步骤:实例化、注解扫描、循环依赖处理、属性填充、初始化与销毁注册。关键回调链贯穿始终——@Autowired 注入在属性填充阶段完成,@PostConstruct 由 BeanPostProcessor 在初始化前调用,AOP 代理则在初始化后通过 postProcessAfterInitialization 生成。理解 doCreateBean 主干流程及各阶段扩展点,可清晰掌握 Spring 容器的底层机制。

2026-09-29 23:14:44 1539

原创 如何快速定位线上OOM

线上服务崩溃,日志显示 java.lang.OutOfMemoryError: Java heap space,但根本原因可能是内存泄漏。需先区分 OOM 类型:Java heap space 与 GC overhead limit exceeded 均指向堆内存无法回收;Metaspace、Direct buffer memory 等则涉及元空间或堆外内存。容器中出现 OOMKilled 是系统强制杀进程,非 JVM 抛出,需关注总内存(含线程栈、直接内存等)是否超 cgroup 限制。排查时应配置 -X

2026-09-29 22:44:01 396

原创 6篇文章讲清楚Git:Git 团队开发实践:从 Pull Request 到版本发布 (6/6)

本文详解多人协作开发中的核心流程与工具:通过 Fork、Clone、Pull Request 实现代码协同,对比 Git Flow、GitHub Flow 等四种工作流,指导团队根据发布频率选择合适模式。重点强调功能分支短命、PR 保持精简、合并前同步主干等实践。同时讲解 tag 标记版本、语义化版本规范及 .gitignore 常见陷阱,如已跟踪文件无法忽略等问题,确保协作高效、代码整洁。

2026-09-28 19:29:10 462

原创 6篇文章讲清楚Git:Git 实用技巧:暂存现场、挑选提交与定位 Bug (5/6)

本文聚焦Git实战命令,涵盖stash临时保存工作现场、cherry-pick精准移植提交、log/blame/bisect定位问题根源,以及交互式rebase整理提交历史。通过具体场景与用法解析,帮助开发者高效处理分支切换、修复合并、代码溯源与历史清理,提升协作效率与调试能力。

2026-09-28 19:15:56 371

原创 6篇文章讲清楚Git:Git 撤销与恢复:reset、revert、restore 应该怎么选?(4/6)

撤销操作的关键在于判断改动所处阶段:未 add 用 git restore,已 add 用 git restore --staged,已 commit 未 push 用 git reset(按需选 --soft/--mixed/--hard),已 push 则用 git revert 安全抵消。仅修改最后一次提交可用 commit --amend。所有危险操作后可借助 reflog 恢复。强推务必使用 --force-with-lease,且仅限私有分支。

2026-09-26 21:35:08 332

原创 6篇文章讲清楚Git:Git 远程协作:fetch、pull、push 到底有什么区别?(3/6)

本文厘清 Git 中 main、origin、origin/main 三者的区别:origin 是远程仓库的别名,main 是本地分支,origin/main 是远程跟踪分支,记录上次同步时的远程状态。它不会自动更新,需通过 git fetch 手动同步。设置上游分支后,可简化 push/pull 操作,并通过 git status 直观查看本地与远程的差异。fetch 仅下载不修改工作区,适合预览变更;pull 等价于 fetch + merge,但可能引发冲突,建议分步操作。推送被拒因非快进(non-f

2026-09-26 21:26:27 483

原创 6篇文章讲清楚Git:Git 分支与合并:一次讲清 HEAD、merge 和 rebase (2/6)

分支是指向提交的可变指针,本质为一小文件。HEAD 指向当前分支,切换即改文件内容。游离 HEAD 会丢失未关联分支的提交。分支操作如创建、删除、查看均高效。合并分快进与合并提交,--no-ff 可保留分支历史。merge 保留真实分叉,rebase 重写提交使历史线性,但不可用于公共分支,以免破坏协作一致性。

2026-09-25 21:45:41 320

原创 6篇文章讲清楚Git:Git 到底是怎么管理代码的?从工作区、暂存区到提交历史 (1/6)

版本控制解决代码管理中的回溯、追踪与协作难题。Git 是本地版本控制工具,通过工作区、暂存区、本地仓库和远程仓库四区域实现高效管理;其基于快照而非差异的存储机制,支持快速分支、离线操作和历史追溯。暂存区允许选择性提交,提升变更粒度控制。核心对象(blob、tree、commit)以内容哈希命名,确保数据一致性与去重。add 和 commit 分步执行,分别准备内容与生成提交。git status 与 diff 命令帮助清晰掌握状态差异,是掌握 Git 的关键基础。

2026-09-25 21:37:46 4249 1

原创 Redo Log 和 Binlog 为什么需要两阶段提交?

MySQL 通过两阶段提交解决 redo log 与 binlog 不一致问题:事务先写 redo log 并标记 prepare,再写 binlog,最后完成 commit。崩溃恢复时,若 redo 为 prepare 状态,则检查 binlog 是否完整,据此决定提交或回滚,确保主从数据一致。虽需额外日志开销,但组提交机制通过合并 fsync 优化性能。此机制非分布式 2PC,而是保障单实例主从一致的关键设计。

2026-09-24 23:50:16 293

原创 Java中垃圾回收器 G1 和 CMS 有什么区别?

CMS 与 G1 均为应对大堆下长停顿问题的垃圾回收器。CMS 追求最短停顿,通过并发标记清除实现低延迟,但存在内存碎片和并发模式失败风险;G1 则以 Region 为单位,采用“垃圾优先”策略,在可预测时间内回收价值最高的区域,避免碎片并支持停顿时间可控。核心差异在于对象消失处理:CMS 使用增量更新,G1 采用 SATB 快照机制。随着 JDK 9 后 CMS 被废弃,G1 成为默认选择,尤其适合大堆场景。

2026-09-24 23:42:03 298

原创 Binlog:MySQL 主从复制和数据恢复的基础

binlog是MySQL Server层的逻辑归档日志,记录数据变更操作,支持主从复制与时间点恢复。相比redo log的物理循环写,binlog为追加式、跨引擎、可长期保留。其格式有STATEMENT(小但可能不一致)、ROW(大但确定,现默认)、MIXED(自动切换,少用)。写入通过binlog cache缓冲,由sync_binlog控制落盘。主从复制依赖binlog经dump、IO、SQL线程实现解耦并行;结合全量备份可实现PITR恢复。因逻辑性、持久性与通用性,binlog不可被redo log替

2026-09-23 21:27:08 418

原创 Undo Log:事务回滚是怎么实现的?

undo log 是 InnoDB 实现事务回滚与 MVCC 的核心机制。它记录修改的反向操作(如“删除主键”“还原旧值”),而非原始数据,确保回滚时仅撤销自身影响。undo log 本身需通过 redo log 保障持久性,且其清理依赖 purge 线程判断是否仍有事务需要旧版本。长事务会阻塞 undo 清理,导致日志堆积,引发表空间膨胀,是线上性能隐患。

2026-09-23 21:22:28 259

原创 Redo Log:MySQL 为什么能够实现崩溃恢复?

InnoDB 通过 WAL(Write-Ahead Logging)实现崩溃恢复:事务提交只需确保 redo log 落盘,而非数据页。redo log 采用循环写,记录物理逻辑修改,以顺序写和小体积保障性能。通过 LSN 比较判断需重放的变更,崩溃恢复时先重放 redo 再回滚未提交事务。redo log 与 binlog 需协同保证一致性,依赖两阶段提交避免主从不一致。

2026-09-22 23:55:20 258

空空如也

空空如也

TA创建的收藏夹 TA关注的收藏夹

TA关注的人

提示
确定要删除当前文章?
取消 删除