pg truncate

命令选项

TRUNCATE [ TABLE ] [ ONLY ] name [ * ] [, ... ][ RESTART IDENTITY | CONTINUE IDENTITY ] [ CASCADE | RESTRICT ]

1.ONLY:只truncate指定的表。当表有继承子表或有子分区时,默认会一起truncate;only可只truncate继承父表。分区父表不能指定only

--不能truncate only分区父表
=> truncate only parttable;
ERROR:  42809: cannot truncate only a partitioned table
HINT:  Do not specify the ONLY keyword, or use TRUNCATE ONLY on the partitions directly.
LOCATION:  ExecuteTruncate, tablecmds.c:1655
--truncate only继承父表,只清理父表
=> truncate table only parenttable;
TRUNCATE TABLE
=> select tableoid::regclass,count(*) from parenttable group by tableoid::regclass ;tableoid  | count 
------------+-------childtable |     1--直接truncate继承父表,子表也会被清理
=>  truncate table parenttable;
TRUNCATE TABLE
=> select tableoid::regclass,count(*) from parenttable group by tableoid::regclass ;tableoid | count 
----------+-------
(0 rows)

2.RESTART IDENTITY CONTINUE IDENTITY:列上的序列是否要重置,默认CONTINUE。

--bigserial 默认会创建列上的序列
=> create table tableserial (a  bigserial not null,b name);
CREATE TABLE
=> \d+ tableserial;Table "public.tableserial"Column |  Type  | Collation | Nullable |                Default                 | Storage | Stats target | Description 
--------+--------+-----------+----------+----------------------------------------+---------+--------------+-------------a      | bigint |           | not null | nextval('tableserial_a_seq'::regclass) | plain   |              | b      | name   |           |          |                                        | plain   |              | => insert into tableserial(b) select md5(random()::text) from generate_series(1,1000);
INSERT 0 1000 
--seq当前值为1000
=> select currval('tableserial_a_seq'::regclass);currval 
---------1000--直接truncate默认不会重置序列
=>  truncate table tableserial;
TRUNCATE TABLE
=> select currval('tableserial_a_seq'::regclass) cur,nextval('tableserial_a_seq'::regclass);cur  | nextval 
------+---------1000 |    1001--显示指定RESTART IDENTITY,重置序列
=>  truncate table tableserial RESTART IDENTITY;
TRUNCATE TABLE
--注意seq在nextval时重置了
=>  select currval('tableserial_a_seq'::regclass) cur,nextval('tableserial_a_seq'::regclass);cur  | nextval 
------+---------1001 |       1

3.CASCADE:清理表及其所有外键表的数据

--创建主表和外键表和数据
=>  create table pri_tab(id bigint primary key,name varchar(10));
CREATE TABLE=> insert into pri_tab values (1,'abc'),(2,'abc'),(3,'abc');
INSERT 0 3=>  create table frn_tab(id bigint,FOREIGN KEY (id) REFERENCES pri_tab(id));
CREATE TABLE=> insert into frn_tab values (1),(2);
INSERT 0 2=> select * from pri_tab;id | name 
----+------1 | abc2 | abc3 | abc
(3 rows)--外键表frn_tab依赖主表pri_tab的数据
=> select * from frn_tab;id 
----12
(2 rows)--有主表外键reference时,外键表必须跟cascade,否则无法清理
=> truncate table pri_tab ;
ERROR:  0A000: cannot truncate a table referenced in a foreign key constraint
DETAIL:  Table "frn_tab" references "pri_tab".
HINT:  Truncate table "frn_tab" at the same time, or use TRUNCATE ... CASCADE.
LOCATION:  heap_truncate_check_FKs, heap.c:3427--外键约束的表一起清空
=> truncate table pri_tab cascade;
NOTICE:  00000: truncate cascades to table "frn_tab"
LOCATION:  ExecuteTruncateGuts, tablecmds.c:1725
TRUNCATE TABLE
=> select * from pri_tab;id | name 
----+------
(0 rows)=>  select * from frn_tab;id 
----
(0 rows)

