PostgreSQL 共享缓冲位于 OS 页缓存上方;OS 命中从内存返回数据,避开下方存储路径。

PostgreSQL 使用两层数据缓存

普通缓冲式关系文件读写中,PostgreSQL 的共享缓冲位于操作系统文件缓存之上。命中共享缓冲时,不需要再次通过下面的文件读取路径;没有命中时,也可能由操作系统直接从内存提供数据。同一文件内容可以在两层各有副本,所以这里并不承诺数据库与内核共用同一份物理内存,更不能把两层缓存理解为完全没有重复占用。mmap 共享内存配置针对共享区域,不能据此推断普通表文件被直接内存映射。 坏掉的样子 执行计划里的共享读取计数,表示块被读入 PostgreSQL 缓冲池,不证明存储设备实际执行了一次物理读取。如果把每次共享读取都算成磁盘未命中,已经温热的操作系统缓存也会被误认为冰冷,从而引出错误的内存配置判断。同样,第二次查询更快,也不能独自说明究竟是哪一层缓存发生了变化,或是否只是其他执行条件改变。 缓存驻留是临时状态。其他查询可能挤走页面,工作集超过容量时,也不能指望所有页永久留在内存里;关系扫描采用的缓冲策略还可能影响复用方式。因此应使用已知大小的工作集、检查实际访问路径,并把耗时结论与真正观察到的缓存层事实分开。没有设备层测量,就不能仅凭一个计划字段宣布磁盘速度发生了变化。 Postgres 有两层缓存:机制图。 快速验证 使用 PostgreSQL 18 可丢弃实例和超级用户,确保附带的缓冲观察与预热扩展可安装、且尚未安装。保留的较早运行使用 PostgreSQL 18.6 单用户后端及一百二十八兆字节共享缓冲。本次来源复核没有重跑;psql 传输与并发活动仍未验证。下面的数据可以轻松放下。通过一个全新的 psql -X -v ON_ERROR_STOP=1 会话按顺序执行。关系驱逐函数用于开发测试,这里只针对隔离实验创建的对象操作: CREATE SCHEMA cache_layer_demo; CREATE EXTENSION pg_buffercache WITH SCHEMA cache_layer_demo; CREATE EXTENSION pg_prewarm WITH SCHEMA cache_layer_demo; CREATE TABLE cache_layer_demo.items (id integer PRIMARY KEY,payload text) WITH (autovacuum_enabled=false); INSERT INTO cache_layer_demo.items SELECT g,repeat(md5(g::text),10) FROM generate_series(1,12000) AS g; VACUUM (ANALYZE) cache_layer_demo.items; CREATE VIEW cache_layer_demo.residency AS SELECT count(*) AS heap_buffers FROM cache_layer_demo.pg_buffercache WHERE reldatabase=(SELECT oid FROM pg_database WHERE datname=current_database()) AND relfilenode=pg_relation_filenode('cache_layer_demo.items') AND relforknumber=0; SET max_parallel_workers_per_gather=0; SELECT pg_relation_size('cache_layer_demo.items')/current_setting('block_size')::int AS heap_blocks; SELECT heap_buffers>0 AS initially_buffered FROM cache_layer_demo.residency; 本次构建的主堆有五百四十六个块,初始缓存比较返回真。观察视图限制数据库、文件标识与主分支,并假定这个新建实验只使用默认表空间。通用监控还必须区分表空间,因为文件编号不是数据库内跨表空间唯一的物理身份。每个缓冲条目的观察是一致的,但整个视图并不是同一瞬间的原子缓存快照。接着,仅驱逐这个关系的共享缓冲: ...

一月 27, 2028 · 1 分钟 · XB Field Notes
时钟与满容量容器表示两种不同未命中原因;正 TTL 不能保护符合候选条件的键免于容量逐出。

Redis 的过期是截止时间,逐出是容量策略

