场景复现和简要分析
postgres@zxm-VMware-Virtual-Platform:~/code/16$ psql -o /dev/nullpsql (16.10)Type "help" for help.postgres=# \timingTiming is on.postgres=# select * from pg_database;Time: 0.491 mspostgres=# select * from pg_database;Time: 0.676 mspostgres=# select * from pg_database;Time: 0.573 mspostgres=# CREATE TEMP TABLE a_gtt (n numeric) ON COMMIT DELETE ROWS;Time: 5.771 mspostgres=# select * from a_gtt;Time: 1.721 mspostgres=# select * from a_gtt;Time: 1.383 mspostgres=# select * from a_gtt;Time: 1.703 mspostgres=# DO $$DECLAREv_sql VARCHAR(100);BEGINFOR i IN 1..3000 LOOPv_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 mspostgres=# select * from pg_database;Time: 0.981 mspostgres=# select * from pg_database;Time: 0.758 ms
truncate的操作// 和on_commits 息息相关static List *on_commits = NIL;
/* 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);
}/*
* 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_truncatevoid
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);
}
}
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));
}
/*postgres@zxm-VMware-Virtual-Platform:~/code/16$ psql -o /dev/nullINFO: PreCommit_on_commit_actions time: 0.000 mspsql (16.10)Type "help" for help.postgres=# \timingTiming is on.postgres=# select * from pg_database;INFO: PreCommit_on_commit_actions time: 0.000 msTime: 1.724 mspostgres=# select * from pg_database;INFO: PreCommit_on_commit_actions time: 0.000 msTime: 0.636 mspostgres=# select * from pg_database;INFO: PreCommit_on_commit_actions time: 0.000 msTime: 0.634 mspostgres=# CREATE TEMP TABLE a_gtt (n numeric) ON COMMIT DELETE ROWS;INFO: heap_truncate time: 0.534 msINFO: PreCommit_on_commit_actions time: 0.618 msTime: 16.613 mspostgres=# select * from a_gtt;INFO: heap_truncate time: 0.756 msINFO: PreCommit_on_commit_actions time: 0.834 msTime: 2.341 mspostgres=# select * from a_gtt;INFO: heap_truncate time: 0.727 msINFO: PreCommit_on_commit_actions time: 0.789 msTime: 1.372 mspostgres=# select * from a_gtt;INFO: heap_truncate time: 0.838 msINFO: PreCommit_on_commit_actions time: 0.918 msTime: 1.746 mspostgres=# DO $$DECLAREv_sql VARCHAR(100);BEGINFOR i IN 1..3000 LOOPv_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 msINFO: PreCommit_on_commit_actions time: 1048.570 msTime: 3854.843 ms (00:03.855)postgres=# select * from a_gtt;INFO: PreCommit_on_commit_actions time: 0.053 msINFO: heap_truncate time: 1234.707 msINFO: PreCommit_on_commit_actions time: 1234.880 msTime: 1242.413 ms (00:01.242)postgres=# select * from a_gtt;INFO: PreCommit_on_commit_actions time: 0.039 msINFO: heap_truncate time: 1199.870 msINFO: PreCommit_on_commit_actions time: 1200.038 msTime: 1207.323 ms (00:01.207)postgres=# select * from a_gtt;INFO: PreCommit_on_commit_actions time: 0.038 msINFO: heap_truncate time: 1384.979 msINFO: PreCommit_on_commit_actions time: 1385.284 msTime: 1393.626 ms (00:01.394)postgres=# select * from pg_database;INFO: PreCommit_on_commit_actions time: 0.063 msINFO: PreCommit_on_commit_actions time: 0.092 msTime: 2.293 mspostgres=# select * from pg_database;INFO: PreCommit_on_commit_actions time: 0.111 msTime: 0.963 mspostgres=# select * from pg_database;INFO: PreCommit_on_commit_actions time: 0.103 msTime: 0.933 mspostgres=#

声明
若文中存在错误或不当之处,敬请指出,以便我进行修正和完善。希望这篇文章能够帮助到各位。
文章转载请联系,谢谢合作~
其他文章推荐
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。




