原有实验把参数已知的 SET counter 4、INCR 和 GET 排队,依次得到 True、5 和字节串 5。普通流水线不添加 MULTI 或 EXEC,命令报错也不会取消已提交的后续命令。

Redis 流水线减少逐条命令等待

Redis 流水线允许客户端连续发送多条命令,不必等前一条回应以后再发下一条。它减少了请求与响应之间的等待,也能分摊套接字输入输出开销。但服务端仍需要执行每条命令并生成回复;批处理不代表这些命令并行运行,也不自动形成事务边界。 原有实验把参数已知的 SET counter 4、INCR 和 GET 排队,依次得到 True、5 和字节串 5。普通流水线不添加 MULTI 或 EXEC,命令报错也不会取消已提交的后续命令。 仅为原有实验示意,本轮未重跑。 少等几次,而不是少执行几条 同步循环不断重复发送一条、等待一条。流水线则先发送一个序列,再按对应顺序收取回复。把一批近似理解为一次往返,可以帮助估算延迟,但不能据此承诺只有一个 TCP 数据包、一次系统调用,或所有回应在同一时刻到达。实际传输仍受缓冲区、数据量和协议处理影响。 收益也不只出现在远程链路。把套接字读写集中起来,可以减少系统调用和调度开销,所以即使本地连接,也可能在不分片的情况下提高实际吞吐。反过来,流水线消除不了昂贵命令本身的工作和带宽上限。应先定位真实瓶颈,而不是看到单节点就断言下一步必须加分片。 普通流水线没有“不被插队”的保证。其它客户端可能在批次的命令之间执行操作,但具体某一次运行也可能碰巧全部连续完成。观察到一次连续执行,并不能把它上升为事务隔离承诺。正确性不能依赖一次测试里没有出现的调度交错。 ...

一月 20, 2027 · 3 分钟 · XB Field Notes
同一 TCP 连接中包含三条 HTTP/2 流:A 被取消,B 完整读取正文,后续 C 复用原连接。共享 TCP 约束仍然限制进度。

HTTP/2 多路复用消除排队

HTTP/2 通过多路复用解除 HTTP 响应的顺序约束,但不同流仍然共享 TCP 连接及其故障边界。取消就是一个值得单独验证的场景:终止某个请求,不一定需要破坏相邻请求或可复用连接。这是一项有明确作用范围的能力,不能扩写成所有流在任何条件下都互不影响。 取消一条 HTTP/2 请求后,应分别核验相邻正文与原连接复用。共享 TCP 约束依然存在;取消也不能撤销已经发生的业务操作。此图不增加运行证据。 流不是底层套接字 HTTP/2 在连接内部标识单条流。RFC 9113 定义的 RST_STREAM 用于终止某条流,既可以表达取消,也可以报告流错误;它并不是重置整个套接字的 TCP reset。只要连接本身仍然可用,实现就可以停止一次交换,同时让其他流继续推进,而不必为每次取消重新建立连接。 ...

一月 16, 2027 · 3 分钟 · XB Field Notes
EUR 请求分成两条路径:转发给源站的输入仍是 EUR,但遗漏币种的缓存键复用了价格为 10 的 USD 正文。

内容分发网络把缓存拉近

CDN 可以缩短读者与可复用响应之间的路径,也能减少源站请求量。但这些收益取决于实际路由、缓存里已有的内容和生效策略。域名指向 CDN,不等于某个对象已经缓存,也不证明服务它的是地理上最近的机器,更不能保证每次请求都会绕开源站。应检查真正发生的请求链路,而不是把网络示意图当作验证结果。 把币种转发给源站,并不自动让缓存区分币种。文章固定时钟模型保留币种与语言,排除无关追踪;错误投影却让 EUR 请求复用价格为 10 的 USD 正文。本图仅解释文中策略,不增加 CDN、路由、延迟、清理或账户隔离验证。 沿着真实请求检查 访客会到达服务商路由选出的边缘。如果那里存在可以复用的响应,就能直接返回;否则请求可能经过另一层缓存,也可能抵达源站。因此某个位置的第一次访问,常常与紧接着的第二次不同。发布文件也不代表它会立刻完整复制到所有边缘,不能把“源站可读取”和“所有节点已预热”混为一谈。 即使不能缓存,流量仍可能受益于连接处理或网络路由优化,但它不会取得缓存命中带来的同等回源减免。测量时要分开看建连、首字节等待和正文传输。一次距离源站很近的快速请求,不能推断远方读者、冷节点或源站故障时的表现;需要按真实访问位置和缓存状态分别取证。 明确哪些请求可以共享字节 缓存键决定哪些请求允许共享同一份响应。路径、选定的查询参数、请求头和 Cookie 都可能影响这个身份。凡是会改变所选表示的输入,都必须在复用决策中得到区分;只有确实不改变响应的追踪参数,才适合排除。维度太多会碎片化缓存,遗漏必要维度则可能返回错误内容,甚至跨越敏感数据边界。 “转发给源站”不等于“纳入缓存键”。CloudFront 区分缓存策略和源站请求策略,后者可以转发不进入缓存键的值。如果币种会传给源站、源站也据此生成金额,但缓存键忽略币种,那么一份美元响应就可能用于欧元请求。此时命中率上升并不是优化成果,而是在掩盖内容正确性缺陷。 ...

