Skip to content

Commit 1761f66

Browse files
author
xiongcc
committed
添加第55章:How to drop a column
1 parent 882775c commit 1761f66

File tree

1 file changed

+92
-0
lines changed

1 file changed

+92
-0
lines changed

How to drop a column.md

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# How to drop a column
2+
3+
>我每天都会发布一篇新的 PostgreSQL "howto" 文章。加入我的旅程吧 — 订阅、提供反馈、分享!
4+
5+
删除一列很简单:
6+
7+
```sql
8+
alter table t1 drop column c1;
9+
```
10+
11+
然而,需要注意在某些情况下可能出现的一些复杂情况。
12+
13+
## 风险1:应用代码未准备好
14+
15+
应用代码需要停止使用该列。这意味着代码需要先部署。
16+
17+
## 风险2:部分停机
18+
19+
在高负载下,如果没有设置较低的 `lock_timeout` 和重试机制,执行此类 `ALTER` 操作是一个糟糕的主意,因为该语句需要获取表的 `AccessExclusiveLock` 。如果尝试获取锁的时间较长 (例如,由于现有事务持有此表上的任何一个锁 — 可能是读取表中一行数据的事务,或者是为了防止事务 ID 回卷而处理该表的`autovacuum`),这将对当前所有查询造成阻塞,导致项目在高负载下出现部分停机。
20+
21+
解决方案:使用较低的 `lock_timeout` 和重试机制。以下是一个示例 (有关此问题的更多详细信息和更进阶的示例,请参照 [zero-downtime Postgres schema migrations need this: lock_timeout and retries](https://postgres.ai/blog/20210923-zero-downtime-postgres-schema-migrations-lock-timeout-and-retries)):
22+
23+
```sql
24+
do $do$
25+
declare
26+
lock_timeout constant text := '50ms';
27+
max_attempts constant int := 1000;
28+
ddl_completed boolean := false;
29+
begin
30+
31+
perform set_config('lock_timeout', lock_timeout, false);
32+
33+
for i in 1..max_attempts loop
34+
begin
35+
execute 'alter table t1 drop column c1';
36+
ddl_completed := true;
37+
exit;
38+
exception
39+
when lock_not_available then
40+
null;
41+
end;
42+
end loop;
43+
44+
if ddl_completed then
45+
raise info 'DDL successfully executed';
46+
else
47+
raise exception 'DDL execution failed';
48+
end if;
49+
end $do$;
50+
```
51+
52+
请注意,在此示例中,子事务是隐式使用的 (`BEGIN/EXCEPTION WHEN/END`块)。在高 `XID` 增长率的情况下 (例如,有大量写入事务) 和长时间运行的事务中,这可能成为一个问题 — 可能会在从库上触发`SubtransSLRU` 的争用 (参照:[PostgreSQL Subtransactions Considered Harmful](https://postgres.ai/blog/20210831-postgresql-subtransactions-considered-harmful))。在这种情况下,应在事务级别实现重试逻辑。
53+
54+
## 风险3:错误地预期数据会被删除
55+
56+
最后,在不同环境之间复制数据并删除敏感数据时,请记住 `ALTER TABLE ... DROP COLUMN ...` 并不安全,它不会真正删除数据。删除列 `c1` 后,元数据中仍然存在相关信息:
57+
58+
```sql
59+
nik=# select attname from pg_attribute where attrelid = 't1'::regclass::oid order by attnum;
60+
attname
61+
------------------------------
62+
tableoid
63+
cmax
64+
xmax
65+
cmin
66+
xmin
67+
ctid
68+
id
69+
........pg.dropped.2........
70+
(8 rows)
71+
```
72+
73+
超级用户可以轻松恢复该列:
74+
75+
```sql
76+
nik=# update pg_attribute
77+
set attname = 'c1', atttypid = 20, attisdropped = false
78+
where attname = '........pg.dropped.2........';
79+
UPDATE 1
80+
nik=# \d t1
81+
Table "public.t1"
82+
Column | Type | Collation | Nullable | Default
83+
--------+---------+-----------+----------+---------
84+
id | bigint | | |
85+
c1 | bigint | | |
86+
```
87+
88+
一些解决方法:
89+
90+
- 在删除列后使用 `VACUUM FULL` 重建表。在这种情况下,虽然尝试恢复会成功,但数据将不会存在。
91+
- 考虑使用受限用户和列级别的权限,而不是删除列。列和数据仍然存在,但用户无法读取。当然,如果严格要求删除数据,此方法不适用。
92+
- 在删除列之后转储/恢复。

0 commit comments

Comments
 (0)