@@ -162,6 +162,7 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq)
162
162
}
163
163
}
164
164
165
+ // 根据plan树填充参数
165
166
/* Check and set all option values */
166
167
init_params (pstate , seq -> options , seq -> for_identity , true,
167
168
& seqform , & seqdataform ,
@@ -194,6 +195,7 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq)
194
195
coldef -> is_not_null = true;
195
196
null [i - 1 ] = false;
196
197
198
+ // List *tableElts; /* column definitions (list of ColumnDef) */
197
199
stmt -> tableElts = lappend (stmt -> tableElts , coldef );
198
200
}
199
201
@@ -205,6 +207,7 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq)
205
207
stmt -> tablespacename = NULL ;
206
208
stmt -> if_not_exists = seq -> if_not_exists ;
207
209
210
+ // 建立类heap表,存储具体的seq数据
208
211
address = DefineRelation (stmt , RELKIND_SEQUENCE , seq -> ownerId , NULL , NULL );
209
212
seqoid = address .objectId ;
210
213
Assert (seqoid != InvalidOid );
@@ -213,15 +216,25 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq)
213
216
tupDesc = RelationGetDescr (rel );
214
217
215
218
/* now initialize the sequence's data */
219
+
220
+ // 填入heap表的初始数据:从value和null构造这个tuple
216
221
tuple = heap_form_tuple (tupDesc , value , null );
222
+ /**
223
+ * 具体的insert逻辑:
224
+ * 通过PageAddItems()等函数将tuple插入到一个page中(随后持久化到磁盘上的heap表)
225
+ * 还有xlog相关操作
226
+ */
217
227
fill_seq_with_data (rel , tuple );
218
228
219
229
/* process OWNED BY if given */
220
230
if (owned_by )
221
231
process_owned_by (rel , owned_by , seq -> for_identity );
222
232
233
+ // 同以前的解读:这里的nolock表明事务结束后统一释放锁
223
234
sequence_close (rel , NoLock );
224
235
236
+ // pg_sequence作为元数据表(如对应seq的min/max),
237
+ // 具体的数据存储(如当前值)在对应的类heap存储seq中
225
238
/* fill in pg_sequence */
226
239
rel = table_open (SequenceRelationId , RowExclusiveLock );
227
240
tupDesc = RelationGetDescr (rel );
@@ -365,6 +378,14 @@ fill_seq_fork_with_data(Relation rel, HeapTuple tuple, ForkNumber forkNum)
365
378
366
379
/* Initialize first page of relation with special magic number */
367
380
381
+ /**
382
+ EB_LOCK_FIRST: 在扩展数据块之前获取锁,以确保操作的安全性和一致性。
383
+ EB_SKIP_EXTENSION_LOCK: 跳过扩展锁的获取,可能用于优化性能,但需要确保调用者已经处理了并发问题。
384
+
385
+ ExtendBufferedRel 函数会为缓冲区加上独占锁(exclusive lock):
386
+ 设置 EB_SKIP_EXTENSION_LOCK 后,ExtendBufferedRel不会获取全局的常规锁(用于防止多个进程同时扩展同一个关系的存储文件),
387
+ 但仍会为返回的缓冲区加上独占锁。
388
+ */
368
389
buf = ExtendBufferedRel (BMR_REL (rel ), forkNum , NULL ,
369
390
EB_LOCK_FIRST | EB_SKIP_EXTENSION_LOCK );
370
391
Assert (BufferGetBlockNumber (buf ) == 0 );
@@ -394,6 +415,10 @@ fill_seq_fork_with_data(Relation rel, HeapTuple tuple, ForkNumber forkNum)
394
415
/* check the comment above nextval_internal()'s equivalent call. */
395
416
if (RelationNeedsWAL (rel ))
396
417
GetTopTransactionId ();
418
+ /**
419
+ GetTopTransactionId() 的主要作用是确保当前事务已经分配了一个顶层事务 ID(Transaction ID, XID)。
420
+ 如果事务尚未分配 XID,该函数会分配一个新的事务 ID
421
+ */
397
422
398
423
START_CRIT_SECTION ();
399
424
@@ -407,7 +432,7 @@ fill_seq_fork_with_data(Relation rel, HeapTuple tuple, ForkNumber forkNum)
407
432
/* XLOG stuff */
408
433
if (RelationNeedsWAL (rel ) || forkNum == INIT_FORKNUM )
409
434
{
410
- xl_seq_rec xlrec ;
435
+ xl_seq_rec xlrec ; // seq的特定xlog结构
411
436
XLogRecPtr recptr ;
412
437
413
438
XLogBeginInsert ();
@@ -418,6 +443,7 @@ fill_seq_fork_with_data(Relation rel, HeapTuple tuple, ForkNumber forkNum)
418
443
XLogRegisterData ((char * ) & xlrec , sizeof (xl_seq_rec ));
419
444
XLogRegisterData ((char * ) tuple -> t_data , tuple -> t_len );
420
445
446
+ // seq的RM
421
447
recptr = XLogInsert (RM_SEQ_ID , XLOG_SEQ_LOG );
422
448
423
449
PageSetLSN (page , recptr );
@@ -527,6 +553,10 @@ AlterSequence(ParseState *pstate, AlterSeqStmt *stmt)
527
553
/* update the pg_sequence tuple (we could skip this in some cases...) */
528
554
CatalogTupleUpdate (rel , & seqtuple -> t_self , seqtuple );
529
555
556
+ /**
557
+ InvokeObjectPostAlterHook 是 PostgreSQL 中用于触发对象修改后钩子(Post-Alter Hook)的函数。
558
+ 它的主要作用是在数据库对象(如表、序列等)被修改后,通知相关的扩展或插件,以便它们可以执行自定义的后续操作
559
+ */
530
560
InvokeObjectPostAlterHook (RelationRelationId , relid , 0 );
531
561
532
562
ObjectAddressSet (address , RelationRelationId , relid );
@@ -581,6 +611,8 @@ DeleteSequenceTuple(Oid relid)
581
611
CatalogTupleDelete (rel , & tuple -> t_self );
582
612
583
613
ReleaseSysCache (tuple );
614
+ // TODO: 这里为什么不使用NoLock模式(等到事务结束再释放)?
615
+ // 难道和系统表或删除有关?
584
616
table_close (rel , RowExclusiveLock );
585
617
}
586
618
@@ -619,6 +651,13 @@ nextval_oid(PG_FUNCTION_ARGS)
619
651
PG_RETURN_INT64 (nextval_internal (relid , true));
620
652
}
621
653
654
+ /**
655
+ 返回下一个序列值
656
+ - 缓存值的使用: 如果序列已经缓存了一些值,函数会直接返回缓存中的下一个值,而无需访问磁盘或更新序列元数据。
657
+ - 序列值的计算: 如果缓存值已用尽,函数会从序列的存储中读取当前值,并根据序列的增量(increment)计算下一个值。
658
+ - 日志记录: 如果启用了 WAL(Write-Ahead Logging),函数会记录序列的更新操作,以确保在崩溃恢复时能够正确回放。
659
+ - 并发控制: 函数通过锁机制确保在多事务环境下的操作安全性,避免多个事务同时修改同一序列导致的不一致。
660
+ */
622
661
int64
623
662
nextval_internal (Oid relid , bool check_permissions )
624
663
{
@@ -665,6 +704,10 @@ nextval_internal(Oid relid, bool check_permissions)
665
704
*/
666
705
PreventCommandIfParallelMode ("nextval()" );
667
706
707
+ /**
708
+ elm->last 表示上一次返回的序列值,而 elm->cached 表示当前缓存的最后一个序列值。
709
+ 如果两者不相等,说明序列中还有未使用的缓存值,可以直接从缓存中获取下一个值,而无需访问磁盘或更新序列元数据
710
+ */
668
711
if (elm -> last != elm -> cached ) /* some numbers were cached */
669
712
{
670
713
Assert (elm -> last_valid );
@@ -686,8 +729,14 @@ nextval_internal(Oid relid, bool check_permissions)
686
729
cycle = pgsform -> seqcycle ;
687
730
ReleaseSysCache (pgstuple );
688
731
732
+
689
733
/* lock page buffer and read tuple */
734
+ /**
735
+ 这里给buffer加了独占锁(同时也pin了buffer):
736
+ read_seq_tuple 函数通过调用 ReadBuffer:它对缓冲区也进行了pin操作
737
+ */
690
738
seq = read_seq_tuple (seqrel , & buf , & seqdatatuple );
739
+
691
740
page = BufferGetPage (buf );
692
741
693
742
last = next = result = seq -> last_value ;
@@ -700,6 +749,9 @@ nextval_internal(Oid relid, bool check_permissions)
700
749
fetch -- ;
701
750
}
702
751
752
+ /**
753
+ 每SEQ_LOG_VALS刷一次xlog,平衡了性能和一致性之间的需求
754
+ */
703
755
/*
704
756
* Decide whether we should emit a WAL log record. If so, force up the
705
757
* 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)
807
859
/* ready to change the on-disk (or really, in-buffer) tuple */
808
860
START_CRIT_SECTION ();
809
861
862
+ // NB:We must mark the buffer dirty before doing XLogInsert()
863
+ // 和WAL语义似乎矛盾了,见自己的代码笔记中的gpt讲解
864
+
810
865
/*
811
866
* We must mark the buffer dirty before doing XLogInsert(); see notes in
812
867
* SyncOneBuffer(). However, we don't apply the desired changes just yet.
@@ -1829,7 +1884,9 @@ pg_sequence_last_value(PG_FUNCTION_ARGS)
1829
1884
PG_RETURN_NULL ();
1830
1885
}
1831
1886
1832
-
1887
+ /**
1888
+ simple example to demonstrate the use of the redolog
1889
+ */
1833
1890
void
1834
1891
seq_redo (XLogReaderState * record )
1835
1892
{
@@ -1874,6 +1931,7 @@ seq_redo(XLogReaderState *record)
1874
1931
PageSetLSN (localpage , lsn );
1875
1932
1876
1933
memcpy (page , localpage , BufferGetPageSize (buffer ));
1934
+ // 让bgwriter待会儿把这个buffer写到磁盘上
1877
1935
MarkBufferDirty (buffer );
1878
1936
UnlockReleaseBuffer (buffer );
1879
1937
@@ -1905,3 +1963,101 @@ seq_mask(char *page, BlockNumber blkno)
1905
1963
1906
1964
mask_unused_space (page );
1907
1965
}
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
+ */
0 commit comments