一月 4, 2027 · 2 分钟 · XB Field Notes
Linux 路径套接字条目仍保留,与打叉表示已关闭的监听器之间有一个醒目的不等号。原文流套接字样本中,连接得到 ECONNREFUSED,绑定同名得到 EADDRINUSE。

Unix 套接字提供本地通信,名称与访问规则仍须明确

Unix 域套接字提供本地进程间通信,无须 IP 地址、TCP 端口或外部网络路由。它仍使用内核套接字设施、缓冲区、调度和应用协议。路径套接字具有文件系统名称,但 Unix 套接字也可以没有名称,Linux 还支持抽象名称。这些形式具有不同访问规则,因此“Unix 套接字就是一个受权限保护的文件”并不是完整解释。 Linux 路径访问同时要求目录可搜索与套接字可写。原文的 UID 65534 样本对比父目录/套接字权限 0755/0600、0755/0666 和 0700/0666。关闭监听器会留下遗留路径;只有确认拥有该遗留路径后才清理,并验证替代监听器返回 restarted。宽松的 0666 仅用于实验隔离检查,不是生产建议。 仅示意选定的原文机制;本轮没有执行示例。 坏掉的样子 应用找得到套接字路径,却可能因为监听已经退出而无法连接。关闭路径套接字,不会自动删除对应文件系统条目。重启时直接绑定相同路径,便可能得到 EADDRINUSE,即使那里已经没有服务接受连接。文件存在是有用诊断信息,但不能作为健康检查;要知道服务是否可用,仍需要真实通信与预期应用响应。 ...

十一月 28, 2026 · 1 分钟 · XB Field Notes
数据由语言缓冲进入页缓存和存储;独立读取者从页缓存分支读取,早于显式 fsync 边界。可见性不能证明持久完成。

写回把缓冲可见性与存储完成分开

缓冲文件 I/O 可以在不同时间经过语言缓冲区、内核页缓存和存储栈。被修改的缓存页需要写回,使数据进入后端文件系统与设备。分开这些阶段可以合并工作、提高吞吐,但也意味着另一读取者看到新字节,本身不能证明已经完成可经历崩溃的存储边界。理解性能和持久性时,需要先确定观察的是哪一层完成。 坏掉的样子 缓冲写入不保证瞬间返回。分配、内存压力、脏页限制或文件系统活动,都可能让写者等待。如果生产速度持续超过存储处理能力,脏数据不能无限增长,压力最终会反映到工作负载。把每次 write 都描述为单纯内存复制、所有 I/O 都在之后发生,会错误解释持续负载下的延迟尖峰。 语言层 flush 与文件系统同步含义不同。flush 把应用缓冲数据提交到下一层,fsync 则请求文件同步边界。普通关闭文件,或者从另一个描述符读到相同字节,不能替代这一显式契约。因此只测成功写入的基准,可能测到的是缓存接受速度,而不是稳定、持久的存储吞吐,需要与实际工作要求对应。 写回设置也不是逐文件持久性截止时间。后台阈值促使刷新线程工作,更高的脏页阈值影响生成写入的进程,周期参数则控制唤醒或较老脏数据何时符合处理条件。设备进展、文件系统行为和竞争负载仍会影响结果。不能仅设一个时间间隔,就证明每次已确认更新在这么多秒后必定安全。 原文读取者在显式 fsync 前看到字节;可见性与系统 Dirty 计数不能建立逐文件持久性。 快速验证 把脚本保存为 writeback.py,在装有 Python 和 strace 的 Linux 环境中运行 python3 writeback.py,并让临时目录位于 ext4。实测使用 Linux 6.12.76、Python 3.11.2 和 strace 6.1。样本写入约四 MiB、读取记账信息,不改变全局脏内存参数或缓存控制: ...

十一月 26, 2026 · 2 分钟 · XB Field Notes
旧红色字节保留旧摘要文件名,变化后的蓝色字节使用新摘要文件名。

不可变资源与内容指纹