由于外键表依赖主表的数据,不能直接truncate主表,必须加cascade,此时外键表也跟随主表一起清空

4.RESTRICT
是否清理foreign key表。没什么用,default选项,加不加都是这样。清理附带的外键表应加CASCADE。

MVCC/transaction

pg官方文档有这么一段化

TRUNCATE is not MVCC-safe. After truncation, the table will appear empty to concurrent transactions, if they are using a snapshot taken before the truncation occurred.
TRUNCATE is transaction-safe with respect to the data in the tables: the truncation will be safely rolled back if the surrounding transaction does not commit.

transaction-safe意思是可以放在事务块里,可以回退
回滚truncate:

=> begin;
BEGIN
=> truncate t1;
TRUNCATE TABLE
=> rollback;
ROLLBACK
=> select count(*) from t1;count 
-------100

not MVCC-safe意思是一个会话在truncate前打了一个快照,快照期间如果发生truncate,这个快照是可以读到truncate清理后的结果的。这不符合MVCC。
不过这个问题不算太大,在会话场景下,因为truncate是8级锁,快照没有结束的话最低在表上有一个读共享锁,所以truncate不会执行。

This will only be an issue for a transaction that did not access the table in question before the DDL command started —any transaction that has done so would hold at least an ACCESS SHARE table lock, which would block the DDL command until that transaction completes

功能更新

在这里插入图片描述
truncate更新功能不多,只需要注意14的时候支持truncate foreign tables即可。truncate foreign tables前提是fdw得支持TRUNCATE API

Also it extends postgres_fdw so that it can issue TRUNCATE command to foreign servers, by adding new routine for that TRUNCATE API.

pg truncate和其他库的功能差异

在这里插入图片描述
在这里插入图片描述
truncate很快、8级锁等特性已经是人尽皆知的事情了,相对于其他数据库,pg还可以:选择是否重置序列RESTART IDENTITY CONTINUE IDENTITY)、回滚简单的授权

truncate做了什么

create table lzl(a int);
create index lzl_idx on lzl(a);
create sequence lzl_seq start with 1;
alter table lzl alter column a set default nextval('lzl_seq');
--select pg_relation_filepath('lzl');
--db路径
=> select oid from  pg_database where datname='lzldb';oid   
--------418679--刚创建时候各个rel的oid=relfilenode
=> select relname,oid,relfilenode,relkind from pg_class where  relname like 'lzl%';relname |  oid   | relfilenode | relkind 
---------+--------+-------------+---------lzl     | 428363 |      428363 | rlzl_idx | 428366 |      428366 | ilzl_seq | 428367 |      428367 | S
(3 rows)=> truncate table lzl;
TRUNCATE TABLE
=> select relname,oid,relfilenode,relkind from pg_class where  relname like 'lzl%';relname |  oid   | relfilenode | relkind 
---------+--------+-------------+---------lzl     | 428363 |      428370 | rlzl_idx | 428366 |      428371 | ilzl_seq | 428367 |      428367 | S
--truncate后,表和索引重建了,sequence却没有M=> truncate table lzl RESTART IDENTITY;
TRUNCATE TABLE
=> select relname,oid,relfilenode,relkind from pg_class where  relname like 'lzl%';relname |  oid   | relfilenode | relkind 
---------+--------+-------------+---------lzl     | 428363 |      428372 | rlzl_idx | 428366 |      428373 | ilzl_seq | 428367 |      428367 | S
--显示restart,sequence还是没有重建M=> alter sequence lzl_seq restart;
ALTER SEQUENCE
M=> select relname,oid,relfilenode,relkind from pg_class where  relname like 'lzl%';relname |  oid   | relfilenode | relkind 
---------+--------+-------------+---------lzl     | 428363 |      428372 | rlzl_idx | 428366 |      428373 | ilzl_seq | 428367 |      428374 | S
--显示restart sequence是会重建sequence的

