为什么 SQL 中 NULL 不能用 = 比较:三值逻辑与查询陷阱

副标题 / 摘要 在 SQL 中,NULL 代表“未知”,因此 = 比较不会返回 true。本文解释三值逻辑的机制,并给出正确写法。 目标读者 经常写 SQL 的后端工程师 在查询结果上踩过 NULL 坑的开发者 需要制定查询规范的团队 背景 / 动机 很多人会写: SELECT * FROM t WHERE field = NULL; 然后发现它“不起作用”。原因是 SQL 使用三值逻辑,NULL 不等于任何值(包括 NULL 本身)。 核心概念 NULL 表示未知,不是空字符串或 0 三值逻辑:true / false / unknown 正确判断方式:IS NULL / IS NOT NULL 实践指南 / 步骤 判断 NULL 用 IS NULL 不要用 = 与 NULL 比较 需要替代值时用 COALESCE 对外部输入做明确转换 可运行示例 SELECT id FROM users WHERE deleted_at IS NULL; 使用替代值: SELECT COALESCE(age, 0) FROM users; 解释与原理 NULL = NULL 的结果是 unknown,而不是 true。 SQL 的 WHERE 只保留 true 的行,unknown 会被过滤掉,因此查询为空。 ...

2026年1月24日 · 1 分钟 · map[name:Jeanphilo]

延迟加载(Lazy Loading)是什么:适用场景与代价

副标题 / 摘要 延迟加载可以减少初始加载成本,但也可能引发 N+1 和不可预期的查询。本文给出适用场景与风险。 目标读者 使用 ORM 的后端工程师 需要优化查询策略的开发者 关注性能与成本的团队 背景 / 动机 延迟加载让你“用到才查”,在数据量大时可以避免一次性加载过多。 但如果使用不当,会触发 N+1 查询和隐式性能问题。 核心概念 Lazy Loading:访问关联数据时才触发查询 Eager Loading:一次性加载相关数据 隐式查询:调用属性触发 SQL 实践指南 / 步骤 默认使用延迟加载,但关键路径要显式控制 在批量访问前使用预加载 监控 SQL 数量与慢查询 避免在循环中触发懒加载 可运行示例 # 伪代码:访问属性触发 SQL user = session.query(User).first() orders = user.orders # 这里触发查询 解释与原理 延迟加载把查询时机推迟到真正访问数据时。 如果访问发生在循环内,就可能触发 N+1。 常见问题与注意事项 延迟加载一定更快吗? 不一定,频繁访问时反而更慢。 可以完全禁用延迟加载吗? 可以,但会增加初始化成本。 如何判断是否该用? 看访问路径与数据规模。 最佳实践与建议 对高频接口禁用隐式懒加载 明确预加载策略 使用 ORM 的加载策略配置 小结 / 结论 延迟加载是性能优化工具,但必须与访问模式匹配。 不加控制的懒加载会造成严重性能问题。 参考与延伸阅读 SQLAlchemy / Hibernate Loading Strategies ORM 性能优化实践 元信息 阅读时长:7~9 分钟 标签:延迟加载、ORM、性能 SEO 关键词:Lazy Loading, ORM 元描述:解释延迟加载的适用场景与缺陷。 行动号召(CTA) 找一个慢接口,检查是否存在隐式懒加载,再考虑预加载优化。

2026年1月24日 · 1 分钟 · map[name:Jeanphilo]

Hot100:合并区间(Merge Intervals)排序扫描 ACERS 解析