过期与逐出都可能让一次成功查找变成未命中,但它们回答的是不同问题。过期服从键的截止时间,逐出则在内存压力下移除符合条件的键。延长 TTL 不能阻止容量驱动的逐出,扩大内存也不能纠正应用写错的截止时间。必须先确定实际发生了哪种变化,才能判断应该检查写入协议、调整容量,还是重新设计缓存用途。 时间与容量是两个独立维度 带有过期设置的键在 Redis 中称为 volatile。Redis 会在访问和主动清理过程中检查过期键,但这不承诺键一定存活到截止时间。显式删除、替换以及适用的逐出策略,都可能提前改变结果。因此,不管最初设定的期限还有多久,缓存客户端都必须能处理未命中,不能把较长 TTL 当作某条数据不会丢失的保证。 maxmemory 是 Redis 进行内存管理决策时使用的统计上限,不是操作系统 RSS 的硬上限。复制与追加日志缓冲区可能包含不计入逐出判断的内存,进程开销、分配器行为和持久化任务也需要容量余量。大型命令还可能短暂超过配置值。规划时既要考虑缓存预算,也要考虑运行整个进程的环境,不能把两者设成同一个数字就认为足够。 先确定候选范围,再讨论排序 allkeys 类策略不管键有没有 TTL,都会把它纳入候选;volatile 类策略则只考虑带过期设置的键。在各自候选范围内,LRU 偏向最久未访问的键,LFU 使用访问频率信息,random 类策略随机选择。Redis 的 LRU、LFU 是近似算法,不保证全局精确的受害键顺序。volatile-ttl 偏向剩余寿命较短的条目,也不是只清除已经到期的数据。 noeviction 在无法满足内存限制时拒绝增长内存的命令,而不是为了腾空间丢键;已有数据仍可能正常读取。但它不是持久性保证,普通过期、显式删除、进程故障和持久化配置仍然分别起作用。应检查当前实例实际配置,不能根据某种部署形式或集群名称,推断所有环境都使用相同默认策略。 volatile 策略如果找不到带 TTL 的候选键,在这种场景下就表现得像 noeviction。选了 volatile-lru,不会自动把持久键变成可丢弃数据。反过来,把会话或权威记录与可重建缓存混放在 allkeys 策略之下,它们也会成为候选。键名前缀只是命名约定,不会给逐出算法建立独立的数据保护边界。 坏掉的样子 未命中突然增加,可能来自过期、逐出、主动失效或键构造变化。需要把 expired_keys、evicted_keys 的增量,与同期请求负载和写命令结果一起看。这些是累计计数,应比较有限时间区间,并考虑重启或重置;仅看到逐出数上升,还不能知道哪个应用功能失去了哪些数据。 ...

十二月 26, 2027 · 2 分钟 · XB Field Notes
相等的 8 GiB 范围示意 2 MiB 比 4 KiB 映射更少;分块数量不按比例,也没有测量 TLB 缺失。

巨型页在分配灵活性与翻译覆盖之间取舍

巨型页可以增加每条地址翻译缓存能够覆盖的内存范围,在合适负载下减少翻译开销。但效果取决于访问模式和硬件,不能仅因为申请了很大一块内存,就断定程序频繁发生 TLB 缺失。描述工作集需要的页数减少,也不表示所有翻译能够同时装入处理器的 TLB;页数算术、硬件容量与真实性能是三个不同问题。 页大小是一项输入 x86 常见基准页为 4 KiB,但 Linux 的页大小依赖架构与配置,应读取运行系统而不是把四千多字节当作普遍常量。支持的巨型页尺寸同样会变化;2 MiB 与 1 GiB 是常见例子,不是所有处理器的完整列表。跨机器比较时,应先确认单位和实际尺寸,再比较统计数字。 对理想化的连续 8 GiB 范围,4 KiB 粒度需要 2,097,152 个页面,2 MiB 粒度只需 4,096 个。这只是映射覆盖率计算,没有测量硬件 TLB 容量、缺失率、页表遍历延迟,或同时驻留的翻译数量。不同页面大小对应的翻译缓存结构还可能具有不同容量,因此不能把两种页数之差直接称为实际节省的 TLB 项。 显式池与透明分配 Hugetlb 提供显式申请的大页,并由内核维护相应池。持久池中的页面不能同时用于普通分配,也不能在内存压力下换出。池可以在启动时准备,也能在运行中调整,还可能允许额外的 surplus 页面。启动较早时通常更容易获得连续内存,但这并不表示大页只能一次性在开机前决定数量,之后永远无法改变。 透明巨型页采用另一套机制。依内核支持和政策,较大的匿名内存分配既可能发生在缺页时,也可能由后续合并产生。THP 不只是后台线程,也不要求管理员先准备 hugetlb 池。现代内核还可以支持多种 THP 尺寸,不能把每次透明分配都等同于一个两兆字节映射,更不能用 hugetlb 总数代替 THP 使用量。 ...

