- 博客(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关注的人
RSS订阅