59.1. 采样方法支持函数 #
TSM 处理器函数返回一个通过 palloc 分配的 TsmRoutine 结构体,其中包含下文所述支持函数的指针。大多数函数是必需的,但有些是可选的,对应指针可以为 NULL。
void
SampleScanGetSampleSize (PlannerInfo *root,
RelOptInfo *baserel,
List *paramexprs,
BlockNumber *pages,
double *tuples);
该函数在规划阶段调用。它必须估计采样扫描期间将读取的关系页数,以及该扫描将选出的元组数。(例如,可以先估计采样比例,再将
baserel->pages 和 baserel->tuples
的值乘以该比例,并确保结果舍入为整数值。)paramexprs 列表保存 TABLESAMPLE
子句参数对应的表达式。如果出于估算目的需要这些值,建议使用
estimate_expression_value() 尝试将这些表达式化简为常量;但即使无法化简,该函数也必须给出大小估计,而且即便这些值看起来无效也不应失败(别忘了,它们只是对运行时取值的估计)。pages 和 tuples 参数是输出参数。
void
InitSampleScan (SampleScanState *node,
int eflags);
为 SampleScan 计划节点的执行进行初始化。该函数在执行器启动期间调用。它应执行开始处理前所需的任何初始化工作。SampleScanState 节点已经创建,但其
tsm_state 字段为 NULL。InitSampleScan 函数可以通过 palloc 分配采样方法所需的任何内部状态数据,并将其指针存入 node->tsm_state。待扫描表的信息可通过 SampleScanState 节点的其他字段访问(但注意 node->ss.ss_currentScanDesc 扫描描述符尚未设置)。eflags 包含描述执行器对此计划节点工作模式的标志位。
当 (eflags & EXEC_FLAG_EXPLAIN_ONLY) 为真时,不会实际执行该扫描,因此这个函数只应做使节点状态对 EXPLAIN
和 EndSampleScan 有效所需的最少工作。
该函数可以省略(将指针设为 NULL),此时
BeginSampleScan 必须执行采样方法所需的全部初始化工作。
void
BeginSampleScan (SampleScanState *node,
Datum *params,
int nparams,
uint32 seed);
开始执行一次采样扫描。它会在第一次尝试提取一个元组之前调用;如果该扫描需要重启,也可能再次调用。待扫描表的信息可通过
SampleScanState 节点的字段访问(但注意
node->ss.ss_currentScanDesc 扫描描述符尚未设置)。长度为 nparams 的 params 数组包含
TABLESAMPLE 子句中提供的参数值。这些参数的个数和类型与该采样方法的 parameterTypes 列表中指定的一致,并且已确认不为 NULL。seed 包含采样方法内部生成随机数时要使用的种子;如果给定了 REPEATABLE 值,它就是从该值派生出的 hash 值,否则就是 random() 的结果。
该函数可以调整 node->use_bulkread 和
node->use_pagemode 字段。如果 node->use_bulkread 为 true(默认如此),扫描将使用一种鼓励在使用后回收缓冲区的缓冲区访问策略。如果该扫描只会访问该表中的一小部分页,把它设为 false
可能更合理。如果 node->use_pagemode 为 true(默认如此),扫描将对每个访问页上的所有元组以单遍方式执行可见性检查。如果该扫描只会从每个访问页中选出一小部分元组,把它设为
false 可能更合理。这样执行的元组可见性检查会更少,但每次检查的代价会更高,因为需要更多加锁。
如果采样方法被标记为 repeatable_across_scans,那么在重新扫描期间它必须能够像最初那样选出相同的一组元组;也就是说,重新调用
BeginSampleScan 必须像之前一样选出相同的元组(如果 TABLESAMPLE 参数和种子没有变化)。
BlockNumber NextSampleBlock (SampleScanState *node, BlockNumber nblocks);
返回下一个待扫描页的块号;如果没有剩余页可扫描,则返回
InvalidBlockNumber。
该函数可以省略(将指针设为 NULL),此时核心代码将对整个关系执行顺序扫描。这样的扫描可能使用同步扫描,因此采样方法不能假定每次扫描都会按相同顺序访问关系页。
OffsetNumber
NextSampleTuple (SampleScanState *node,
BlockNumber blockno,
OffsetNumber maxoffset);
返回指定页上下一个要采样的元组的偏移号;如果没有剩余元组可采样,则返回
InvalidOffsetNumber。maxoffset 是该页上正在使用的最大偏移号。
注意
NextSampleTuple 不会被显式告知在
1 .. maxoffset 范围内哪些偏移号实际上包含有效元组。通常这不是问题,因为核心代码会忽略对缺失或不可见元组的采样请求;这不应在样本中引入任何偏差。不过,如有需要,该函数可以使用
node->donetuples 来检查它返回的元组中有多少是有效且可见的。
注意
NextSampleTuple 绝不能假定
blockno 与最近一次 NextSampleBlock
调用返回的页号相同。它是由之前某次
NextSampleBlock 调用返回的,但核心代码可以在真正扫描页之前就调用 NextSampleBlock,以支持预取。可以假定的是,一旦开始对某个给定页进行采样,后续连续的
NextSampleTuple 调用在返回
InvalidOffsetNumber 之前都指向同一页。
void EndSampleScan (SampleScanState *node);
结束扫描并释放资源。通常不必专门释放通过 palloc 分配的内存,但任何对外可见的资源都应清理掉。在没有此类资源的常见情况下,可以省略这个函数(将指针设为 NULL)。
报告文档问题
阅读 上游文档. 通过 PostgreSQL 文档反馈表单.