truncate ···RESTART IDENTITY没有重建我们sequence,alter sequence lzl_seq restart重建了sequence。应该是RESTART IDENTITY没有理解对。在看下官方文档对RESTART IDENTITY的解释

Automatically restart sequences owned by columns of the truncated table(s).

sequence必须owned by表上的列,注意不是owner to。虽然\d可以看到表上的sequence,但是它可能不属于表

 \d+ lzl;Table "public.lzl"Column |  Type   | Collation | Nullable |           Default            | Storage | Stats target | Description 
--------+---------+-----------+----------+------------------------------+---------+--------------+-------------a      | integer |           |          | nextval('lzl_seq'::regclass) | plain   |              | 

owned by修改sequence的所属表

=> ALTER SEQUENCE lzl_seq OWNED BY lzl.a;
ALTER SEQUENCE--查看序列的所有者信息
SELECT s.relname AS seq, n.nspname AS sch, t.relname AS tab, a.attname AS col  
FROM pg_class s  
JOIN pg_depend d ON d.objid=s.oid AND d.classid='pg_class'::regclass AND d.refclassid='pg_class'::regclass  
JOIN pg_class t ON t.oid=d.refobjid  
JOIN pg_namespace n ON n.oid=t.relnamespace  
JOIN pg_attribute a ON a.attrelid=t.oid AND a.attnum=d.refobjsubid  
WHERE s.relkind='S' AND d.deptype='a';seq        |  sch   |     tab     | col 
-------------------+--------+-------------+-----tableserial_a_seq | public | tableserial | alzl_seq           | public | lzl         | a=> truncate table lzl RESTART IDENTITY;
TRUNCATE TABLE
M=> select relname,oid,relfilenode,relkind from pg_class where  relname like 'lzl%';relname |  oid   | relfilenode | relkind 
---------+--------+-------------+---------lzl     | 428363 |      428375 | rlzl_idx | 428366 |      428376 | ilzl_seq | 428367 |      428377 | S

sequence owned by表上的列时,truncate显示带RESTART IDENTITY就会restart这个sequence,也就重建了sequence。默认以serial/bigserial方式创建的序列是被表拥有的,随表drop而删除;那些不被表拥有的序列,drop不会删除
truncate重建特性汇总:

  • 直接truncate table会重建表和索引
  • truncate table+RESTART IDENTITY,会重建(也就是retart)属于这个表的sequence。只要不属于这个表的sequence,哪怕列上关联了seq的默认值,也不会重建这个seq

源码分析

truncate也是utility命令,很快就可以找到入口函数
src/backend/commands/tablecmds.c中的ExecuteTruncate为入口函数,注释其实已经说明truncate要获得exclusive lock,并检查权限和relation是否ok,递归检查所有需要truncate的表

