内容演示:
postgres@zxm-VMware-Virtual-Platform:~/code/16$ psql -o /dev/null
psql (16.10)
Type "help" for help.
postgres=# \timing
Timing is on.
postgres=# select * from pg_database;
Time: 0.491 ms
postgres=# select * from pg_database;
Time: 0.676 ms
postgres=# select * from pg_database;
Time: 0.573 ms
postgres=# CREATE TEMP TABLE a_gtt (n numeric) ON COMMIT DELETE ROWS;
Time: 5.771 ms
postgres=# select * from a_gtt;
Time: 1.721 ms
postgres=# select * from a_gtt;
Time: 1.383 ms
postgres=# select * from a_gtt;
Time: 1.703 ms
postgres=# DO $$
DECLARE
v_sql VARCHAR(100);
BEGIN
FOR i IN 1..3000 LOOP
v_sql := 'CREATE TEMP TABLE a_gtt'||i||'(n numeric) ON COMMIT DELETE ROWS';
EXECUTE v_sql;
END LOOP;
END;
$$ LANGUAGE plpgsql;
Time: 4150.059 ms (00:04.150)
postgres=# select * from a_gtt;
Time: 1250.611 ms (00:01.251)
postgres=# select * from a_gtt;
Time: 1239.305 ms (00:01.239)
postgres=# select * from a_gtt;
Time: 1213.000 ms (00:01.213)
postgres=# select * from pg_database;
Time: 1.975 ms
postgres=# select * from pg_database;
Time: 0.981 ms
postgres=# select * from pg_database;
Time: 0.758 ms
粗略的判断,没有仔细研究,估计原因在于需要多次遍历大的list,以及还需要对临时表做一些额外的清理工作
// 和on_commits 息息相关
static List *on_commits = NIL;
在创建临时表时,如果存在ON COMMIT的动作 就会将其动作注册到这个变量中
/* What to do at commit time for temporary relations */
typedef enum OnCommitAction
{
ONCOMMIT_NOOP, /* No ON COMMIT clause (do nothing) */
ONCOMMIT_PRESERVE_ROWS, /* ON COMMIT PRESERVE ROWS (do nothing) */
ONCOMMIT_DELETE_ROWS, /* ON COMMIT DELETE ROWS */
ONCOMMIT_DROP /* ON COMMIT DROP */
} OnCommitAction;
// heap_create_with_catalog
* If there's a special on-commit action, remember it
*/
if (oncommit != ONCOMMIT_NOOP)
register_on_commit_action(relid, oncommit);
/*
* Register a newly-created relation's ON COMMIT action.
*/
void
register_on_commit_action(Oid relid, OnCommitAction action)
{
OnCommitItem *oc;
MemoryContext oldcxt;
/*
* We needn't bother registering the relation unless there is an ON COMMIT
* action we need to take.
*/
if (action == ONCOMMIT_NOOP || action == ONCOMMIT_PRESERVE_ROWS)
return;
oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
oc = (OnCommitItem *) palloc(sizeof(OnCommitItem));
oc->relid = relid;
oc->oncommit = action;
oc->creating_subid = GetCurrentSubTransactionId();
oc->deleting_subid = InvalidSubTransactionId;
/*
* We use lcons() here so that ON COMMIT actions are processed in reverse
* order of registration. That might not be essential but it seems
* reasonable.
*/
on_commits = lcons(oc, on_commits);
MemoryContextSwitchTo(oldcxt);
}
而在事务将要完成之前,需要处理这个list中的相关内容
/*
* Perform ON COMMIT actions.
*
* This is invoked just before actually committing, since it's possible
* to encounter errors.
*/
void
PreCommit_on_commit_actions(void)
{
ListCell *l;
List *oids_to_truncate = NIL;
List *oids_to_drop = NIL;
// 遍历
foreach(l, on_commits)
{
OnCommitItem *oc = (OnCommitItem *) lfirst(l);
/* Ignore entry if already dropped in this xact */
if (oc->deleting_subid != InvalidSubTransactionId)
continue;
switch (oc->oncommit)
{
case ONCOMMIT_NOOP:
case ONCOMMIT_PRESERVE_ROWS:
/* Do nothing (there shouldn't be such entries, actually) */
break;
case ONCOMMIT_DELETE_ROWS:
/*
* If this transaction hasn't accessed any temporary
* relations, we can skip truncating ON COMMIT DELETE ROWS
* tables, as they must still be empty.
*/
if ((MyXactFlags & XACT_FLAGS_ACCESSEDTEMPNAMESPACE))
oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid); // 生成新的list
break;
case ONCOMMIT_DROP:
oids_to_drop = lappend_oid(oids_to_drop, oc->relid);
break;
}
}
/*
* Truncate relations before dropping so that all dependencies between
* relations are removed after they are worked on. Doing it like this
* might be a waste as it is possible that a relation being truncated will
* be dropped anyway due to its parent being dropped, but this makes the
* code more robust because of not having to re-check that the relation
* exists at truncation time.
*/
if (oids_to_truncate != NIL)
heap_truncate(oids_to_truncate); // 新的list需要处理
if (oids_to_drop != NIL)
{
ObjectAddresses *targetObjects = new_object_addresses();
foreach(l, oids_to_drop)
{
ObjectAddress object;
object.classId = RelationRelationId;
object.objectId = lfirst_oid(l);
object.objectSubId = 0;
Assert(!object_address_present(&object, targetObjects));
add_exact_object_address(&object, targetObjects);
}
/*
* Object deletion might involve toast table access (to clean up
* toasted catalog entries), so ensure we have a valid snapshot.
*/
PushActiveSnapshot(GetTransactionSnapshot());
/*
* Since this is an automatic drop, rather than one directly initiated
* by the user, we pass the PERFORM_DELETION_INTERNAL flag.
*/
performMultipleDeletions(targetObjects, DROP_CASCADE,
PERFORM_DELETION_INTERNAL | PERFORM_DELETION_QUIETLY);
PopActiveSnapshot();
#ifdef USE_ASSERT_CHECKING
/*
* Note that table deletion will call remove_on_commit_action, so the
* entry should get marked as deleted.
*/
foreach(l, on_commits)
{
OnCommitItem *oc = (OnCommitItem *) lfirst(l);
if (oc->oncommit != ONCOMMIT_DROP)
continue;
Assert(oc->deleting_subid != InvalidSubTransactionId);
}
#endif
}
}
其实主要耗时应该还是heap_truncate
void
heap_truncate(List *relids)
{
List *relations = NIL;
ListCell *cell;
// 遍历
/* Open relations for processing, and grab exclusive access on each */
foreach(cell, relids)
{
Oid rid = lfirst_oid(cell);
Relation rel;
rel = table_open(rid, AccessExclusiveLock);
relations = lappend(relations, rel); // 生成新的list
}
/* Don't allow truncate on tables that are referenced by foreign keys */
heap_truncate_check_FKs(relations, true); // 其实这内部还有遍历
// 再次遍历
/* OK to do it */
foreach(cell, relations)
{
Relation rel = lfirst(cell);
/* Truncate the relation */
heap_truncate_one_rel(rel); // 估计大部分的耗时在此处
/* Close the relation, but keep exclusive lock on it until commit */
table_close(rel, NoLock);
}
}
heap_truncate_check_FKs中还存在遍历,调试可以看到:
加了些打印,看的更清楚一些
postgres@zxm-VMware-Virtual-Platform:~/code/16$ psql -o /dev/null
INFO: PreCommit_on_commit_actions time: 0.000 ms
psql (16.10)
Type "help" for help.
postgres=# \timing
Timing is on.
postgres=# select * from pg_database;
INFO: PreCommit_on_commit_actions time: 0.000 ms
Time: 1.724 ms
postgres=# select * from pg_database;
INFO: PreCommit_on_commit_actions time: 0.000 ms
Time: 0.636 ms
postgres=# select * from pg_database;
INFO: PreCommit_on_commit_actions time: 0.000 ms
Time: 0.634 ms
postgres=# CREATE TEMP TABLE a_gtt (n numeric) ON COMMIT DELETE ROWS;
INFO: heap_truncate time: 0.534 ms
INFO: PreCommit_on_commit_actions time: 0.618 ms
Time: 16.613 ms
postgres=# select * from a_gtt;
INFO: heap_truncate time: 0.756 ms
INFO: PreCommit_on_commit_actions time: 0.834 ms
Time: 2.341 ms
postgres=# select * from a_gtt;
INFO: heap_truncate time: 0.727 ms
INFO: PreCommit_on_commit_actions time: 0.789 ms
Time: 1.372 ms
postgres=# select * from a_gtt;
INFO: heap_truncate time: 0.838 ms
INFO: PreCommit_on_commit_actions time: 0.918 ms
Time: 1.746 ms
postgres=# DO $$
DECLARE
v_sql VARCHAR(100);
BEGIN
FOR i IN 1..3000 LOOP
v_sql := 'CREATE TEMP TABLE a_gtt'||i||'(n numeric) ON COMMIT DELETE ROWS';
EXECUTE v_sql;
END LOOP;
END;
$$ LANGUAGE plpgsql;
INFO: heap_truncate time: 1048.421 ms
INFO: PreCommit_on_commit_actions time: 1048.570 ms
Time: 3854.843 ms (00:03.855)
postgres=# select * from a_gtt;
INFO: PreCommit_on_commit_actions time: 0.053 ms
INFO: heap_truncate time: 1234.707 ms
INFO: PreCommit_on_commit_actions time: 1234.880 ms
Time: 1242.413 ms (00:01.242)
postgres=# select * from a_gtt;
INFO: PreCommit_on_commit_actions time: 0.039 ms
INFO: heap_truncate time: 1199.870 ms
INFO: PreCommit_on_commit_actions time: 1200.038 ms
Time: 1207.323 ms (00:01.207)
postgres=# select * from a_gtt;
INFO: PreCommit_on_commit_actions time: 0.038 ms
INFO: heap_truncate time: 1384.979 ms
INFO: PreCommit_on_commit_actions time: 1385.284 ms
Time: 1393.626 ms (00:01.394)
postgres=# select * from pg_database;
INFO: PreCommit_on_commit_actions time: 0.063 ms
INFO: PreCommit_on_commit_actions time: 0.092 ms
Time: 2.293 ms
postgres=# select * from pg_database;
INFO: PreCommit_on_commit_actions time: 0.111 ms
Time: 0.963 ms
postgres=# select * from pg_database;
INFO: PreCommit_on_commit_actions time: 0.103 ms
Time: 0.933 ms
postgres=#
没修改什么代码
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index e67ffc37b7d..d3d85cb1264 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -75,6 +75,7 @@
#include "utils/lsyscache.h"
#include "utils/syscache.h"
+#include "portability/instr_time.h"
/* Potentially set by pg_upgrade_support functions */
Oid binary_upgrade_next_heap_pg_class_oid = InvalidOid;
@@ -3070,6 +3071,9 @@ heap_truncate(List *relids)
{
List *relations = NIL;
ListCell *cell;
+ instr_time start;
+ instr_time duration;
+ INSTR_TIME_SET_CURRENT(start);
/* Open relations for processing, and grab exclusive access on each */
foreach(cell, relids)
@@ -3095,6 +3099,10 @@ heap_truncate(List *relids)
/* Close the relation, but keep exclusive lock on it until commit */
table_close(rel, NoLock);
}
+
+ INSTR_TIME_SET_CURRENT(duration);
+ INSTR_TIME_SUBTRACT(duration, start);
+ elog(INFO, "heap_truncate time: %.3f ms\n", INSTR_TIME_GET_MILLISEC(duration));
}
/*
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 016ae0a65f9..0172ec8bc5e 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -105,6 +105,8 @@
#include "utils/typcache.h"
#include "utils/usercontext.h"
+#include "portability/instr_time.h"
+
/*
* ON COMMIT action list
*/
@@ -17120,6 +17122,9 @@ PreCommit_on_commit_actions(void)
ListCell *l;
List *oids_to_truncate = NIL;
List *oids_to_drop = NIL;
+ instr_time start;
+ instr_time duration;
+ INSTR_TIME_SET_CURRENT(start);
foreach(l, on_commits)
{
@@ -17211,6 +17216,10 @@ PreCommit_on_commit_actions(void)
}
#endif
}
+
+ INSTR_TIME_SET_CURRENT(duration);
+ INSTR_TIME_SUBTRACT(duration, start);
+ elog(INFO, "PreCommit_on_commit_actions time: %.3f ms\n", INSTR_TIME_GET_MILLISEC(duration));
}
/*
来自tom lane的评价,因为场景比较单一,对于优化此处要增加代码复杂度,让他并不感兴趣
❤️ 转载文章请注明出处,谢谢!❤️