技术笔记整理

技术体系化整理、工程实践与开发笔记。

专题分组

二级专题与三级分类

7 个下级
当前页继续展示这个根分类下的全部文章;上面的二级专题和三级分类用于快速定位更细的主题入口。
数据结构
6 分钟

平衡二叉树AVL

这篇笔记面向正在学习数据结构和索引原理的读者,梳理 AVL 树作为自平衡二叉搜索树的基本定义、操作流程与应用价值。正文先用“任意结点左右子树高度差不超过 1”界定平衡条件,并引入高度和平衡因子 BF,说明 AVL 通过保持树高接近对数级来让查找、插入、删除稳定在 O(log n)。在基本操作部分,查找沿用普通 BST 的比较路径,不需要旋转;插入则先按 BST 规则放置新节点,再自底向上更新高度和 BF,一旦出现 |BF|>1 就在局部执行单旋或双旋。文章重点拆分 LL、RR、LR、RL 四种失衡形态:左左和右右分别用一次右旋或左旋修复,左右和右左则需要先处理子节点方向再旋转失衡节点。删除部分强调先按 BST 的叶子、单孩子、双孩子规则完成结构删除,再回溯检查平衡,且一次删除可能在多层触发旋转。最后,笔记把 AVL 的高度控制思想延伸到 B 树、B+ 树和 MySQL InnoDB、PostgreSQL、SQLite 等索引场景,帮助读者理解平衡树为什么适合动态有序且频繁增删的数据维护。

数据结构
4 分钟

哈夫曼树与哈夫曼编码

这篇笔记聚焦哈夫曼树与哈夫曼编码在无损压缩中的基本原理:根据符号出现次数或概率分配变长二进制码,让高频符号更短、低频符号更长,从而降低加权平均码长。正文把哈夫曼编码定位为前缀码中的平均码长最优方案,并用带权路径长度 WPL 公式说明其目标是寻找一棵使权值与码长乘积总和最小的满二叉树。构造部分给出典型流程:统计权值,将每个符号作为单节点树放入小根堆,反复合并权值最小的两棵树,直到得到唯一根树,再按左 0 右 1 的路径生成码字。示例使用 a 到 f 的频率演示树形结构、各字符码字以及 face 被拼接成比特串的过程,同时提醒左右孩子次序不同会导致具体码字不同,但总加权码长保持一致。文章还补充了哈夫曼树的结构性质与实现代价,包括 n 个叶子对应 2n-1 个节点、小根堆构造复杂度为 O(nlogn)、解码时从根按 0/1 走到叶子再回根继续。应用场景覆盖 ZIP、GZIP、PNG、MP3、JPEG、HTTP/2 HPACK、HTTP/3 QPACK、嵌入式存储和搜索索引等,适合数据结构学习者、后端与网络开发者理解压缩算法中“最优前缀码”的基础机制。

数据结构
1 分钟

关于“数据结构”类别

“数据结构”类别的核心作用是为站内相关内容提供清晰的归档边界,而不是展开某一种具体结构或算法实现。该说明需要帮助作者判断:一篇文章是否真正以数据组织方式、结构特性、使用场景或相关规则为中心,而不是仅在其他主题中顺带提及。它还强调分类应与既有类别保持区分,避免因为概念相邻、应用场景重叠或文章内容混杂而造成归类混乱。适合纳入该类别的内容,应围绕该分类存在的理由、承载的话题范围、与其他分类的差异以及后续维护规则展开。对于站点维护者而言,这类说明的价值在于统一写作和归档口径,减少重复分类、过细拆分或不必要的新建类别。读者也能据此快速理解该栏目关注的知识范围,并判断某篇技术笔记是否应被放入“数据结构”这一分类下。

Python 开发
10 分钟

Catch-All 路由

Catch-All 路由指用 FastAPI 的 `/{full_path:path}` 这类 `path` 类型路径参数匹配任意层级 URL 的兜底路由,常见于前端 SPA 路由回退、后台管理插件或静态资源兜底处理。文章先区分了默认 `str` 参数只匹配单段路径,而 `path` 会连同斜杠一起接收多段路径,因此 `/api/does-not-exist` 这类未命中精确接口的请求不会直接 404,而可能进入 fallback 处理函数。重点风险在路由注册顺序:FastAPI 按注册先后匹配,若 SQLAdmin 等插件提前注册了 catch-all,后续业务 router 可能被截获,导致本应进入 `/api/v1/**` 的请求落到兜底逻辑。文章还把这种顺序问题和浏览器 CORS 报错关联起来,说明 OPTIONS 预检请求在目标接口未注册、被覆盖或 catch-all 不支持 OPTIONS 时,可能得到 404/405 且缺少跨域响应头,最终表现为跨域失败。处理方式包括先注册所有业务接口,再注册 SQLAdmin 或其他兜底路由,并让 catch-all 使用 `api_route(..., methods=["GET", "POST", "OPTIONS"])` 显式接住 OPTIONS。若不希望业务逻辑处理预检,也可以单独添加 `@router.options("/{full_path:path}")` 返回空响应,保证预检请求有合法 handler。适合正在排查 FastAPI 路由“莫名其妙被命中”、后台插件干扰 API、或未命中接口却显示 CORS error 的后端开发者参考。

