#后端
15 篇
原子写文件:先写临时文件,再 rename
直接覆写会在断电、崩溃、并发读的那一刻留下半个文件。临时文件加 rename 借文件系统的原子性把「半个文件」变成不可能。
工期估算:为什么「大概三天」永远变成三周
估的是「写代码」的时间,交出去的是「写代码 + 联调 + 评审 + 改需求 + 等别人」的时间。把不确定的部分显式列出来,比把天数乘二更有效。
功能开关:它是负债,不是配置
每个开关都会让代码路径翻倍,而开关本身永远不会被删。给每个开关定一个移除日期,比设计一套精巧的开关系统重要得多。
优雅关闭:进程退出方式决定用户看到 502 还是正常响应
收到终止信号立刻 exit 会切断在途请求。正确顺序是停止接收新连接、等在途请求跑完、再释放资源、最后退出。
幂等键:让重试变得安全的那一次写入
网络超时不代表服务端没执行。幂等键把「同一个请求」标识出来,服务端据此返回首次结果而不是再执行一遍。
JSON 规范化:为什么同样的对象算出了两个哈希
键的顺序、数字写法、转义方式都会改变字节流。要让哈希稳定,必须在序列化这一层定死规则,而不是指望 JSON.stringify 每次都给你同一串。
死锁的四个条件:破坏其中任意一个就够了
互斥、持有并等待、不可抢占、循环等待——四个同时成立才会死锁。工程上最容易破坏的是循环等待:全局给锁定一个顺序。
N+1 查询:ORM 最典型的性能陷阱
取 100 条记录再逐条查关联,就是 101 次查询。修法只有三种:预加载、批量查、或者干脆用一次 JOIN。
限流:令牌桶与漏桶的实际差别
令牌桶允许突发,漏桶把流量削成恒定速率。选错的典型症状是正常用户被限、或者限流形同虚设,取决于你的下游能不能吃突发。
SQLite 索引:最左前缀不是限制,是排序的结果
复合索引 (a, b) 只有先按 a 排才能按 b 排,所以「跳过 a 只用 b」自然用不上。理解成排序而非规则,就不会再背前缀口诀。
时区与夏令时:存 UTC,存本地时间一定会出事
夏令时会让某一天有 23 或 25 小时,且切换时刻的本地时间可能不存在或出现两次。只存 UTC 时间戳,展示时再转,是唯一稳的模型。
Unicode 规范化:看起来一样的两个字符串为什么不相等
é 可以是一个码点,也可以是 e 加组合重音。比较、存储、索引之前先统一成 NFC,否则同形异码会让查重和登录全部失效。
Rust 的 Result 里,上下文要补在出错的那一层
`?` 会把错误原样上抛,不告诉你在哪一步失败。底层返回可匹配的结构化错误,应用层用 Context 补上「在做什么」和输入参数,日志里才有能定位的那一行。
什么时候该把一个 crate 拆成 workspace
拆 workspace 的触发条件是编译时间和依赖边界,不是目录好不好看。三个信号成立才拆,否则只是把一次编译换成多个包的管理成本。
结构化日志不是把 println 换成 tracing::info
拼接字符串的日志只能被人眼读,带字段的日志才能被查询和聚合。字段、span、级别、以及别在热点路径上格式化,是四个真正要做的决定。
