Skip to content

Commit 0515371

Browse files
committed
C on sequence
1 parent aed05f9 commit 0515371

File tree

4 files changed

+171
-2
lines changed

4 files changed

+171
-2
lines changed

src/backend/commands/sequence.c

Lines changed: 158 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq)
162162
}
163163
}
164164

165+
// 根据plan树填充参数
165166
/* Check and set all option values */
166167
init_params(pstate, seq->options, seq->for_identity, true,
167168
&seqform, &seqdataform,
@@ -194,6 +195,7 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq)
194195
coldef->is_not_null = true;
195196
null[i - 1] = false;
196197

198+
// List *tableElts; /* column definitions (list of ColumnDef) */
197199
stmt->tableElts = lappend(stmt->tableElts, coldef);
198200
}
199201

@@ -205,6 +207,7 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq)
205207
stmt->tablespacename = NULL;
206208
stmt->if_not_exists = seq->if_not_exists;
207209

210+
// 建立类heap表,存储具体的seq数据
208211
address = DefineRelation(stmt, RELKIND_SEQUENCE, seq->ownerId, NULL, NULL);
209212
seqoid = address.objectId;
210213
Assert(seqoid != InvalidOid);
@@ -213,15 +216,25 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq)
213216
tupDesc = RelationGetDescr(rel);
214217

215218
/* now initialize the sequence's data */
219+
220+
// 填入heap表的初始数据:从value和null构造这个tuple
216221
tuple = heap_form_tuple(tupDesc, value, null);
222+
/**
223+
* 具体的insert逻辑:
224+
* 通过PageAddItems()等函数将tuple插入到一个page中(随后持久化到磁盘上的heap表)
225+
* 还有xlog相关操作
226+
*/
217227
fill_seq_with_data(rel, tuple);
218228

219229
/* process OWNED BY if given */
220230
if (owned_by)
221231
process_owned_by(rel, owned_by, seq->for_identity);
222232

233+
// 同以前的解读:这里的nolock表明事务结束后统一释放锁
223234
sequence_close(rel, NoLock);
224235

236+
// pg_sequence作为元数据表(如对应seq的min/max),
237+
// 具体的数据存储(如当前值)在对应的类heap存储seq中
225238
/* fill in pg_sequence */
226239
rel = table_open(SequenceRelationId, RowExclusiveLock);
227240
tupDesc = RelationGetDescr(rel);
@@ -365,6 +378,14 @@ fill_seq_fork_with_data(Relation rel, HeapTuple tuple, ForkNumber forkNum)
365378

366379
/* Initialize first page of relation with special magic number */
367380

381+
/**
382+
EB_LOCK_FIRST: 在扩展数据块之前获取锁,以确保操作的安全性和一致性。
383+
EB_SKIP_EXTENSION_LOCK: 跳过扩展锁的获取,可能用于优化性能,但需要确保调用者已经处理了并发问题。
384+
385+
ExtendBufferedRel 函数会为缓冲区加上独占锁(exclusive lock):
386+
设置 EB_SKIP_EXTENSION_LOCK 后,ExtendBufferedRel不会获取全局的常规锁(用于防止多个进程同时扩展同一个关系的存储文件),
387+
但仍会为返回的缓冲区加上独占锁。
388+
*/
368389
buf = ExtendBufferedRel(BMR_REL(rel), forkNum, NULL,
369390
EB_LOCK_FIRST | EB_SKIP_EXTENSION_LOCK);
370391
Assert(BufferGetBlockNumber(buf) == 0);
@@ -394,6 +415,10 @@ fill_seq_fork_with_data(Relation rel, HeapTuple tuple, ForkNumber forkNum)
394415
/* check the comment above nextval_internal()'s equivalent call. */
395416
if (RelationNeedsWAL(rel))
396417
GetTopTransactionId();
418+
/**
419+
GetTopTransactionId() 的主要作用是确保当前事务已经分配了一个顶层事务 ID(Transaction ID, XID)。
420+
如果事务尚未分配 XID,该函数会分配一个新的事务 ID
421+
*/
397422

398423
START_CRIT_SECTION();
399424