voidExecuteTruncate(TruncateStmt *stmt){.../** Open, exclusive-lock, and check all the explicitly-specified relations*/foreach(cell, stmt->relations){...
LOCKMODE lockmode = AccessExclusiveLock;  //8级锁
...rel = table_open(myrelid, NoLock); //打开表void
ExecuteTruncate(TruncateStmt *stmt)
{
...foreach(cell, stmt->relations){
...LOCKMODE	lockmode = AccessExclusiveLock; //8级锁
.../* open the relation, we already hold a lock on it */rel = table_open(myrelid, NoLock); //打开表
...truncate_check_activity(rel);  //虽然已经有锁了,但是还是要验证是否在使用
...if (recurse) //递归执行{
...children = find_all_inheritors(myrelid, lockmode, NULL); //找到所有继承子表foreach(child, children){
...//上面只检查了父表,递归要检查子表truncate_check_rel(RelationGetRelid(rel), rel->rd_rel);truncate_check_activity(rel);rels = lappend(rels, rel);  //加入到待truncate的rel队列中relids = lappend_oid(relids, childrelid);
...}}//递归结束//发现truncate only分区父表,直接报错else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)ereport(ERROR,(errcode(ERRCODE_WRONG_OBJECT_TYPE),errmsg("cannot truncate only a partitioned table"),errhint("Do not specify the ONLY keyword, or use TRUNCATE ONLY on the partitions directly.")));}//主体函数	ExecuteTruncateGuts(rels, relids, relids_logged,stmt->behavior, stmt->restart_seqs);/* And close the rels */foreach(cell, rels){Relation	rel = (Relation) lfirst(cell);table_close(rel, NoLock);}
}

ExecuteTruncateGuts函数不仅被truncate命令调用,还被订阅端调用(发布订阅可以同步truncate)。

void
ExecuteTruncateGuts(List *explicit_rels,List *relids,List *relids_logged,DropBehavior behavior, bool restart_seqs)
{
...rels = list_copy(explicit_rels);if (behavior == DROP_CASCADE)  //如果指定了cascade选项,提取所有reference的relation{for (;;){
...newrelids = heap_truncate_find_FKs(relids); //找到fkif (newrelids == NIL)break;			/* nothing else to add */ //没有rel直接退出foreach(cell, newrelids){
...rel = table_open(relid, AccessExclusiveLock); //所有rel获得AccessExclusiveLockereport(NOTICE,(errmsg("truncate cascades to table \"%s\"",RelationGetRelationName(rel))));truncate_check_rel(relid, rel->rd_rel);  //检查是否是可以truncate的对象,得是存储数据的表truncate_check_perms(relid, rel->rd_rel);  //检查是否有权限truncate_check_activity(rel);  //检查是否在使用
...}}}...if (restart_seqs) //restart seq的处理{foreach(cell, rels){Relation	rel = (Relation) lfirst(cell);List	   *seqlist = getOwnedSequences(RelationGetRelid(rel));
...//只是做sequence的权限检查if (!pg_class_ownercheck(seq_relid, GetUserId()))aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_SEQUENCE,RelationGetRelationName(seq_rel));
...}}...//执行所有before truncate触发器foreach(cell, rels){ExecBSTruncateTriggers(estate, resultRelInfo);resultRelInfo++;}//正式开始truncateforeach(cell, rels){
...//如果是分区父表,啥都不做if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)continue;//如果是foreign table的处理if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE){...}...//如果是同一事务,因为可能会回退,直接执行heap_truncate_one_rel函数,不创建新的relfilenodeif (rel->rd_createSubid == mySubid ||rel->rd_newRelfilenodeSubid == mySubid){/* Immediate, non-rollbackable truncation is OK */heap_truncate_one_rel(rel);}else{
...//设置NewRelfilenodeRelationSetNewRelfilenode(rel, rel->rd_rel->relpersistence);heap_relid = RelationGetRelid(rel);//toast同理toast_relid = rel->rd_rel->reltoastrelid;if (OidIsValid(toast_relid)){Relation	toastrel = relation_open(toast_relid,AccessExclusiveLock);RelationSetNewRelfilenode(toastrel,toastrel->rd_rel->relpersistence);table_close(toastrel, NoLock);}...//重建索引reindex_relation(heap_relid, REINDEX_REL_PROCESS_TOAST,&reindex_params);}pgstat_count_truncate(rel); //更新pgstat的truncate计算}...//重置sequence foreach(cell, seq_relids){Oid			seq_relid = lfirst_oid(cell);ResetSequence(seq_relid);}//写walif (list_length(relids_logged) > 0){...}//触发AFTER TRUNCATE triggersresultRelInfo = resultRelInfos;foreach(cell, rels){ExecASTruncateTriggers(estate, resultRelInfo);resultRelInfo++;}
...
}	

ExecuteTruncateGuts函数根据truncate选项进行处理,处理过程如下:

  1. 根据cascade选项找到所有reference的外键表
  2. 触发before truncate触发器
  3. 执行truncate
  • 如果是同一事务,不立即生成NewRelfilenode,直接调用函数heap_truncate_one_rel进行truncate
  • 如果不是同一事务,调用RelationSetNewRelfilenode新建NewRelfilenode
  1. reindex_relation函数重建索引
  2. 根据restart identity重置sequence
  3. 写wal日志
  4. 触发after truncate触发器

