- 专栏内容:postgresql内核源码分析
- 个人主页:我的主页
- 座右铭:天行健,君子以自强不息;地势坤,君子以厚德载物.
目录
前言
调用关系
概要流程
详细流程
创建对象列表空间
删除多个指定的数据库对象(performMultipleDeletions)
对于普通表,最终调用heap_drop_with_catalog删除表元数据和文件
在事务结束时的动作真正删除磁盘文件
结尾
前言
本文是基于postgresql 15的代码进行分析解读,演示是在centos8系统上进行。介绍了内核源码中关于DDL中drop table的处理流程。在删除表时,如何做到删除表物理文件与的事务原子性。
调用关系
生成执行计划后,调用standard_ProcessUtility进行执行,其中计划节点类型为T_DropStmt。
case T_DropStmt:
ExecDropStmt((DropStmt *) parsetree, isTopLevel);
/* no commands stashed for DROP */
commandCollected = true;
break;
在执行层调用ExecDropStmt进行实际执行。
概要流程
/*
* Dispatch function for DropStmt
*/
static void
ExecDropStmt(DropStmt *stmt, bool isTopLevel)
{
switch (stmt->removeType)
{
case OBJECT_INDEX:
if (stmt->concurrent)
PreventInTransactionBlock(isTopLevel,
"DROP INDEX CONCURRENTLY");
/* fall through */
case OBJECT_TABLE:
case OBJECT_SEQUENCE:
case OBJECT_VIEW:
case OBJECT_MATVIEW:
case OBJECT_FOREIGN_TABLE:
RemoveRelations(stmt);
break;
default:
RemoveObjects(stmt);
break;
}
}
可以看到删除数据库对象被分成了三种情况,表/索引等关系对象调用RemoveRelations。
ExecDropStmt调用,这里index/table/foreign table/view/materialized view/sequence都是调用这里。对于删除表的主要流程如下:
(1)对于concurrent删除索引判断,如果是的话,需要加一些较弱的锁;调用关系ExecDropStmt;
(2)判断删除的对应类型; 调用关系ExecDropStmt->RemoveRelations;
(3)建立删除对象的列表,并且锁定每张表;调用关系ExecDropStmt->RemoveRelations;
(4)依据列表检查依赖并生成报告,再删除对象;调用关系ExecDropStmt->RemoveRelations->performMultipleDeletions;
(5)释放对角列表空间,调用关系ExecDropStmt->RemoveRelations->free_object_addresses;
详细流程
-
创建对象列表空间
ObjectAddresses *
new_object_addresses(void)
{
ObjectAddresses *addrs;
addrs = palloc(sizeof(ObjectAddresses));
addrs->numrefs = 0;
addrs->maxrefs = 32;
addrs->refs = (ObjectAddress *)
palloc(addrs->maxrefs * sizeof(ObjectAddress));
addrs->extras = NULL; /* until/unless needed */
return addrs;
}
这里初始最大可以有32个对象,后面添加时,不够时会再进行扩展。
-
删除多个指定的数据库对象(performMultipleDeletions)
(1)如果指定了CASCADE,就会递归删除依赖的对象;
(2)如果指定为RESTRICT,则当遇到依赖时会报错;
删除的模式可以有以下几种:
PERFORM_DELETION_INTERNAL, 删除操作不是由用户直接发起;比如临时schema的删除;目前此模式下会事件触发器和权限检查不会被调用。
PERFORM_DELETION_CONCURRENTLY,仅用于index,进行concurrently delete;
PERFORM_DELETION_QUIETLY,会减少日志打印
PERFORM_DELETION_SKIP_ORIGINAL,仅仅删除指定对象的依赖对象,该指定对象不删除;
PERFORM_DELETION_SKIP_EXTENSIONS,用于删除临时对象,当对象是扩展的一部分时,只删对象而不删除扩展;
PERFORM_DELETION_CONCURRENT_LOCK,用于REINDEX CONCURRENTLY,在删除时加concurrent lock;
其中调用,deleteObjectsInList-> deleteOneObject进行真正的删除动作。
deleteOneObject
(1)对于concurrently模式,需要先关闭pg_depend系统表,在(2)删除后再次打开;
(2)调用doDeletion进行删除对象;
(3)删除pg_depend中的依赖数据行;
(4)删除comments,security labels, privilages;
(5)CommandCounterIncrement,变更可见性;
doDeletion根据class类型,调用对应的接口
-
对于普通表,最终调用heap_drop_with_catalog删除表元数据和文件
heap_drop_with_catalog的流程如下:
(1)从syscache中获取表信息,查看是否为分区表;是分区表的话,要锁定父表;
(2)打开表加8级表锁,也就是最高级表锁;下面开始删除操作;
(3)检查串行化冲突;
(4)如果是外部表,打开pg_foreign_table并删除表元数据,并从syscache中删除;
(5)分区表时,删除表对应的分区键;如果是默认分区,需要更新默认分区;
(6)删除表的文件,添加到待删除列表中,在提交时统一删除,子事务的汇集到父事务提交时统一删除;
(7)删除表的统计信息,也是在提交时统一删除;
(8)关闭表,此时不需要解锁;在事务提交时会自动释放;
(9)删除订阅关系映射,从pg_subscription_rel中查询删除元组;
(10)忘记该表的提交时的行为记录;
(11)刷新relcache数据字典缓存,确保没有被引用或引时被重建;
(12)移除继承关系,从pg_inherits删除对应元组;
(13)从pg_statistic中删除统计信息;
(14)删除属性,pg_attribute中的相关元组;
(15)最后从pg_class中删除表的相关元组;
(16)对于分区表,更新默认分区和父表的元数据缓存;对于它们是update操作;
-
在事务结束时的动作真正删除磁盘文件
CommitTransaction
->smgrDoPendingDeletes(true)
当然在abort时也会调用,但是传参为false不会删除;
结尾
作者邮箱:study@senllang.onaliyun.com
如有错误或者疏漏欢迎指出,互相学习。
注:未经同意,不得转载!