api-开发
Python 开发
13 分钟

接口等幂处理

接口等幂讨论的是 Web/API 在重复请求、自动重试或并发调用下如何避免重复修改系统状态,核心判断标准是同一操作执行多次后业务结果一致且不产生额外副作用。文章先用 HTTP 方法语义区分 GET、DELETE、PUT、POST 的等幂差异:查询、删除和“更新为固定状态”通常可视为等幂,而创建类 POST 容易产生重复资源,因此在支付、下单、扣积分等场景尤其需要防重。针对 PUT,文中强调理论语义和工程实现并不总是一致,即使请求体相同,也可能因 update_time、变更日志、审计表、MQ 消息、缓存刷新等动作破坏等幂。推荐做法是在更新前比较新旧数据,未变化时跳过写入和时间戳更新,或让日志、消息、缓存等副作用只在内容真正变更时触发。对于短时间重复提交或并发请求,文章给出基于 Redis 的幂等锁与状态缓存思路:用 idempotent key 标识一次操作,已处理则直接返回,未处理才执行业务逻辑。最后比较前端生成结构化 UUID 与后端令牌模式,认为完全随机 UUID 难以识别同一业务操作,而后端生成并下发幂等 key 更便于控制生命周期、缓存执行结果和保障支付、提交订单等关键接口的安全性。

api-开发
Python 开发
13 分钟

单例模式-介绍

这篇笔记以 Python 中的单例模式为对象,说明“一个类在程序运行期间只创建一个全局共享实例”的适用场景,包括配置管理、日志器、数据库连接池、线程池、缓存和 RedisClient 等需要复用状态或资源的对象。内容先对比懒汉式与饿汉式:前者在第一次使用时通过 `__new__` 创建实例,节省资源但在多线程并发判断 `_instance is None` 时可能产生多个对象;后者在模块加载时提前创建,写法简单且天然线程安全,但可能带来不必要的启动资源占用。针对懒汉式的并发问题,文章给出基于 `threading.Lock` 的双重检查锁实现,用外层判断减少加锁开销,用锁内二次判断避免多个线程排队后重复初始化。随后还展示了装饰器封装单例的函数式写法,并提醒其存在首次参数不可重设、默认线程不安全、只在进程内生效等边界。更 Pythonic 的方案是直接利用模块作为单例:`import` 会通过 `sys.modules` 缓存模块对象,使配置、全局状态或缓存变量在多个引用方之间共享。最后补充了 `threading.Lock` 的 acquire、release 和 `with lock` 用法,以及死锁、加锁粒度过大、共享状态污染和测试耦合等风险,适合正在学习 Python 设计模式和多线程资源保护的开发者。

设计模式
数据库与缓存
10 分钟

mysql错误码 2013 解决方案

这篇笔记聚焦脚本长时间持续写入 MySQL 时出现的 2013 错误,即客户端已建立连接并发起 SQL 后,在查询执行过程中与服务端断开。正文先梳理常见诱因,包括慢 SQL 超过 net_read_timeout 或 net_write_timeout、网络或容器中断、MySQL 被重启或 kill、连接池复用失效连接,以及空闲连接被 wait_timeout、interactive_timeout 回收。作者进一步指出自己的场景并非典型慢查询或服务端异常,而是纯 SQL 脚本中每个函数都手动 connect 和 close,导致连接建立、认证、释放过于频繁,可能触发连接数压力、连接未就绪即查询、或查询尚未结束就关闭连接等问题。解决方案采用全局 DBManager 管理单例 SyncMySQLSDK 连接,通过 get_dsn_sdk 按需创建并复用连接,避免在大量函数签名中补传 session,降低改造和测试成本。管理器还提供 check_and_release 按空闲时间释放连接、force_release_all 在脚本退出时强制清理,并用 threading.Lock 保护多线程场景下的连接状态。该方案适合爬取、报表、批处理等长时间运行且频繁读写数据库的脚本,可减少连接抖动和资源消耗,但仍应结合超时配置、慢查询、服务端稳定性与网络状态一起排查。

数据库与缓存
9 分钟

乐观锁和悲观锁

