Skip to content

Commit f327db5

Browse files
committed
C
1 parent 09ef2f8 commit f327db5

File tree

4 files changed

+93
-0
lines changed

4 files changed

+93
-0
lines changed

src/backend/access/heap/heapam.c

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1044,7 +1044,12 @@ heapgettup_pagemode(HeapScanDesc scan,
10441044
* ----------------------------------------------------------------
10451045
*/
10461046

1047+
/**
1048+
begin scan的核心就是创建一个TableScanDesc
10471049
1050+
note: nkeys一般针对索引扫描才设置;对于顺序扫描,nkeys为0
1051+
因为过滤条件放到获取slot后的过滤函数中ExecQual()
1052+
*/
10481053
TableScanDesc
10491054
heap_beginscan(Relation relation, Snapshot snapshot,
10501055
int nkeys, ScanKey key,
@@ -1159,6 +1164,80 @@ heap_beginscan(Relation relation, Snapshot snapshot,
11591164
return (TableScanDesc) scan;
11601165
}
11611166

1167+
/**
1168+
在 PostgreSQL 中,`heap_rescan` 函数用于重新初始化表扫描(table scan),
1169+
以便在现有的扫描上下文中重新开始扫描操作,而无需关闭并重新打开扫描。
1170+
1171+
调用 `rescan` 的场景通常出现在以下几种情况下:
1172+
---
1173+
1174+
### 1. **嵌套循环连接(Nested Loop Join)**
1175+
在嵌套循环连接中,外部表的每一行都会触发对内部表的重新扫描。例如:
1176+
1177+
```sql
1178+
SELECT *
1179+
FROM outer_table o
1180+
JOIN inner_table i
1181+
ON o.id = i.id;
1182+
```
1183+
1184+
在这种情况下,每次处理外部表的一行时,内部表需要重新扫描以匹配当前的外部行。这时会调用 `rescan` 来重置内部表的扫描状态。
1185+
1186+
---
1187+
1188+
### 2. **重复使用扫描节点**
1189+
在查询计划中,如果某个扫描节点需要被多次使用(例如在某些子查询或递归查询中),则会调用 `rescan` 来重置扫描状态。例如:
1190+
1191+
```sql
1192+
SELECT *
1193+
FROM (SELECT * FROM table WHERE column = 1) subquery
1194+
WHERE subquery.column > 10;
1195+
```
1196+
1197+
在这种情况下,子查询的扫描可能会被多次使用,每次使用前需要调用 `rescan`。
1198+
1199+
---
1200+
1201+
### 3. **参数化查询**
1202+
当查询计划中包含参数化路径(Parameterized Path)时,扫描节点可能需要根据不同的参数值重新扫描。例如:
1203+
1204+
```sql
1205+
SELECT *
1206+
FROM table
1207+
WHERE column = $1;
1208+
```
1209+
1210+
在这种情况下,每次参数 `$1` 的值发生变化时,都会调用 `rescan` 来重新初始化扫描。
1211+
1212+
---
1213+
1214+
### 4. **游标(Cursor)操作**
1215+
在使用游标时,可能需要重新扫描表。例如,当游标被重新定位到起始位置时,可能会调用 `rescan` 来重置扫描状态。
1216+
1217+
```sql
1218+
DECLARE my_cursor CURSOR FOR SELECT * FROM table;
1219+
FETCH ALL FROM my_cursor;
1220+
MOVE BACKWARD ALL IN my_cursor;
1221+
FETCH ALL FROM my_cursor; -- 此时可能触发 rescan
1222+
```
1223+
1224+
---
1225+
1226+
### 5. **查询计划的动态调整**
1227+
在某些复杂查询中,查询计划可能会动态调整。例如,在某些情况下,查询优化器可能会决定重新扫描某个表以获取最新的数据或重新评估条件。
1228+
1229+
---
1230+
1231+
### 6. **并行扫描中的重新分配**
1232+
在并行扫描中,如果需要重新分配扫描任务(例如由于工作线程的状态变化),可能会调用 `rescan` 来重新初始化扫描。
1233+
1234+
---
1235+
1236+
### 总结
1237+
`rescan` 的调用场景主要集中在需要重新初始化扫描状态的情况下,
1238+
例如嵌套循环连接、重复使用扫描节点、参数化查询、游标操作以及并行扫描等。
1239+
通过调用 `rescan`,可以避免重新创建扫描上下文,从而提高性能并减少资源消耗。
1240+
*/
11621241
void
11631242
heap_rescan(TableScanDesc sscan, ScanKey key, bool set_params,
11641243
bool allow_strat, bool allow_sync, bool allow_pagemode)
@@ -1535,6 +1614,7 @@ heap_fetch(Relation relation,
15351614
/*
15361615
* Fetch and pin the appropriate page of the relation.
15371616
*/
1617+
// pin就是refcount,放在buf_desc->state中
15381618
buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
15391619

15401620
/*

src/backend/access/table/tableam.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,11 @@ table_block_parallelscan_nextpage(Relation rel,
496496
BlockNumber page;
497497
uint64 nallocated;
498498

499+
/**
500+
分配一批连续的块号(称为chunk)给每个worker,
501+
从而保证每个worker都能收益于os prefech
502+
*/
503+
499504
/*
500505
* The logic below allocates block numbers out to parallel workers in a
501506
* way that each worker will receive a set of consecutive block numbers to

src/backend/executor/execScan.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ ExecScan(ScanState *node,
176176
*/
177177
if (!qual && !projInfo)
178178
{
179+
// 没有qualification和projection,直接返回scan tuple
179180
ResetExprContext(econtext);
180181
return ExecScanFetch(node, accessMtd, recheckMtd);
181182
}
@@ -192,6 +193,7 @@ ExecScan(ScanState *node,
192193
*/
193194
for (;;)
194195
{
196+
// 因为需要判断quliafication,所以需要放到循环中
195197
TupleTableSlot *slot;
196198

197199
slot = ExecScanFetch(node, accessMtd, recheckMtd);
@@ -222,6 +224,7 @@ ExecScan(ScanState *node,
222224
* when the qual is null ... saves only a few cycles, but they add up
223225
* ...
224226
*/
227+
// execQual()用于判断tuple是否满足qual
225228
if (qual == NULL || ExecQual(qual, econtext))
226229
{
227230
/*

src/include/access/relscan.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ typedef struct TableScanDescData
5151
} TableScanDescData;
5252
typedef struct TableScanDescData *TableScanDesc;
5353

54+
/**
55+
各个parallel worker之间是通过共享内存来通信的:
56+
一般都是分为2部分:shm中的共享对象(ParallelTableScanDescData,DSM动态共享内存机制分配)
57+
和本地worker的对象(ParallelBlockTableScanDescData)
58+
*/
5459
/*
5560
* Shared state for parallel table scan.
5661
*

0 commit comments

Comments
 (0)