@@ -407,7 +432,7 @@ fill_seq_fork_with_data(Relation rel, HeapTuple tuple, ForkNumber forkNum)
407432
/* XLOG stuff */
408433
if (RelationNeedsWAL(rel) || forkNum == INIT_FORKNUM)
409434
{
410-
xl_seq_rec xlrec;
435+
xl_seq_rec xlrec; // seq的特定xlog结构
411436
XLogRecPtr recptr;
412437

413438
XLogBeginInsert();
@@ -418,6 +443,7 @@ fill_seq_fork_with_data(Relation rel, HeapTuple tuple, ForkNumber forkNum)
418443
XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
419444
XLogRegisterData((char *) tuple->t_data, tuple->t_len);
420445

446+
// seq的RM
421447
recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
422448

423449
PageSetLSN(page, recptr);
@@ -527,6 +553,10 @@ AlterSequence(ParseState *pstate, AlterSeqStmt *stmt)
527553
/* update the pg_sequence tuple (we could skip this in some cases...) */
528554
CatalogTupleUpdate(rel, &seqtuple->t_self, seqtuple);
529555

556+
/**
557+
InvokeObjectPostAlterHook 是 PostgreSQL 中用于触发对象修改后钩子(Post-Alter Hook)的函数。
558+
它的主要作用是在数据库对象(如表、序列等)被修改后,通知相关的扩展或插件,以便它们可以执行自定义的后续操作
559+
*/
530560
InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
531561

532562
ObjectAddressSet(address, RelationRelationId, relid);
@@ -581,6 +611,8 @@ DeleteSequenceTuple(Oid relid)
581611
CatalogTupleDelete(rel, &tuple->t_self);
582612

583613
ReleaseSysCache(tuple);
614+
// TODO: 这里为什么不使用NoLock模式(等到事务结束再释放)?
615+
// 难道和系统表或删除有关?
584616
table_close(rel, RowExclusiveLock);
585617
}
586618

@@ -619,6 +651,13 @@ nextval_oid(PG_FUNCTION_ARGS)
619651
PG_RETURN_INT64(nextval_internal(relid, true));
620652
}
621653