这篇笔记梳理了悲观锁与乐观锁两类并发控制策略,重点放在数据库业务中“先读后改”时如何避免数据被并发覆盖、重复扣减或状态错乱。悲观锁的核心是假设冲突一定会发生,需要在事务内先通过 SELECT ... FOR UPDATE 查询并锁定记录,再由应用层完成余额、库存、订单状态等判断和更新,直到提交或回滚前其他事务不能修改该行。它适合库存扣减、积分余额变更、任务领取、名额抢占、订单状态流转等写冲突高且强一致要求明确的场景,但代价是锁等待、死锁风险和更高的数据库压力。乐观锁则默认冲突较少,不在读取时加锁,而是在提交更新时用 version 或 updated_at 等字段校验数据是否仍是读取时的版本,若 UPDATE 影响行数为 0,就说明发生冲突,需要失败提示或重试。文章特别强调前端或调用方必须把读取到的 version 一并提交,否则后端无法判断并发修改;如果只能由后端再查版本,会增加一次数据库 IO,也削弱乐观锁的优势。版本字段选择上,int 自增最常用且 SQL 简单高效,timestamp 可读但可能受精度影响,uuid 虽不可猜但更新逻辑复杂,一般不适合作为版本号。整体内容适合后端开发者在配置管理、内容编辑、用户资料修改、非关键订单更新等读多写少场景中选择乐观锁,也能帮助判断何时应为金融、电商、调度类关键数据改用悲观锁。

数据库与缓存
11 分钟

mysql中的锁

这篇笔记梳理 MySQL 在并发读写中的锁机制,先从锁的目的切入,说明它用于保障多用户访问时的数据一致性与完整性,并区分表锁、行锁、页锁等物理层锁,以及乐观锁、悲观锁这类通常由业务或事务语句体现的控制方式。内容重点放在 InnoDB 与 MyISAM 的差异:MyISAM 更常见表级锁,而 InnoDB 默认支持行级锁,UPDATE、DELETE 按索引命中目标行时通常只加排他行锁,INSERT 一般也不会阻塞已有数据。文章提醒,行锁并不等于永远只锁一行,如果 WHERE 条件没有使用索引或无法命中索引,InnoDB 可能出现范围锁、间隙锁,甚至表现得接近表级阻塞,这是排查更新慢和并发冲突时的重要线索。它还解释了 SELECT FOR UPDATE、显式事务与自动提交的关系:即使没有手写 BEGIN,单条 UPDATE 也会在 InnoDB 内部以隐式事务执行,语句完成后自动提交并释放锁,所以很多场景下不容易观察到阻塞。后半部分聚焦 ALTER TABLE、增删字段、同步建索引等 DDL 操作,说明这类操作需要表级排他访问,会等待已有事务释放相关锁,并可能阻塞后续 SELECT、UPDATE、INSERT、DELETE。对于后端开发和运维人员,这篇笔记的价值在于建立一套判断加锁范围、锁释放时机和 DDL 卡住原因的基础框架,并给出生产环境避开高峰、考虑 pt-online-schema-change 或 gh-ost 等在线 DDL 工具的实践提醒。

数据库与缓存
10 分钟

数据库预热-介绍

这篇笔记围绕 FastAPI 等 Web 服务的数据库预热展开,核心问题是应用启动后即使配置了 SQLAlchemy、asyncpg 等连接池,首个真实请求仍可能因为懒加载连接、TCP/TLS 握手、认证和首次轻量查询而明显变慢。正文把预热定义为在启动阶段主动建立数据库连接或执行 `SELECT 1` 之类的轻量 SQL,使连接池中提前拥有可复用连接,从而降低对外 API、性能敏感服务的首请求延迟。连接机制部分梳理了 PostgreSQL 从 TCP 端口连接、StartupMessage、SSLRequest、TLS 协商到用户名密码认证的流程,并强调 TLS 握手只发生在新连接建立时,连接池维护的持久 TCP/TLS 连接在未超时或关闭前可以反复复用。文章还提到 pgbouncer、asyncpg + SQLAlchemy 内建连接池和 OS 层 keepalive 等方案,用于在不同层面减少重复建连和连接过早断开的问题。实践部分给出在 FastAPI `lifespan` 钩子中创建 `AsyncSessionLocal` 并执行 `SELECT 1` 的写法,启动时记录预热结果,退出时通过 `engine.dispose()` 释放连接池。需要注意的是,`yield` 之后的清理逻辑只在 Ctrl+C 或 `kill -TERM` 等优雅退出场景可靠执行,热重载、崩溃或强制终止通常不会触发关闭流程;这篇内容适合后端开发者和运维人员理解首请求变慢的来源,并为服务启动、健康检查和连接池配置提供操作依据。