PostgreSQL: 恢复删除的列

今天看了德哥的一篇日志,讲的是表上被 drop 的列还能恢复,很受启发,原文链接 http://blog.163.com/digoal@126/blog/static/163877040201112251058216/ ,根据德哥的BLOG,下面也来学习下。

PostgreSQL 在执行 Alter table table_name drop column 命令后,并没有在物理上删除这个列,而只是改下这个列的标志,可以通过修改 pg_attribute 属性进行恢复,下面是详细步骤:

先看 pg_attribute 几个关键的列

Name Type References Description
attrelid oid pg_class.oid The table this column belongs to
attname name The column name
atttypid oid pg_type.oid The data type of this column
attnum int2 The number of the column. Ordinary columns are numbered from 1 up. System columns, such as oid, have (arbitrary) negative numbers
attisdropped bool This column has been dropped and is no longer valid. A dropped column is still physically present in the table, but is ignored by the parser and so cannot be accessed via SQL

测试场景一: 删除列后恢复

创建测试表并插入数据

1
2
3
4
skytf=> create table test_26 (id integer,name varchar(32),machine varchar(32));  
CREATE TABLE
skytf=> insert into test_26 select generate_series(1,10000),'francs','sky';
INSERT 0 10000

查看表结构

1
2
3
4
5
6
7
skytf=> /d test_26  
Table "skytf.test_26"
Column | Type | Modifiers
---------+-----------------------+-----------
id | integer |
name | character varying(32) |
machine | character varying(32) |

查看一条测试数据

1
2
3
4
5
skytf=> select * from test_26 limit 1;  
id | name | machine
----+--------+---------
1 | francs | sky
(1 row)

查看表信息

1
2
3
4
5
6
7
8
9
10
11
skytf=> select oid,relname from pg_class where relname='test_26';  
oid | relname
----------+---------
14280890 | test_26
(1 row)

skytf=> select attrelid,attname,atttypid,attisdropped ,attnum from pg_attribute where attrelid=14280890 and attnum=2;
attrelid | attname | atttypid | attisdropped | attnum
----------+---------+----------+--------------+--------
14280890 | name | 1043 | f | 2
(1 row)

删除列

1
2
skytf=> alter table test_26 drop column name;  
ALTER TABLE

再次查看 pg_attribute 列信息

1
2
3
4
5
skytf=> select attrelid,attname,atttypid,attisdropped ,attnum from pg_attribute where attrelid=14280890 and attnum=2;  
attrelid | attname | atttypid | attisdropped | attnum
----------+------------------------------+----------+--------------+--------
14280890 | ........pg.dropped.2........ | 0 | t | 2
(1 row)

查看表结构

1
2
3
4
5
6
skytf=> /d test_26  
Table "skytf.test_26"
Column | Type | Modifiers
---------+-----------------------+-----------
id | integer |
machine | character varying(32) |

恢复删除的列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
skytf=> /c skytf postgres  
You are now connected to database "skytf" as user "postgres".

skytf=# update pg_attribute set attname='name' ,atttypid=1043,attisdropped='f' where attrelid=14280890 and attnum=2;
UPDATE 1

skytf=# /d skytf.test_26;
Table "skytf.test_26"
Column | Type | Modifiers
---------+-----------------------+-----------
id | integer |
name | character varying(32) |
machine | character varying(32) |

skytf=# select * From skytf.test_26 limit 5;
id | name | machine
----+--------+---------
1 | francs | sky
2 | francs | sky
3 | francs | sky
4 | francs | sky
5 | francs | sky
(5 rows)

备注:通过修改系统表 pg_attribute 列 的 attname, atttypid, attisdropped 值, 列上数据可以恢复。

测试场景二: 删除列之后插入数据

查看表信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
skytf=> select max(id) from test_26;  
max
-------
10000
(1 row)

skytf=> /d test_26;
Table "skytf.test_26"
Column | Type | Modifiers
---------+-----------------------+-----------
id | integer |
name | character varying(32) |
machine | character varying(32) |