资源指纹把文件字节的摘要放进地址。样式表发生变化,构建就为新内容生成另一个地址,页面因此能够选择新版本,而不用等待所有旧缓存同时过期。这项方法依赖发布流程遵守承诺:某个地址一旦对外提供,就不能随意替换成与名称摘要不符的内容。文件名看起来像摘要,不代表服务器已经落实这种约束。 字节变化应使用新的摘要地址;先让资源可用,再发布引用它们的 HTML,并保留仍被引用的旧文件。覆盖旧地址会让新旧客户端看到不同内容。文件名摘要不是浏览器完整性检查;本图未重新执行实验。 名称配合新鲜度契约 构建程序可以对生成文件计算完整的 SHA-256,再把页面中的逻辑名称替换成相应地址。同样的字节保留相同名称,变化的字节通常得到不同名称;摘要截断越短,碰撞风险越需要认真评估。浏览器不会因为名称含有一串十六进制字符,就自动计算文件摘要验证完整性。实际缓存键还受到请求上下文和 Vary 等因素影响,命名方法不能取代 HTTP 缓存规则。 公开指纹资源常用的响应头是 Cache-Control: public, max-age=31536000, immutable。前者给出一年新鲜期,后者承诺该表示在新鲜期内不会改变,因此 RFC 8246 建议客户端避免不必要的条件请求。这不是永久保存许可:缓存可能被淘汰,用户可以强制刷新,客户端也可能不支持扩展。规范还建议在 HTTPS 等经过认证的上下文之外忽略 immutable,不能把它当作内容可信性的替代品。 负责引用这些资源的 HTML 需要独立缓存策略,例如较短新鲜期或合适的重新验证,让用户能够发现新版本。发布顺序应先让新资源可用,再暴露引用它们的页面或清单。旧页面、旧标签页和回退版本仍可能使用旧地址,资源保留期限必须覆盖这些真实使用方式。仅有指纹,不能让整个多文件发布自动成为原子事务。 public 只适合允许共享的表示。某份个性化下载即使带有内容摘要,也不会因此变成可供共享缓存保存的公开资源。响应策略仍然必须服从身份鉴别与保密要求;摘要只是命名依据,不能充当授权或隔离用户数据的措施。 坏掉的样子 覆盖旧指纹路径会制造两套互相矛盾的事实:已有用户继续从缓存读取红色样式,新客户端却在同一个地址读到蓝色字节。清理一个 CDN 节点,也不能把所有浏览器里的私有副本同步删除。发布检查应核对真实文件字节和记录的摘要,在开放流量之前拒绝这种变更,而不是依赖上线后提醒用户刷新。 立即删除上一版资源会产生另一类故障。缓存中的页面还可能引用昨日脚本,回退后的 HTML 也可能依赖已经移除的文件。保留源码提交不等于保留可直接恢复的构建产物。即使所有摘要都正确,先发布页面、后上传文件仍会造成暂时的缺失资源;多节点传播期间也需要检查依赖是否已经就绪。 ...

十一月 25, 2026 · 2 分钟 · XB Field Notes
两个合成响应都暴露 Server-Timing 响应头;原文历史观察中,仅获计时授权的响应显示具名性能条目。

Server-Timing 向浏览器暴露具名测量结果

