五份结果不能少:批次与位图访问实作
这份任务要求把同一查询写成三种执行:逐行、位置批次、索引位图加堆重检。终点是完整五份输出和一份可检查的成本账本。不能只比较SUM,因为相同总和可能掩盖重复或遗漏;也不能只比较set,因为两份10都是答案。
入口、核心与选读
核心两站是批次及借用合同 → 候选组合与有损重检。开始前应能解释next返回后的暂停状态,并知道列之间为什么要保留共同位置。不知道NULL如何筛选时,按需补三值谓词;本路线消费其TRUE规则,不重新要求完成所有外连接与去相关任务。
算索引页时可回看B+树回表账本;关心掩码字操作时选读字级并行;要把成本放进有限帧调度,再接物理计划账本。这条新路线不替换DB-4连接路线或NULL-6改写路线。
入口自测:一批容量4,实际只有2槽,筛完剩0个位置,这就是EOF吗?两个不同TID的v都为10,索引OR合并后能只留一个10吗?两题都是否;前者还要继续拉取,后者要保留两份出现。
固定数据与任务
堆页从0编号,每页三份出现。位置i只供追踪,不是用户声明的业务主键;同一快照内每个位置稳定,所有十二行都可见。
| i | 页 | x | y | v |
|---|---|---|---|---|
| 0 | 0 | 1 | 1 | 10 |
| 1 | 0 | 1 | 1 | 10 |
| 2 | 0 | 0 | 1 | 20 |
| 3 | 1 | 1 | 0 | 30 |
| 4 | 1 | 0 | 1 | 40 |
| 5 | 1 | 1 | 1 | 50 |
| 6 | 2 | 0 | NULL | 60 |
| 7 | 2 | 1 | 1 | 70 |
| 8 | 2 | 1 | 0 | 80 |
| 9 | 3 | 0 | 0 | 90 |
| 10 | 3 | 1 | 1 | 100 |
| 11 | 3 | 0 | 1 | 110 |
查询为SELECT v FROM R WHERE x=1 AND y=1;另计算相同筛选上的SUM(v)。所有v为精确整数,表达式纯、确定、在合法输入值上总定义;NULL比较按SQL规则。空结果的SQL SUM完成值为NULL,内部累加状态可从0开始,但不能直接把它作为空查询的完成值。成功输出按bag比较,无ORDER BY承诺。
完成六项任务:
- 写出逐行的出现编号、全部输出及SUM,再用K=1、2、4、5、12分别分批得到同一答案
- 列出K=4每批的base、n、局部S与全局I;K=5额外检查短尾。让K=1经过一次空批后继续找到后续结果
- 模拟一次借用引用被下一次拉取覆盖;给出复制方案。检查失败、提前close、空输入和inactive lane除零
- 分别构建x=1与y=1的TID集合,执行AND和OR;比较TID合并、扫描拼接以及按值去重
- 预算从12降为10、4、3单位,解释哪个页退化、候选变化、完整重检和失败。另把降精度放在AND之前
- 分开列出分派、谓词、逻辑字节、索引页和堆页成本;把候选缩到位置0、1,再改变输出列的值顺序,检查原结论哪些保持
完整答案与执行证明
先锚定五份结果
真实出现为[0,1,5,7,10],输出为[10,10,50,70,100],精确SUM=240。出现0与1对任意bag比较都必须保留两份。检查器同时比较(i,v)序列与v的Counter,避免把错误的配对只靠总和掩盖。
K=4时三批依次为:base0、n4、S=[0,1];base4、n4、S=[1,3];base8、n4、S=[2]。映回I后分别[0,1]、[5,7]、[10],三次局部和为20、120、100。第二批把S直接当全局编号会错读位置1、3,这是选择位置与出现身份混淆。
K=5时前两批有效长度5,末批base10、n2,S=[0]。尾槽2、3、4保留旧值也必须拒读。K=1的十二批中七批为空选择;空选择只有“本批无输出”的含义。N=0时第一次拉取直接END,没有一条伪造NULL行。
逐批不变量是:已交付并复制的结果,恰为已消费出现前缀中满足完整谓词的出现。下一批只追加这些出现对应的新片段,空批追加空片段,END证明剩余输入为空。不同连续切分不改变任何出现的判定。顺序不变的gather也适用;重排gather只保留bag等式,不能继续要求输出序列完全相同。
资源寿命与错误路径
第一批借用代次e保存到父算子后,子算子下次拉取把同一列缓冲改写为e+1。旧引用不再有效;即使S被复制,列数组仍可能已经变化。正确消费者在拉取前复制要保存的I、值、validity与选择信息,或明确延长资源租期。检查器的snapshot生成拥有自己值的结果,借用对象则在下一次拉取、ERROR、END或close后报StaleBatch。
读到第一批后注入读取错误,下一次及随后拉取都返回ERROR;不会把未读八行当不存在,也不会把前缀SUM当成功结果。外层finally总会close。close可重复,之后拉取为合同错误。实际页引用还需要pin和同步机制;代次检查只是本单线程模型的可见失效检测。
迁移d=[1,0]、S=[0],只对活动槽算10/d得到[10];先对全部槽计算再blend会在第二槽除零。总定义的首版证明没有覆盖这种部分表达式;若要扩展,必须像这个显式循环一样保证inactive lane不求值,且活动槽的异常传播规则明确。SQL文本中AND的左右次序本身不提供这一保证。
两个访问路径的集合与bag
A=[0,1,3,5,7,8,10],B=[0,1,2,4,5,7,10,11]。
- A∩B=[0,1,5,7,10],五份,SUM=240
- A∪B=[0,1,2,3,4,5,7,8,10,11],十份,SUM=520
- 直接拼接A与B有十五份;五个重叠TID各计两次,350+410=760
- 对AND输出值去重得到[10,50,70,100],只有四份,SUM=230
AND/OR在TID层消除同一出现的重复访问,不在值层消除独立出现。OR的重检必须仍是原OR,不能错误地要求两条路径都成立。NULL在位置6使y=1为UNKNOWN;该行x=1为FALSE,故AND与OR都不为TRUE。
内存退化后的完整恢复
玩具表示预算只计可变位图条目:非空exact页3单位,lossy页1单位,控制区另计;它不表示Python实际内存或PostgreSQL字节数,也不是整个执行的峰值空间。检查器同时保留A、B、exact、limited及复制中间量;预算只约束交付的单个位图。真正的受限构建必须另计这些同时存活结构,或设计流式/原地转换并证明峰值。初始AND候选有四个exact页,共12。
预算10按既定策略先退化页1,候选为[0,1,3,4,5,7,10];新增位置3的y为0,位置4的x为0。完整AND重检删除它们,仍SUM240;若直接输出候选会得310。预算4将四页全lossy,访问全部十二个位置再筛出五份。预算3连四个页标记都存不下,应报CapacityExceeded并不给成功结果。
先退化A的页1再与B相交,B在该页只列出全局4、5。组合后位置仍精确为{4,5},但recheck=true;位置4依然不是AND真结果。这展示exact位置和谓词确定性是两个维度。
普遍证明分两步:每条路径的真匹配都在其候选中,交并及降精度保持这个包含关系;然后逐候选核验visible与完整q,输出既不会漏真结果,也不会留假结果。检查器穷举单页真假集合及候选超集、两种组合,另穷举十二行的可见性子集;这些是实现证据,普遍保证来自两项包含证明。
操作、字节与页的分账
本例满批扫描K=1、2、4、5、12的数据分派依次为12、6、3、3、1,每次执行另有一个END调用。所有计划仍检查十二份输入谓词;本例实现总是读取x、y两列,共24个逻辑字段。一般最多K条的接口可返回更多短批或空批,ceil(N/K)不再是实际调用次数。
若为账本统一把每个字段和内部出现编号视为8字节槽,读取x、y的逻辑载荷为192B;延迟取五个v是40B;复制五份(i,v)是80B。这些是不同阶段的字节事件,不是独立堆I/O次数,validity、选择列表、对齐及控制元数据另计。NULL载荷无语义,192B是为全部谓词槽预留/访问的模型上界,不强迫读取NULL的旧载荷。
假定两个索引共读2页、冷缓存、按页集中处理且每堆页只读一次:exact或只退化页1的候选均覆盖4个堆页,因此总页读6,无索引全扫为4。降精度5→7只增加两次重检,本例没有增加候选页数。只匹配0、1的迁移在仍需2个索引页的假设下为2+1=3;不得免费全扫得到候选后再宣称索引只花3。
分派成本、谓词计算、位图字操作、gather、输出复制、I/O与同时工作空间应该保留独立单位。稠密N位AND/OR需O(ceil(N/w))字操作;本检查器用Python集合做参考规格,不冒充密集字数组的性能实现。K变大带来的cache变化是条件因素,程序没有墙钟计时,也不输出某数据库加速倍数。
迁移ORDER BY:把输出改为u=120−10i,五个堆序值为[120,110,70,50,20]。候选正确性和份数保持,升序输出却需要[20,50,70,110,120];位图按堆页访问已丢失原索引键序,必须另计排序或证明其他顺序方案。
下载、运行与错误定位
下载Python 3标准库检查器。它不安装依赖、不访问网络、不写数据库;正常Python及python -O均保留全部显式检查。
python3 foundations-batch-bitmap-checker.py
python3 foundations-batch-bitmap-checker.py --batch-sizes 1,5 --bitmap-budget 4
python3 foundations-batch-bitmap-checker.py --bitmap-budget 3
前两条成功并输出JSON,最后一条以非零状态报告CapacityExceeded。默认预算10得到七个候选、五个结果和240。程序核对2048种连续切分、37050个小型标量/批次组合、2450个逐页组合及8192个可见性实例,还测试尾槽、空输入、真假容量、读取失败、借用失效和inactive trap。布尔值、负数与小数不能冒充合法容量。标准库SQLite另核对固定AND、OR输出及空输入SUM;不据此声称运行了PostgreSQL位图访问。
若K=1提前只得20,检查是否把DATA(empty)当EOF;若K=5多出旧值,检查循环是否用K而非n;若第二批输出40、50,检查是否只有一列压紧;若OR有十五份,检查是否拼接访问路径;若AND只剩四份,检查是否按v去重;若预算10得310,检查是否遗漏完整重检;若失败后给出部分SUM,检查是否把FAILED伪装成正常结束。
本任务是单线程、固定快照合同下的可运行参考规格,不是完整向量引擎、生产位图实现、MVCC扫描器或硬件测量。原DB-4三种连接与NULL-6语义改写继续独立有效。
参考资料
- MonetDB/X100原论文 §4.1.1、§4.2支撑选择位置传播;具体接口、例子、证明与测试由本任务构造
- PostgreSQL 18多索引组合文档支撑访问路径组合和排序边界
- PostgreSQL滚动master tidbitmap.c支撑exact/lossy及recheck的机制背景;本文对称代数及预算规则不是该生产源码的逐行移植