后面大概追了下函数,套娃比较多
RelationSetNewRelfilenode
table_relation_set_new_filenode
relation_set_new_filenode在这里插入代码片
heapam_relation_set_new_filenode
RelationCreateStorage
然后到src/backend/storage/smgr/smgr.c中的smgrcreatesmgr_create。后面就没看太懂了(一到函数指针就有点追不到的感觉,先这样吧~)···
对于smgr.c有这样的注释:

public interface routines to storage manager switch
All file system operations in POSTGRES dispatch through these routines.

任何文件系统操作都会经过smgr(storage manager);到这里就是文件系统操作了。

reference

https://www.postgresql.org/docs/15/sql-truncate.html
https://www.postgresql.org/docs/current/mvcc-caveats.html
https://pgpedia.info/t/truncate.html
https://www.orafaq.com/wiki/SQL_FAQ
https://learnsql.com/blog/difference-between-truncate-delete-and-drop-table-in-sql/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/175785.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

电机伺服驱动学习笔记(7)待编辑

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、pandas是什么?二、使用步骤1.引入库2.读入数据 总结 前言 提示:这里可以添加本文要记录的大概内容: 例如:…

vue2使用ts vue-class-component

目前,对于Vue3来说,TypeScript的支持已经相当成熟,但公司的老项目一直处于迭代和维护无法从v2重构成v3,并且重构的成本也是很大的一个问题,所以记录一下vue2如何去搭配TypeScript。 目录 一、脚手架创建项目 二、vu…

西南科技大学C++程序设计实验一(C++基础知识)

目录 一、实验目的 二、实验任务 三、预习内容(复习书中前3章内容,说明C++相对于C的扩展有哪些?) 四、问题思考与讨论 一、实验目的 1.熟悉编程环境 2.掌握程序调试方法。 3.熟悉枚举类型、结构体类型等自定义数据类型的使用 4.熟悉函数的定义、说明与使用 5.熟悉引用…

在vue项目中使用vue-video-player播放m3u8视频文件

1.简介 Vue Video Player 是一个基于Vue.js 的视频播放器库,官方API Video.js API docs m3u8是UTF-8编码格式。M3U8是指UTF-8编码的M3U文件,而M3U文件是记录了一个索引纯文本文件,打开它时播放软件并不是播放它,而是根据它的索引…

计算机网络:快速了解网络框架

文章目录 前言一、什么是Internet?1.从具体构成角度什么是协议? 2.从服务角度3小结 二、网络边缘1.采用网络设施面向连接服务(TCP)2.采用基础设施的无连接服务(UDP) 三、网络的核心1.电路交换2.分组交换3.分…

时间数据与字符串间相互转换