654+
/**
655+
返回下一个序列值
656+
- 缓存值的使用: 如果序列已经缓存了一些值,函数会直接返回缓存中的下一个值,而无需访问磁盘或更新序列元数据。
657+
- 序列值的计算: 如果缓存值已用尽,函数会从序列的存储中读取当前值,并根据序列的增量(increment)计算下一个值。
658+
- 日志记录: 如果启用了 WAL(Write-Ahead Logging),函数会记录序列的更新操作,以确保在崩溃恢复时能够正确回放。
659+
- 并发控制: 函数通过锁机制确保在多事务环境下的操作安全性,避免多个事务同时修改同一序列导致的不一致。
660+
*/
622661
int64
623662
nextval_internal(Oid relid, bool check_permissions)
624663
{
@@ -665,6 +704,10 @@ nextval_internal(Oid relid, bool check_permissions)
665704
*/
666705
PreventCommandIfParallelMode("nextval()");
667706

707+
/**
708+
elm->last 表示上一次返回的序列值,而 elm->cached 表示当前缓存的最后一个序列值。
709+
如果两者不相等,说明序列中还有未使用的缓存值,可以直接从缓存中获取下一个值,而无需访问磁盘或更新序列元数据
710+
*/
668711
if (elm->last != elm->cached) /* some numbers were cached */
669712
{
670713
Assert(elm->last_valid);
@@ -686,8 +729,14 @@ nextval_internal(Oid relid, bool check_permissions)
686729
cycle = pgsform->seqcycle;
687730
ReleaseSysCache(pgstuple);
688731

732+
689733
/* lock page buffer and read tuple */
734+
/**
735+
这里给buffer加了独占锁(同时也pin了buffer):
736+
read_seq_tuple 函数通过调用 ReadBuffer:它对缓冲区也进行了pin操作
737+
*/
690738
seq = read_seq_tuple(seqrel, &buf, &seqdatatuple);
739+
691740
page = BufferGetPage(buf);
692741

693742
last = next = result = seq->last_value;
@@ -700,6 +749,9 @@ nextval_internal(Oid relid, bool check_permissions)
700749
fetch--;
701750
}
702751

752+
/**
753+
每SEQ_LOG_VALS刷一次xlog,平衡了性能和一致性之间的需求
754+
*/
703755
/*
704756
* Decide whether we should emit a WAL log record. If so, force up the
705757
* fetch count to grab SEQ_LOG_VALS more values than we actually need to
@@ -807,6 +859,9 @@ nextval_internal(Oid relid, bool check_permissions)
807859
/* ready to change the on-disk (or really, in-buffer) tuple */
808860
START_CRIT_SECTION();
809861

862+
// NB:We must mark the buffer dirty before doing XLogInsert()
863+
// 和WAL语义似乎矛盾了,见自己的代码笔记中的gpt讲解
864+
810865
/*
811866
* We must mark the buffer dirty before doing XLogInsert(); see notes in
812867
* SyncOneBuffer(). However, we don't apply the desired changes just yet.
@@ -1829,7 +1884,9 @@ pg_sequence_last_value(PG_FUNCTION_ARGS)
18291884
PG_RETURN_NULL();
18301885
}
18311886

1832-
1887+
/**
1888+
simple example to demonstrate the use of the redolog
1889+
*/
18331890
void
18341891
seq_redo(XLogReaderState *record)
18351892
{
@@ -1874,6 +1931,7 @@ seq_redo(XLogReaderState *record)
18741931
PageSetLSN(localpage, lsn);
18751932

18761933
memcpy(page, localpage, BufferGetPageSize(buffer));
1934+
// 让bgwriter待会儿把这个buffer写到磁盘上
18771935
MarkBufferDirty(buffer);
18781936
UnlockReleaseBuffer(buffer);
18791937

@@ -1905,3 +1963,101 @@ seq_mask(char *page, BlockNumber blkno)
19051963

19061964
mask_unused_space(page);
19071965
}
1966+
1967+
/**
1968+
这段代码是 PostgreSQL 中用于管理序列(sequence)的核心实现。序列是一种特殊的数据库对象,通常用于生成唯一的数值(例如主键)。以下是对代码的详细解释:
1969+
1970+
---
1971+
1972+
### 1. **序列的日志与缓存**
1973+
- **`SEQ_LOG_VALS`**:
1974+
定义了预先记录的序列值数量(32)。通过预记录多个值,可以减少每次获取序列值时的日志记录开销。如果发生崩溃,可能会跳过这些预记录的值。
1975+
- **`SEQ_MAGIC`**:
1976+
定义了序列缓冲区页面的特殊标识符,用于验证页面的正确性。
1977+
1978+
---
1979+
1980+
### 2. **序列的元数据结构**
1981+
- **`sequence_magic`**:
1982+
包含一个 `magic` 字段,用于标识序列缓冲区页面的特殊区域。
1983+
- **`SeqTableData`**:
1984+
存储每个序列的会话状态,包括:
1985+
- `relid`: 序列的 OID。
1986+
- `filenumber`: 序列的文件编号。
1987+
- `lxid`: 上次操作序列的事务 ID。
1988+
- `last_valid`: 是否有有效的上次值。
1989+
- `last` 和 `cached`: 上次返回的值和缓存的值。
1990+
- `increment`: 序列的增量值。
1991+
1992+
`SeqTableData` 的实例存储在哈希表 `seqhashtab` 中,用于快速访问。
1993+
1994+
---
1995+
1996+
### 3. **序列的创建与初始化**
1997+
- **`DefineSequence`**:
1998+
用于创建新的序列对象。主要步骤包括:
1999+
1. 检查是否已存在同名序列(如果指定了 `if_not_exists`)。
2000+
2. 初始化序列的参数(如起始值、增量、最大值等)。
2001+
3. 创建序列的元数据和数据文件。
2002+
4. 如果指定了 `OWNED BY`,设置序列与表的依赖关系。
2003+
2004+
- **`fill_seq_with_data`**:
2005+
初始化序列的数据文件。如果序列是未记录的(unlogged),还会初始化 `INIT_FORKNUM`。
2006+
2007+
---
2008+
2009+
### 4. **序列的修改与重置**
2010+
- **`AlterSequence`**:
2011+
修改序列的定义,例如改变增量值或最大值。必要时会重写序列的数据文件以确保事务性。
2012+
- **`ResetSequence`**:
2013+
将序列重置为初始状态。通过创建新的文件编号实现事务性重置。
2014+
2015+
---
2016+
2017+
### 5. **序列值的获取与设置**
2018+
- **`nextval_internal`**:
2019+
获取序列的下一个值。主要逻辑包括:
2020+
1. 检查权限和并发限制。
2021+
2. 如果有缓存值,直接返回缓存值。
2022+
3. 如果需要新值,从序列文件中读取并更新。
2023+
4. 如果需要日志记录(WAL),记录额外的值以减少日志开销。
2024+
2025+
- **`do_setval`**:
2026+
设置序列的当前值。支持两种模式:
2027+
1. `iscalled = true`: 更新 `currval` 状态。
2028+
2. `iscalled = false`: 仅更新序列值。
2029+
2030+
---
2031+
2032+
### 6. **序列的依赖管理**
2033+
- **`process_owned_by`**:
2034+
处理 `OWNED BY` 选项,建立序列与表列之间的依赖关系。确保序列与表具有相同的所有者和命名空间。
2035+
2036+
---
2037+
2038+
### 7. **其他功能**
2039+
- **`ResetSequenceCaches`**:
2040+
清除会话中的序列缓存。
2041+
- **`seq_redo`**:
2042+
在恢复过程中重做序列的日志记录。
2043+
- **`sequence_options`**:
2044+
返回序列的参数列表,用于显示或信息查询。
2045+
2046+
---
2047+
2048+
### 8. **设计特点**
2049+
- **性能优化**:
2050+
通过缓存和预记录减少频繁的磁盘 I/O 和日志记录。
2051+
- **事务性**:
2052+
通过文件编号的更改和日志记录确保序列操作的事务性。
2053+
- **灵活性**:
2054+
支持多种操作(创建、修改、重置、获取值等)以及复杂的依赖管理。
2055+
2056+
---
2057+
2058+
### 9. **总结**
2059+
这段代码展示了 PostgreSQL 对序列的高效实现,结合了性能优化和事务性保障。它支持多种场景下的序列操作,是数据库生成唯一值的重要基础设施。
2060+
2061+
另外的详解:
2062+
https://mp.weixin.qq.com/s/JOgsMPmq3gnYMNkKa5tbCw
2063+
*/

src/backend/storage/buffer/bufmgr.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1189,6 +1189,10 @@ PinBufferForBlock(Relation rel,
11891189
return BufferDescriptorGetBuffer(bufHdr);
11901190
}
11911191

1192+
/**
1193+
ReadBuffer时会自动pin buffer(显然)
1194+
在缓冲区已经存在,但需要增加引用计数时才会单独调用PinBuffer
1195+
*/
11921196
/*
11931197
* ReadBuffer_common -- common logic for all ReadBuffer variants
11941198
*
@@ -2223,6 +2227,7 @@ ExtendBufferedRelShared(BufferManagerRelation bmr,
22232227
* we get the lock.
22242228
*/
22252229
if (!(flags & EB_SKIP_EXTENSION_LOCK))
2230+
// 该"常规锁"用来保护对关系的扩展(Extension)操作
22262231
LockRelationForExtension(bmr.rel, ExclusiveLock);
22272232

22282233
/*

src/backend/tcop/utility.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1080,6 +1080,10 @@ standard_ProcessUtility(PlannedStmt *pstmt,
10801080
CommandCounterIncrement();
10811081
}
10821082

1083+
/**
1084+
由于事件触发器的复杂性,某些语句需要通过 "慢路径" 进行处理,
1085+
以确保触发器的正确执行。这种慢路径通常涉及额外的逻辑检查和触发器调用。
1086+
*/
10831087
/*
10841088
* The "Slow" variant of ProcessUtility should only receive statements
10851089
* supported by the event triggers facility. Therefore, we always

src/include/catalog/objectaddress.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@
1818
#include "storage/lockdefs.h"
1919
#include "utils/relcache.h"
2020

21+
/**
22+
classId 字段存储对象的类型(如表、序列、索引等)。
23+
objectId 字段存储对象的唯一标识符(OID)。
24+
*/
2125
/*
2226
* An ObjectAddress represents a database object of any type.
2327
*/

0 commit comments

Comments
 (0)