Server-Timing 响应头允许服务端给响应附加具名测量结果,浏览器开发者工具和性能接口随后可以把这些结果与对应资源请求关联起来。当网络瀑布图只能显示等待很久,却无法说明后端是否在查询数据库、渲染页面或执行其他操作时,这类信息很有帮助。但它提供的是服务端选择上报的观测结果,并不是浏览器独立测得这些内部操作的耗时,因此仍然需要理解测量范围和产生位置。 正确的时长语法是 db;dur=42, render;dur=81,建议使用毫秒作为单位。浏览器解析数值,但无法验证发送方实际采用的单位。直接写 db=42,并不是这项协议表达时长的方式。每个指标有名称,也可以带时长和描述参数。生产环境应采用含义稳定的名称,从实际插桩中得到数值。描述可以解释测量对象,但不应包含响应本来不该披露的原始查询、身份编号或内部细节。浏览器能够展示字段,不代表所有调试内容都适合跟随响应对外发送。 让数值对应明确的时间区间 服务端的多个测量值不一定能加成浏览器看到的请求总耗时。它们可能覆盖嵌套操作、相互重叠的工作,也可能只覆盖处理路径的一部分。只有数据库时间与渲染时间的定义允许相加时,加总才有意义。网络传输、测量区间之外的排队、缓存和客户端处理,都可能解释两侧数字的差异,并不一定说明某个时钟有错误。应先弄清每个数值从哪里开始、在哪里结束。 这个响应头也不会自动生成分布式链路。它在单个响应上命名测量结果,并没有自动提供父子关系、跨服务传播或采集器存储。可以用它展示少量对请求有帮助的测量,再通过日志或链路关联获取更深的解释。当响应经过缓存和代理时,还应检查最终交付的响应头,确定这些数字由哪个组件产生。读取一个字段与证明当前源站执行了相应工作,是不同层次的证据。 坏掉的样子 跨源资源存在两个容易混淆的权限边界。CORS 允许脚本读取响应,而 Access-Control-Expose-Headers 可以让 response.headers 暴露 Server-Timing 字段。Timing-Allow-Origin 则控制相应的详细资源计时信息是否可见。安全上下文与浏览器隐私规则还可能进一步限制计时暴露。因此,脚本成功读取响应头,并不能证明 PerformanceResourceTiming.serverTiming 数组一定包含同样的数据。排查时需要分别验证,不能只用其中一个接口有值就推断另一项能力已经成立。 另一个错误,是把解析演示当成真实性能测量。服务端发送固定时长,只能证明浏览器怎样解析这个数字,不能说明处理器实际运行了那么久。同样,计时数组为空,也不能证明服务端没有做任何工作。应先核对来源授权与真实线上响应头,再检查测量代码放置的位置。公开名称与说明还要符合接收对象的范围,避免为了排障而把不该公开的后端结构随响应一起暴露出去。 相同的合成 Server-Timing 可作为响应头读取,却不出现在浏览器性能条目中。原文历史 Chrome 152 对照检查两种暴露权限,不是后端性能测量。 快速验证 将代码保存为 server-timing.py,执行 python3 server-timing.py --serve;此前记录使用 Python 3.14.4;本轮没有重跑套接字或浏览器测试。脚本在两个不同的临时端口上启动本机回环 HTTP 服务,因此它们是不同来源,然后输出待打开的页面地址。页面会请求两份资源,把响应头内容与浏览器解析出的性能条目同时显示出来。结束后按控制键加 C 关闭两个服务;不带服务参数执行时,只检查响应头并退出,不会自动运行浏览器。 ...

十一月 7, 2026 · 2 分钟 · XB Field Notes
编号 100 和 200 的状态为 ready,进入 ready_jobs;状态为 done 的 101 不被收录。查询条件必须蕴含状态为 ready。

预备计划必须证明部分索引谓词

一个任务队列可能只有一百条待处理记录,却保留了成千上万条已完成记录。如果主要请求都是领取待处理任务,只为这个子集建立索引就可能更合适。PostgreSQL 把它称为部分索引,索引谓词决定哪些表行拥有条目。收益仍取决于真实查询和状态变化,不能把“所有索引越小越好”当成脱离业务的优化目标。 查询条件必须蕴含索引谓词,索引才具备使用资格;代价与可见性仍会影响最终访问路径。 坏掉的样子 条件之间的蕴含方向不能写反:查询条件必须蕴含索引谓词,也就是查询可能需要的每一行都在索引覆盖范围内。只收录待处理任务的索引,不能完整回答没有状态限制的全表查询。两个条件看上去不矛盾,也不代表规划器能证明它们之间的关系。PostgreSQL 不会识别所有数学上等价的写法,所以让谓词接近应用实际过滤表达式,也有助于人工审阅和计划识别。 哪些参数已经知道,同样由计划阶段决定。预备语句的定制计划,可以使用这次传入的状态值;通用计划却必须对所有可能的参数都成立,不能假定每次调用永远查询待处理状态。因此,手工测试常量查询时看到索引被使用,并不能直接证明应用长期执行的预备语句也会选择同一条访问路径。 快速验证 在 PostgreSQL 18 的可丢弃数据库中,使用一个全新的 psql -X -v ON_ERROR_STOP=1 会话按顺序执行。实验没有建立覆盖全表的其他索引,便于观察资格差异。先创建一万条任务,其中编号为一百整数倍的记录处于待处理状态: CREATE SCHEMA partial_demo; CREATE TABLE partial_demo.jobs (id integer,state text); INSERT INTO partial_demo.jobs SELECT g,CASE WHEN g%100=0 THEN 'ready' ELSE 'done' END FROM generate_series(1,10000) AS g; CREATE INDEX ready_jobs ON partial_demo.jobs(id) WHERE state='ready'; VACUUM (ANALYZE) partial_demo.jobs; SELECT state,count(*) FROM partial_demo.jobs GROUP BY state ORDER BY state; 分组结果应是已完成九千九百条、待处理一百条。这里索引键是任务编号,谓词检查的是状态,两者不要求是同一个字段。先检查带常量条件的计划: ...

十月 17, 2026 · 1 分钟 · XB Field Notes