副标题 / 摘要 合并区间是最典型的“排序 + 线性扫描”问题:先按起点排序,再顺序合并重叠区间。本文按 ACERS 结构拆解题意、核心概念、工程迁移与多语言实现,帮助你形成可复用的区间处理模型。 预计阅读时长:12~15 分钟 标签:Hot100、区间、排序、扫描线、合并区间 SEO 关键词:Merge Intervals, 合并区间, 区间合并, 排序, 扫描线 元描述:合并区间的排序扫描解法与工程应用解析,含复杂度对比与多语言实现。 目标读者 想掌握“区间合并”基础模型的初学者 需要把算法思路迁移到工程场景的中级开发者 正在准备算法面试、希望快速建立区间类题型的求职者 背景 / 动机 区间问题在日程排班、监控窗口、日志聚合、资源分配中非常常见。 如果没有一个统一的合并策略,很容易产生重复统计、冲突判断错误或资源浪费。 因此,“把重叠区间合成最少的不重叠集合”是工程与算法都高频出现的基础能力。 A — Algorithm(题目与算法) 题目还原 给定一个区间数组 intervals,其中 intervals[i] = [starti, endi] 表示第 i 个区间。 请合并所有重叠的区间,并返回一个不重叠的区间数组,且能完整覆盖输入中的所有区间。 输入输出 名称 类型 描述 intervals int[][] 区间数组,元素为 [start, end] 返回 int[][] 合并后的不重叠区间数组 基础示例(官方) 输入 输出 [[1,3],[2,6],[8,10],[15,18]] [[1,6],[8,10],[15,18]] [[1,4],[4,5]] [[1,5]] 合并示意(示例 1) 排序后: [1,3] [2,6] [8,10] [15,18] 合并: [1,3] + [2,6] -> [1,6] 结果: [1,6] [8,10] [15,18] 思路概览 按区间起点升序排序(起点相同则按终点升序)。 线性扫描,维护当前合并区间 [cur_start, cur_end]。 如果下一个区间 next_start <= cur_end,则合并为 cur_end = max(cur_end, next_end)。 否则将当前区间放入结果,并以新起点开始下一段合并。 C — Concepts(核心思想) 核心概念 概念 含义 作用 重叠 next_start <= cur_end 判断是否需要合并 合并 cur_end = max(cur_end, next_end) 扩展当前区间 排序 按起点排序 让重叠区间相邻 方法类型 排序 + 线性扫描 + 贪心合并。 ...

2026年1月24日 · 6 分钟 · map[name:Jeanphilo]

GitFlow vs GitHub Flow:工作流差异与选择

副标题 / 摘要 GitFlow 强调多分支与发布管理,GitHub Flow 强调持续集成与快速迭代。本文对比二者并给出选型建议。 目标读者 负责团队协作流程的技术负责人 需要选择 Git 工作流的团队 希望提升发布效率的工程师 背景 / 动机 工作流决定协作效率。 选错工作流会导致发布迟缓、分支混乱与冲突高发。 核心概念 GitFlow:feature/develop/release/hotfix 多分支模型 GitHub Flow:短分支 + PR + main 始终可部署 CI/CD:自动化测试与交付 实践指南 / 步骤 评估发布频率:频繁发布更适合 GitHub Flow 评估团队规模:大型团队可能偏 GitFlow 统一分支命名与合并规范 强制 CI 通过再合并 设定回滚与热修策略 可运行示例 # GitHub Flow 的典型流程 git checkout -b feature/payment # 开发并提交 git push origin feature/payment # 提 PR -> CI 通过 -> 合并到 main 解释与原理 GitFlow 适合发布周期长、需要严格版本管理的场景。 GitHub Flow 适合快速迭代、持续交付的场景。 ...

2026年1月24日 · 1 分钟 · map[name:Jeanphilo]

Web 与桌面应用的容错管理差异:思路与实践