十二月 21, 2027 · 2 分钟 · XB Field Notes
虚拟内存申请先经过三种 overcommit 准入政策之一;后续物理需求仍受 cgroup、NUMA 及其他资源约束。

Linux overcommit 为虚拟内存承诺设定政策

Linux 的 overcommit 政策控制某些虚拟内存承诺是否可以获准,和分配常驻物理页、选择 OOM 牺牲进程、执行容器内存限制并不是同一件事。一次成功申请可能早于实际物理需求,但这不表示所有分配都没有成本,也不保证后续访问在任何资源约束下都能成功。分析时应把承诺、驻留和故障处理分别记录,而不是都称为“剩余内存”。 虚拟承诺不同于实际驻留 malloc 等分配器可能通过 brk 或 mmap 取得内存,也可能直接复用已有内存区,不发起新的系统调用。匿名页通常按需填入,但地址空间元数据、页表以及实际缺页过程仍然消耗资源。具体成本取决于应用申请什么、访问什么,不能把延迟分配简化成近乎免费地运行整个进程。 Committed_AS 是记账数量,不是所有进程常驻集之和,也不是当前内存压力读数。映射类型会影响成本:私有可写映射可能在页面尚未全部驻留时就形成承诺,普通共享或只读文件映射则采用不同规则。因此,虚拟大小很大的进程未必就是物理内存短缺的责任方,应同时查看实际使用和对应记账范围。 三种准入政策 模式零是默认启发式政策,拒绝明显超额的申请,同时允许超过简单常驻内存预算的分配。它的判断不等于为每个获准字节准备专属 RAM。模式一跳过 overcommit 准入检查,有助于某些稀疏工作负载,但地址空间上限、映射数量以及其他分配失败仍然存在,不能描述成任何荒谬请求都会被无条件接受。 模式二执行更严格的承诺记账。名义 CommitLimit 来自交换空间加上配置的内存额度:比例计算会扣除预留给 hugetlb 的内存,非零 overcommit_kbytes 则用绝对额度代替比例。内核保留量与调用上下文还会影响实际准入。因此,CommitLimit 与 Committed_AS 的差值是有用背景,不是保证任意调用者都能一次申请成功的最大值,也不是当前可用物理内存。 需要记账的分配如果无法纳入预算,可能立即返回 ENOMEM,使部分容量问题更早暴露,包括 fork 和后续改变映射权限所需的承诺。fork 记账与具体映射及写时复制义务有关,并不是简单把父进程常驻集乘二。有些应用有意保留稀疏地址空间,因此政策变化可能影响启动,即使它们预计不会触碰所有页面。 严格记账不是 OOM 免疫 模式二降低一类超额承诺风险,却不会覆盖 cgroup 内存限制、NUMA 约束、其他内核分配以及所有后续需求。任务仍可能遇到内存分配失败或 OOM 事件。不能承诺选了模式二,某个业务工作进程就绝不会被杀,更不能把这个模式当成服务可靠性的单一开关。 ...

十二月 18, 2027 · 2 分钟 · XB Field Notes
内存访问可进入内存中的次缺页处理、涉及换入 I/O 的主缺页处理,或非法访问处理;缺页数不等于全部存储请求数。

缺页并不总是意味着存储读取