skytf=> select * from test_26 limit 1;
id | name | machine
----+--------+---------
1 | francs | sky
(1 row)

删除列

1
2
skytf=> alter table test_26 drop column name;  
ALTER TABLE

查看数据

1
2
3
4
5
skytf=> select * from test_26 limit 1;  
id | machine
----+---------
1 | sky
(1 row)

插入一条数据

1
2
skytf=> insert into test_26(id,machine) values (10001,'sky10001');  
INSERT 0 1

恢复删除的列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
skytf=> /c skytf postgres  
You are now connected to database "skytf" as user "postgres".

skytf=# update pg_attribute set attname='name' ,atttypid=1043,attisdropped='f' where attrelid=14280890 and attnum=2;
UPDATE 1

skytf=# /c skytf skytf
You are now connected to database "skytf" as user "skytf".

skytf=> select * from test_26 where id=10001;
id | name | machine
-------+------+----------
10001 | | sky10001
(1 row)

备注:对于删除的列恢复后,后面接着的 insert 记录的该列为空。测试场景三,删除列后,后面接着有数据 update 的场景

测试场景三: 删除列之后更新数据

查看表信息

1
2
3
4
5
6
7
8
9
10
11
12
13
skytf=> /d test_26  
Table "skytf.test_26"
Column | Type | Modifiers
---------+-----------------------+-----------
id | integer |
name | character varying(32) |
machine | character varying(32) |

skytf=> select * From test_26 where id=1;
id | name | machine
----+--------+---------
1 | francs | sky
(1 row)

删除列

1
2
skytf=> alter table test_26 drop column name;  
ALTER TABLE

修改一条记录

1
2
3
4
5
6
7
8
skytf=> update test_26 set machine ='sky_001' where id=1;  
UPDATE 1

skytf=> select * from test_26 where id=1;
id | machine
----+---------
1 | sky_001
(1 row)

恢复删除的列

1
2
3
4
5
6
7
8
9
10
11
skytf=> /c skytf postgres  
You are now connected to database "skytf" as user "postgres".

skytf=# update pg_attribute set attname='name' ,atttypid=1043,attisdropped='f' where attrelid=14280890 and attnum=2;
UPDATE 1

skytf=# select * from skytf.test_26 where id=1;
id | name | machine
----+------+---------
1 | | sky_001
(1 row)

备注: 在删除列后,对于后面有 update 操作的行,则在列恢复后,被update的行的此列数据丢失。

测试场景四: 删除列之后 VACUUM FULL 表

查看表信息

1
2
3
4
5
6
7
skytf=> /d test_26  
Table "skytf.test_26"
Column | Type | Modifiers
---------+-----------------------+-----------
id | integer |
name | character varying(32) |
machine | character varying(32) |

查看表大小

1
2
3
4
5
skytf=> select pg_size_pretty(pg_relation_size('test_26'));  
pg_size_pretty
----------------
440 kB
(1 row)

删除列

1
2
skytf=> alter table test_26 drop column name;  
ALTER TABLE

再次查看表大小,表大小没变化

1
2
3
4
5
6
7
8
9
skytf=> select pg_size_pretty(pg_relation_size('test_26'));  
pg_size_pretty
----------------
440 kB
(1 row)
vacuum full
skytf=> vacuum full verbose test_26;
INFO: vacuuming "skytf.test_26"
VACUUM

再次查看表大小,发现变小了

1
2
3
4
5
skytf=> select pg_size_pretty(pg_relation_size('test_26'));  
pg_size_pretty
----------------
360 kB
(1 row)

列恢复

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
skytf=> /c skytf postgres  
You are now connected to database "skytf" as user "postgres".

skytf=# select attrelid,attname,atttypid,attisdropped ,attnum from pg_attribute where attrelid=14280890 and attnum=2;
attrelid | attname | atttypid | attisdropped | attnum
----------+------------------------------+----------+--------------+--------
14280890 | ........pg.dropped.2........ | 0 | t | 2
(1 row)