副标题 / 摘要 Web 与桌面应用的容错目标不同:Web 更关注高可用与多副本,桌面更关注本地恢复与数据完整性。本文给出对比与实践建议。 目标读者 负责跨端系统设计的工程师 需要制定容错策略的技术负责人 关注可靠性与用户体验的开发者 背景 / 动机 同样是“容错”,Web 关心的是“服务不中断”,桌面关心的是“用户不丢数据”。 如果把 Web 的策略照搬到桌面,或反之,效果往往不佳。 核心概念 Web 容错:多副本、负载均衡、熔断、降级 桌面容错:本地事务、自动恢复、崩溃保护 状态管理:无状态 vs 有状态 实践指南 / 步骤 明确容错目标:可用性、数据完整性、体验连续性 Web 端优先无状态,用多副本与自动扩缩容 桌面端优先保护本地状态(自动保存、崩溃恢复) 建立错误分级:可重试、可降级、必须失败 跨端一致性:必要时用同步/冲突解决策略 可运行示例 下面展示桌面应用“自动保存”的最小示例: import json import time state = {"text": "draft"} def autosave(state, path="autosave.json"): with open(path, "w") as f: json.dump(state, f) if __name__ == "__main__": for i in range(3): state["text"] += "!" autosave(state) time.sleep(0.1) print("saved") 解释与原理 Web 服务通过多副本与负载均衡保证“任一实例失败不影响整体”。 桌面应用无法依赖多副本,只能通过本地持久化与恢复机制容错。 ...

2026年1月24日 · 1 分钟 · map[name:Jeanphilo]

分布式 vs 集中式版本控制:优势、劣势与适用场景

副标题 / 摘要 分布式版本控制把“历史”分散到每个开发者本地,带来更强的离线能力与分支灵活性,但也引入更高的协作复杂度。本文给出清晰对比。 目标读者 需要理解 Git / SVN 差异的工程师 负责制定团队工作流的技术负责人 正在从 SVN 迁移到 Git 的团队 背景 / 动机 版本控制不是工具问题,而是协作效率问题。 分布式与集中式的差异会直接影响团队工作流、代码评审与发布节奏。 核心概念 集中式 VCS:单一中央仓库(SVN) 分布式 VCS:每个开发者都有完整历史(Git) 分支模型:分支的创建、合并成本不同 实践指南 / 步骤 评估团队规模与协作模式 看是否需要离线工作 评估分支与合并频率 选择合适的工作流(GitFlow/GitHubFlow) 建立统一的代码评审与发布规范 可运行示例 比较 SVN 与 Git 的本地历史能力: # Git:离线查看历史 git log -5 # SVN:依赖服务器 svn log -l 5 解释与原理 分布式 VCS 把历史复制到本地,允许开发者离线查看与提交。 集中式 VCS 依赖中心仓库,操作更集中、权限更清晰,但分支成本高。 常见问题与注意事项 分布式 VCS 是否更安全? 历史分散降低单点风险,但也需要更强的权限策略。 集中式 VCS 是否更简单? 对小团队可能更简单,但扩展性差。 Git 学习成本高吗? 相对高,但收益巨大。 ...

2026年1月24日 · 1 分钟 · map[name:Jeanphilo]

什么时候使用异步通信:场景、收益与代价

副标题 / 摘要 异步通信提升解耦与吞吐,但引入一致性与可观测性成本。本文给出适用场景与落地指南。 目标读者 负责系统架构与通信模式选型的工程师 设计消息队列与事件流的开发者 希望提升系统稳定性的团队 背景 / 动机 同步调用容易形成链路耦合与级联失败。 异步通信通过消息缓冲解耦,提高系统韧性,但代价是复杂度提升。 核心概念 同步通信:请求-响应,强一致 异步通信:事件驱动,最终一致 消息队列:解耦、削峰、缓冲 实践指南 / 步骤 判断是否必须强一致 评估下游稳定性与峰值压力 明确消息语义(至少一次/至多一次) 引入可观测性(重试、死信) 设计幂等与补偿机制 可运行示例 import queue import threading import time q = queue.Queue() def producer(): for i in range(5): q.put(i) time.sleep(0.1) def consumer(): while True: item = q.get() print("consume", item) q.task_done() if item == 4: break if __name__ == "__main__": threading.Thread(target=consumer).start() producer() 解释与原理 异步通信把“耦合”从时间维度中移除。 上游不必等待下游响应,减少链路阻塞与级联失败。 ...

2026年1月24日 · 1 分钟 · map[name:Jeanphilo]

什么是 rebase:作用、风险与正确用法