缺页表示处理器无法依据当前映射或权限完成一次访问。它不等于存储读取,有些缺页还代表非法访问,而不是可以恢复的按需分页。Linux 的次缺页与主缺页计数,区分处理过程中是否需要页面换入输入输出。解释时应结合映射类型与工作负载,不能把任意一个计数直接当作应用全部存储流量,更不能仅凭名字中的“主”“次”判断问题严重程度。 次缺页不代表没有成本 次缺页可能为已有文件缓存页建立映射,也可能为匿名内存分配并清零物理页,或在写时复制时复制一页内容。内核可能需要分配内存、维护页表、复制数据和同步。把它说成只增加一个指针,会遗漏这些真实成本。即使完全没有存储读取,密集次缺页仍然可以消耗可观处理器时间,影响请求延迟,不能因为分类是 minor 就无条件忽略。 应用第一次访问,并不保证每个基本页都产生一次新的缺页。预先建立映射、已存在的映射、预读相关处理和大页都可能改变计数。系统页大小也不是处处都等于四千零九十六字节。示例应读取本机值,不能从循环次数直接承诺精确缺页数量;否则换一台机器或启用不同内存策略,实验就可能因为错误前提而失败。 主缺页描述另一条处理路径 主缺页意味着处理涉及页面换入输入输出,常见于文件映射或已换出的内存。背后可能是存储设备,也可能是远程文件系统,因此这个分类不是物理磁盘延迟基准。一次缺页还可能触发预读,让后续访问直接获益,计数与页数或磁盘请求数并不一一对应。反过来,普通文件读取也可能产生输入输出,却不在应用里形成对应的主缺页。 文件页已经在主机缓存中时,新进程即使从未访问过它,也可能只产生次缺页。重启进程不等于清空整台机器的页缓存。匿名内存首次写入通常通过分配零填充页面完成,不必从存储读回旧载荷,但压力和同时发生的活动仍会影响测量。“匿名”不能保证整个进程的主缺页计数在任何环境都保持不变;应以本次实际计数为准。 坏掉的样子 冷启动延迟可能与主缺页同时出现,但在查清缺少什么之前,不应一律建议预热。提前触碰不需要的页面会消耗内存,甚至挤掉有用缓存。错开启动也许能缓解已确认的共享瓶颈,另一个负载却可能需要更小映射、不同访问模式或更多容量余量。单靠缺页数无法在这些方案之间作出可靠选择,需要把变化与业务延迟联系起来。 持续交换会让被逐出的页面反复进入工作集,并拉长等待。增加工作者可能扩大内存需求,使情况更差。应关联缺页、换入换出、回收、存储等待和请求延迟,而不是一看到主缺页上升就宣布存储设备损坏,或认定所有缓存缺失都本来可以避免。对顺序扫描和随机访问,也应分别理解预读与局部性的作用。 进程级计数还包含测量代码之外的工作。解释器、启动过程和其他线程都可能贡献变化。常驻情况查询只是一个时刻的快照,紧接着访问时状态可能已经改变。不要为了得到更明显的图表就清空全局缓存,或不断扩大分配直到影响其他任务。诊断实验应该有明确资源预算,并保留失败时的原始观察,而不是只保留符合预期的数字。 次缺页、主缺页与非法访问走不同路径;匿名内存示例并未覆盖所有路径。 快速验证 保存为 fault_probe.py,在有足够空闲内存的 Linux 上使用 Python 3.9 或更新版本执行 python3 fault_probe.py。程序创建十六 MiB 的私有匿名映射,按本机页大小每页写一个字节,再重复一轮。结束时自动关闭映射,不操作文件、不修改全局缓存设置,也不改变任何内存限制。 import mmap import resource import sys import time if not sys.platform.startswith("linux"): raise SystemExit("This exercise requires Linux getrusage semantics") PAGE = mmap.PAGESIZE SIZE = 16 * 1024 * 1024 def counters(): value = resource.getrusage(resource.RUSAGE_SELF) return value.ru_minflt, value.ru_majflt def touch(region): for offset in range(0, len(region), PAGE): region[offset] = 1 with mmap.mmap(-1, SIZE, flags=mmap.MAP_PRIVATE | mmap.MAP_ANONYMOUS, prot=mmap.PROT_READ | mmap.PROT_WRITE) as region: before = counters() start = time.perf_counter() touch(region) first_seconds = time.perf_counter() - start middle = counters() start = time.perf_counter() touch(region) second_seconds = time.perf_counter() - start after = counters() first = tuple(b - a for a, b in zip(before, middle)) second = tuple(b - a for a, b in zip(middle, after)) print(f"page_size={PAGE} mapping_bytes={SIZE}") print(f"first: minor={first[0]} major={first[1]} seconds={first_seconds:.6f}") print(f"second: minor={second[0]} major={second[1]} seconds={second_seconds:.6f}") assert first[0] > 0, "expected demand-fault activity" assert second[0] < first[0], "repeat pass did not show reduced minor faults" assert first[1] == second[1] == 0, "observed major faults: this run is not a no-major example" print("PASS: this run had fewer second-pass minor faults and no measured major faults") 本实验预期第一轮出现按需缺页,第二轮次缺页减少,并且这次运行没有测得主缺页。本轮两种语言的完整示例均通过这些断言。计数与耗时只是观察,不是固定目标。脚本没有断言第二轮一定更快,因为计时、调度和机器负载会变化。使用本机页大小,也避免把某种平台常见值误当成通用常量。 ...