skytf=# update pg_attribute set attname='name' ,atttypid=1043,attisdropped='f' where attrelid=14280890 and attnum=2;
UPDATE 1

skytf=# select attrelid,attname,atttypid,attisdropped ,attnum from pg_attribute where attrelid=14280890 and attnum=2;
attrelid | attname | atttypid | attisdropped | attnum
----------+---------+----------+--------------+--------
14280890 | name | 1043 | f | 2
(1 row)

skytf=# /c skytf skytf
You are now connected to database "skytf" as user "skytf".

再次查看数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
skytf=> /d test_26;  
Table "skytf.test_26"
Column | Type | Modifiers
---------+-----------------------+-----------
id | integer |
name | character varying(32) |
machine | character varying(32) |
skytf=> select * From test_26 limit 5;
id | name | machine
----+------+---------
2 | | sky
3 | | sky
4 | | sky
5 | | sky
6 | | sky
(5 rows)

备注,在列被删除后,如果后面执行了 vacuum full 操作,被删除的列名能恢复,但列的数据丢失。

测试场景五: 删除列之后 VACUUM 表

环境准备

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
skytf=> update test_26 set name='francs';  
UPDATE 10001
skytf=> select * From test_26 limit 5;
id | name | machine
----+--------+---------
2 | francs | sky
3 | francs | sky
4 | francs | sky
5 | francs | sky
6 | francs | sky
(5 rows)

查看表大小
skytf=> select pg_size_pretty(pg_relation_size('test_26'));
pg_size_pretty
----------------
792 kB
(1 row)

删除列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
skytf=> alter table test_26 drop column name;  
ALTER TABLE

skytf=> select pg_size_pretty(pg_relation_size('test_26'));
pg_size_pretty
----------------
792 kB
(1 row)
VACUUM

skytf=> vacuum verbose test_26;
INFO: vacuuming "skytf.test_26"
INFO: "test_26": removed 0 row versions in 45 pages
INFO: "test_26": found 0 removable, 10001 nonremovable row versions in 99 out of 99 pages
DETAIL: 0 dead row versions cannot be removed yet.
There were 0 unused item pointers.
0 pages are entirely empty.
CPU 0.00s/0.00u sec elapsed 0.00 sec.
VACUUM

再次查看表大小

1
2
3
4
5
skytf=> select pg_size_pretty(pg_relation_size('test_26'));  
pg_size_pretty
----------------
792 kB
(1 row)

恢复列

1
2
3
4
5
skytf=> /c skytf postgres  
You are now connected to database "skytf" as user "postgres".

skytf=# update pg_attribute set attname='name' ,atttypid=1043,attisdropped='f' where attrelid=14280890 and attnum=2;
UPDATE 1

查看表数据

1
2
3
4
5
6
skytf=# select * from skytf.test_26 limit 2;  
id | name | machine
----+--------+---------
2 | francs | sky
3 | francs | sky
(2 rows)

备注:删除列后,如果些表被 vacuum, 被 drop 的列依然能完全恢复。

总结

  1. 在删除列后,可以通过修改系统表pg_attribute 的 attname, atttypid, attisdropped值, 对删除的列进行恢复。
  2. 在删除列后,对于后面有 insert 操作的行,则在列恢复后,后面接着的 insert 记录的该列为空。
  3. 在删除列后,对于后面有 update 操作的行,则在列恢复后,后面接着的 update 的行的此列的数据为空。
  4. 在删除列后,如果后面执行了 vacuum full 操作,被删除的列名能恢复,但数据丢失。
  5. 在删除列后,如果此表被 vacuum, 被 drop 的列依然能完全恢复。

原创文章,作者:506227337,如若转载,请注明出处:https://blog.ytso.com/236382.html

(0)
上一篇 2022年1月24日
下一篇 2022年1月24日

相关推荐

发表回复

登录后才能评论