副标题 / 摘要 rebase 可以让提交历史更线性,但也会重写历史。本文解释它的价值、风险与使用边界。 目标读者 希望保持整洁提交历史的开发者 团队协作中经常处理冲突的人 需要制定 Git 规范的技术负责人 背景 / 动机 合并分支会产生大量 merge commit,让历史难以阅读。 rebase 能把分支“挪到”最新主线之上,形成更清晰的线性历史。 核心概念 rebase:把分支的提交“搬到”新基线 历史重写:提交哈希会变化 交互式 rebase:整理、合并提交 实践指南 / 步骤 仅对本地未推送的分支使用 rebase 拉取最新主线再 rebase 解决冲突并继续 必要时用交互式 rebase 压缩提交 公共分支禁止 rebase 可运行示例 # 在 feature 分支上 git fetch origin git rebase origin/main # 若冲突 # 解决后: git add . git rebase --continue 解释与原理 rebase 会“重放”每一个提交到新基线上,因此提交哈希会改变。 这让历史更整洁,但也意味着共享分支上会引发冲突与丢失提交的风险。 常见问题与注意事项 rebase 与 merge 有什么区别? rebase 改写历史,merge 保留历史分叉。 ...

2026年1月24日 · 1 分钟 · map[name:Jeanphilo]

什么是竞争条件:Race Condition 的本质与示例

副标题 / 摘要 竞态条件是并发中最隐蔽的 bug 类型之一,源于对共享状态的非原子操作。本文给出可运行示例与规避方法。 目标读者 需要理解线程安全的工程师 做并发开发与性能优化的开发者 负责可靠性与稳定性的技术负责人 背景 / 动机 竞态条件会导致偶发错误:你很难复现,但它确实存在。 理解其成因,才能正确使用锁、原子操作与无锁结构。 核心概念 共享状态:多线程同时访问的变量 非原子操作:读-改-写不是一个不可分割的步骤 临界区:必须串行访问的代码区域 实践指南 / 步骤 识别共享状态 确定临界区 使用锁或原子操作保护 最小化临界区范围 用竞态检测工具验证 可运行示例 import threading counter = 0 def inc(): global counter for _ in range(100000): counter += 1 if __name__ == "__main__": t1 = threading.Thread(target=inc) t2 = threading.Thread(target=inc) t1.start() t2.start() t1.join() t2.join() print(counter) 在部分解释器/实现中可能输出小于 200000,这就是竞态。 解释与原理 counter += 1 并不是原子操作,它包含读取、加一、写回三个步骤。 两个线程交错执行时会丢失更新。 ...

2026年1月24日 · 1 分钟 · map[name:Jeanphilo]

什么是死锁:成因、示例与规避策略

副标题 / 摘要 死锁发生在多个线程互相等待对方持有的资源。本文解释成因、给出示例,并总结规避方法。 目标读者 需要理解并发风险的工程师 负责系统可靠性的开发者 做并发设计评审的技术负责人 背景 / 动机 死锁通常在压力或线上才出现,一旦发生会导致服务完全卡住。 理解死锁条件并提前规避,是并发系统的必修课。 核心概念 互斥:资源一次只能被一个线程占用 占有且等待:拿着资源还要等另一个资源 不可抢占:资源不能被强制夺取 循环等待:形成等待环 实践指南 / 步骤 统一锁顺序(按固定顺序获取) 减少锁数量 设置超时与回退 用锁分层减少交叉等待 监控阻塞与持锁时间 可运行示例 import threading import time lock_a = threading.Lock() lock_b = threading.Lock() def t1(): with lock_a: time.sleep(0.1) with lock_b: pass def t2(): with lock_b: time.sleep(0.1) with lock_a: pass if __name__ == "__main__": threading.Thread(target=t1).start() threading.Thread(target=t2).start() time.sleep(1) print("可能已死锁") 解释与原理 当 t1 拿着 A 等 B,而 t2 拿着 B 等 A,就形成循环等待。 满足四个必要条件时死锁发生。 ...

2026年1月24日 · 1 分钟 · map[name:Jeanphilo]