十一月 13, 2027 · 1 分钟 · XB Field Notes
共同比例条形图对比历史 libc 近似观察:加载后分配量约 16.7 MiB,删除所有键后约 1.04 MiB,清空后的 RSS 约 20.8 MiB,最终比率约 20。这些观察不是目标值,也不证明泄漏。

Redis 内存比率需要结合分配背景判断

即使应用键已经删除,Redis 进程仍可能保留较大的常驻内存。这个现象值得解释,但它本身既不能证明内存泄漏,也不能直接证明分配器碎片。真正要回答的是哪些字节仍被分配、哪些页面仍驻留,以及这种占用是否威胁实际容量预算。只看一个名字里带“碎片”的比率,很容易选错处理对象。 历史 libc 近似观察值与分配量断言关系需分开。更高比率本身不证明泄漏、碎片或回收有效。 把不同测量对象分开 INFO memory 中的 used_memory 表示 Redis 统计的分配量,used_memory_rss 则是操作系统观察到的进程常驻内存。它们都不等于应用负载长度的简单相加,键、对象、元数据、客户端及其他开销也要占空间。MEMORY USAGE 可以估计单个键及其值的分配量,包括相关开销,但不测量这个键独立贡献了多少进程 RSS。 mem_fragmentation_ratio 大体比较常驻内存和 Redis 统计的分配量,两者差额还可能包括代码、共享库、栈以及分配器保留的页面。删除数据后分母下降,即使 RSS 几乎不变,比率也可能迅速升高。因此必须同时检查绝对字节数;一个很小的空实例出现较高比率,不等于它正在占用危险数量的内存,更不是必须立即重启的充分理由。 ...

六月 3, 2027 · 3 分钟 · XB Field Notes
交换占用用保存的冷页表示,间隔流量用双向页面箭头表示,内存压力用任务停顿标记表示;三种观察回答不同问题。

Swap 用回收成本换取驻留空间

Swap 为 Linux 中适合换出的页面提供后备存储,让暂时不需要驻留的数据离开物理内存,把 RAM 留给活跃工作。但它不会让超出容量的工作集继续拥有内存访问速度。真正要问的是:回收是否腾出了空间,同时没有反复阻塞重要任务。交换占用、交换流量与应用进展,分别回答这个问题的一部分,不能拿其中一个数字概括全部情况。 分清哪些页面可以移动 匿名内存经常使用交换,但并非唯一情况。配置允许时,tmpfs 和共享内存页面也能换出。普通文件支撑的干净页面通常可以丢弃,以后从原文件重读;脏文件页则需要适当写回。并非所有分配都可以回收,被锁定而禁止换页的内存还有另外约束。因此,“所有 malloc 分配的东西”不足以描述内核实际上能够移走哪些内容。 vm.swappiness 在零到二百的文档范围内,表达交换与文件系统换页之间的相对成本;它不是达到某个 RAM 使用百分比后才开启交换的阈值。设为零也不等于在所有情形下禁用交换。如果压缩交换或更快的交换设备比文件系统换页便宜,大于一百的值可能合理,但适当取值取决于工作负载与存储。解释图表之前,应先读取实际配置,而不是默认每台机器都采用相同策略。 zram 提供压缩内存块设备,可以配置为交换;zswap 则在交换页与后备交换设备之间提供压缩缓存。压缩本身消耗 RAM 和 CPU,效果也依赖数据内容。两者都没有通用的延迟、压缩比或安全超配比例。zswap 不是可以直接挂到 /tmp 的文件系统;tmpfs 挂载具有自己的存储语义和容量约束,不能把名称相近的内存机制当成同一项功能。 坏掉的样子 反复回收马上又要使用的页面,会造成严重停顿。原因可能涉及存储延迟与吞吐,也可能涉及回收工作、压缩开销、控制组限制和竞争 I/O。颠簸并不保证最终触发 OOM 杀进程,固定的交换速率也不是普遍适用的诊断线。应把内存压力与有效吞吐、响应时间放在同一个观察窗口比较,再决定主要瓶颈在哪里。 交换已用量很大,可能只是早先事件留下的冷页面,不证明当前正在发生故障。相反,交换流量很小也不证明内存足够:交换可能未启用,工作负载可能碰到控制组上限,或者回收主要发生在文件页。主缺页计数上升也包括文件支撑的缺页,不能直接重新命名成换入次数。把相关指标当成同一个计数器,会让排查走向相反结论。 计划中的交换文件可能因为布局或文件系统限制而无法启用。带空洞的文件不适用,但复制是否产生空洞,取决于具体工具、选项和源文件,不能一概而论。应检查已经激活的交换清单,不要假定配置文件里写过一行就已经生效。也不要仅为重现一篇诊断文章,就在活动机器上启停交换或调整容量。 仅凭交换占用不能判断当前压力;间隔流量与 PSI 分别量化活动与停顿的不同方面。 快速验证 在安装了 util-linux 和 procps 的 Linux 环境运行这些只读命令。检查只观察当前环境的三个采样间隔,不制造内存压力,也不更改内核设置。机器没有启用交换也是有效观察结果,并不是实验失败;它只是告诉你不能依靠这些交换设备缓解未来的页面回收需求: ...