1. 字符串转成时间 使用datetime.strptime from datetime import datetime start_date "2023-11-28" start_datetime datetime.strptime(start_date, "%Y-%m-%d") print(start_date,start_date) print(start_datetime,start_datetime) print(type(star…

Android : 获取、添加、手机联系人-ContentResolver简单应用

示例图: MainActivity.java package com.example.mygetdata;import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat;import android.Mani…

什么是关系型数据库?

什么是关系型数据库? 关系型数据库(RDBMS)是建立在关系模型基础上的数据库系统。关系模型是一种数据模型,它表示数据之间的联系,包括一对一、一对多和多对多的关系。在关系型数据库中,数据以表格的形式存储…

Vue项目上线后关闭chroma的vue-devtools调试工具

在项目的main文件中添加如下代码: // 通过设置环境变量 注释生产环境的debug devtools 生产提示 const isDebugMode process.env.NODE_ENV ! production; Vue.config.debug isDebugMode; Vue.config.devtools isDebugMode; Vue.config.productionTip isDebugM…

2024 年应该使用 Bun、Node.js 还是 Deno

2024 年应该使用 Bun、Node.js 还是 Deno 到 2024 年,构建基于 JavaScript 的现代 API 相对简单。我们可以使用Express.js等库并在几分钟内启动可用的 API。但是,现在最具挑战性的部分是选择正确的 JavaScript 引擎。 目前主流的三个运行时是&#xff…

Logstash 部署

目录 1.安装配置Logstash (1)安装 (2)测试文件 (3)配置 服务器 安装软件主机名IP地址系统版本配置LogstashElk10.3.145.14centos7.5.18042核4G 软件版本:logstash-7.13.2.tar.gz 1.安装配…

Android 单元测试初体验(二)-断言

[TOC](Android 单元测试初体验(二)-断言) 前言 当初在学校学安卓的时候,老师敢教学进度,翻到单元测试这一章节的时候提了两句,没有把单元测试当重点讲,只是说我们工作中几乎不会用到,果真在之前的几年工作当中我真的没…

95.STL-遍历算法 for_each

算法概述: 算法主要是由头文件 <algorithm> <functional> <numeric> 组成。 <algorithm> 是所有STL头文件中最大的一个&#xff0c;范围涉及到比较、 交换、查找、遍历操作、复制、修改等等 <numeric> 体积很小&#xff0c;只包括几个在序列上面…

第1章 爬虫基础

目录 1. HTTP 基本原理1.1 URI 和 URL1.2 HTTP 和 HTTPS1.3 请求1.3.1 请求方法1.3.2 请求的网址1.3.3 请求头1.3.4 请求体 1.4 响应1.4.1 响应状态码1.4.2 响应头1.4.3 响应体 2. Web 网页基础2.1 网页的组成2.1.1 HTML2.1.2 CSS2.1.3 JavaScript 2.2 网页的结构2.3 节点树及节…

kubenates的傻瓜式部署教程(K8S部署教程)

推荐配置 主节点2C4G&#xff0c;两个从节点2C2G即可 操作系统我用的centos 一、Dokcer环境安装&#xff08;阿里云环境&#xff09; 1.1 下载docker实例 sudo wget -O /etc/yum.repos.d/docker-ce.repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo…

【LeetCode】69. x的平方根

69. x的平方根 难度&#xff1a;简单 题目 给你一个非负整数 x &#xff0c;计算并返回 x 的 算术平方根 。 由于返回类型是整数&#xff0c;结果只保留 整数部分 &#xff0c;小数部分将被 舍去 。 **注意&#xff1a;**不允许使用任何内置指数函数和算符&#xff0c;例如…

人工智能 -- 技术概览

1、我们身处人工智能的时代 人们从早期做web开发&#xff0c;到移动端的开发&#xff1b;之后随着数据量的增大&#xff0c;人们开始研究高并发的问题&#xff1b;当数据量不断的增大&#xff0c;而人们希望数据不被浪费时&#xff0c;产生了大数据的技术&#xff0c;包括&…

正则表达式 通配符 awk文本处理工具

目录 什么是正则表达式 概念 正则表达式的结构 正则表达式的组成 元字符 元字符点&#xff08;.&#xff09; 代表字符. 点值表示点需要转义 \ r..t 代表r到t之间任意两个字符 过滤出小写 过滤出非小写 space空格 [[:space:]] 表示次数 位置锚定 例&#xff1a…

第三节HarmonyOS DevEco Studio了解基本工程目录

一、工程级目录 工程的目录结构如下。 目录详情如下&#xff1a; AppScope&#xff1a;存放应用全局所需要的资源文件。Entry&#xff1a;应用的主模块&#xff0c;存放HarmonyOS应用的代码、资源等。oh_modules&#xff1a;工程的依赖包&#xff0c;存放工程依赖的源文件。b…

Umi-OCR图片批量识别文字工具

OCR software, free and offline. 开源、免费的离线OCR软件。支持截屏/粘贴/批量导入图片&#xff0c;段落排版/排除水印&#xff0c;扫描/生成二维码。内置多国语言库。 项目地址&#xff1a;https://github.com/hiroi-sora/Umi-OCR