十一月 25, 2026 · 1 分钟 · XB Field Notes
三块原始页符号变为一块压缩载荷与独立开销符号,全部位于 RAM 边界内;这是示意比例,不是实测容量收益。

zram 用压缩的方式在内存里挤出交换空间

内存吃紧时,符合条件的冷页可能被回收到交换空间。作为交换设备的 zram 把页面压缩后放在 RAM 中,以处理器工作和内存开销换取潜在余量。收益依赖工作负载,不是一份固定增加的内存,也不保证每台机器都只有很小的处理器成本。 内存里的压缩块设备 zram 是驻留在 RAM 中的压缩块设备。配置为交换时,它可以保留符合条件的页面,避免普通磁盘交换输入输出。但可选写回功能能够使用另行配置的后备设备,因此“永不接触磁盘”不是通用性质。压缩可能降低延迟,具体仍取决于算法、处理器负载和数据。逻辑设备容量与真实 RAM 占用必须分开观察,二者也都不能直接替代系统剩余内存。 压缩就是这笔交易 「压缩」就是这笔交易赚头所在,也是「一台小机型显得能装下比物理内存更多内容」的根源。一页值不值得压要看内容:全零页、文本和有结构的数据都缩得不错,而图片、压缩包这类已经被压缩过的对象几乎缩不动。站在工程视角看,zram 等于把暂时闲置的 CPU 换算成「看似更多」的内存;当工作负载多半是可压缩的、又特别想避开真实磁盘换页的延迟时,这笔交易才划算。 适合服务器与低端设备 它最合身的地方是那些内存不多、换页压力又不过度的机器:小 VPS、容器宿主机,以及手机、路由器这类如今要把袖珍机器安排得明明白白的角色。反过来,内存本来就充裕、或者本来就要换到冷冷盘上的服务器,同一笔钱买一张真实磁盘往往能装下更多,因此未必轮到 zram。实用的判断方法是盯住压缩率和 swap 使用量:当驻留数据始终很小、又够可压缩、且 CPU 相对空闲,才是让 zram 出场的时刻。 坏掉的样子 压缩效果差可能消除预期收益。照片、视频、加密缓冲和已压缩归档可能几乎缩不动。应比较 orig_data_size 与 compr_data_size,同时查看包含分配器碎片和元数据的 mem_used_total。池占用、可用 RAM、负载需求与压力仍然重要。接近一的压缩比不能决定是否发生 OOM,也不能预测其时间;冷页驻留与回收行为同样影响结果。不能把一组字节比率当成故障倒计时。 昂贵的压缩算法可能增加处理器占用与交换延迟。可用算法及参数取决于内核构建,应在代表性压力下比较 lzo-rle、lz4 或 zstd 等候选,而不是断言某一个总适合交互机器。系统态占用高或 kswapd 忙碌,也不能单独证明压缩器就是原因。修改策略前,应把应用延迟、有效进展、压缩统计和回收活动放在同一观察窗口,保留原配置并验证实际变化。 ...

十一月 24, 2026 · 3 分钟 · XB Field Notes