PostgreSQL: 给大表 Varchar 字段扩长的方法

昨天有个日志库有个需求,需要将日志表的一个 varchar 字段扩长,在数据库中给字段增长是一个非常普遍的需求,但在 PostgreSQL 里,这却是件蛋疼的事,因为 PostgreSQL 在给字段增长的场景,大多数据情况下需要重写表,这里可以参考我以前写的 blog: https://postgres.fun/20110215200654.html

考虑到直接修改原表字段类型会对所有表数据进行重写,这个耗时是非常长的,而且在 ALTER TABLE 过程中会锁表,表上所有的查询,插入等操作都不能进行,于是想到了个釜底抽薪的方法,可以解决这个问题,接下来往下看。

场景介绍

父表: tbl_tmp_log
子表: 1158 张日表,如 tbl_tmp_log_20111203 ,日表大小在 23 G左右
需求: 修改 tbl_tmp_log 表的 refer 字段类型,从 character varying(2000) 扩容到 character varing(5000)

环境信息

父表表定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
skytf=> /d tbl_tmp_log  
Table "public.tbl_tmp_log"
Column | Type | Modifiers
-----------+-----------------------------+-------------------------------------------------------------
xxx | bigint | not null default nextval('tbl_tmp_log_id_seq'::regclass)
xxx | character varying(50) |
xxx | character varying(50) |
xxx | timestamp without time zone | not null default now()
xxx | character varying(2000) |
xxx | character varying(10) |
xxx | integer |
refer | character varying(2000) |
xxx | character varying(50) |
xxx | character varying |
xxx | character varying(50) |
xxx | character varying(50) |
xxx | boolean |
xxx | integer |
xxx | character varying(50) |
xxx | integer |
Indexes:
"pk_tbl_tmp_log_id" PRIMARY KEY, btree (id)
"idx_tbl_tmp_log_ntime" btree (ntime), tablespace "tbs_skytf_idx"
Number of child tables: 1158 (Use d+ to list them.)
Tablespace: "tbs_skytf_idx"

子表信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
skytf=> /dt+  
List of relations
Schema | Name | Type | Owner | Size | Description
---------+-------------------------------+-------+---------+------------+-------------
skytf | tbl_tmp_log_20111108 | table | skytf | 26 GB |
skytf | tbl_tmp_log_20111109 | table | skytf | 26 GB |
skytf | tbl_tmp_log_20111110 | table | skytf | 24 GB |
skytf | tbl_tmp_log_20111111 | table | skytf | 25 GB |
skytf | tbl_tmp_log_20111112 | table | skytf | 28 GB |
skytf | tbl_tmp_log_20111113 | table | skytf | 26 GB |
skytf | tbl_tmp_log_20111114 | table | skytf | 23 GB |
skytf | tbl_tmp_log_20111115 | table | skytf | 23 GB |
skytf | tbl_tmp_log_20111116 | table | skytf | 23 GB |
skytf | tbl_tmp_log_20111117 | table | skytf | 23 GB |
skytf | tbl_tmp_log_20111118 | table | skytf | 24 GB |
skytf | tbl_tmp_log_20111119 | table | skytf | 25 GB |
skytf | tbl_tmp_log_20111207 | table | skytf | 0 bytes |
...省略

备注:为了便于显示,只列出少数表,实际上有 1158 张子表。

实施过程

清理历史数据
由于这是日志库,数据仓库会实时抽取数据,那么只要是同步到仓库里的数据,在生产环境下是可以清除的,所以接下来向仓库部门请求核对 2011-11-08 到 2011-12-01 的数据,核对无误后,这些数据都可以清除了。

数据清理后的表情况

1
2
3
4
5
6
7
8
9
10
skytf=> /dt+  
List of relations
Schema | Name | Type | Owner | Size | Description
---------+-------------------------------+-------+---------+------------+-------------
skytf | tbl_tmp_log_20111202 | table | skytf | 6805 MB |
skytf | tbl_tmp_log_20111203 | table | skytf | 0 bytes |
skytf | tbl_tmp_log_20111204 | table | skytf | 0 bytes |
skytf | tbl_tmp_log_20111205 | table | skytf | 0 bytes |
skytf | tbl_tmp_log_20111206 | table | skytf | 0 bytes |
skytf | tbl_tmp_log_20111207 | table | skytf | 0 bytes |

备份表 tbl_tmp_log_20111202 表结构
表 tbl_tmp_log_20111202 的表结构和权限信息可以通 pg_dump 导出 如

1
pg_dump -h 127.0.0.1 -p 1921 -E UTF8 -t "skytf.tbl_tmp_log_20111202" -s -v skytf > tbl_tmp_log_20111202.ddl

重命名日表 tbl_tmp_log_20111202 的表名和索引

1
2
3
alter table tbl_tmp_log_20111202 rename to tbl_tmp_log_20111202_bak;  
alter index tbl_tmp_log_20111202_pkey rename to tbl_tmp_log_20111202_pkey_bak;
alter index idx_tbl_tmp_log_20111202_ntime rename to idx_tbl_tmp_log_20111202_ntime_bak;

取消日表 tbl_tmp_log_20111202 和父表的继承关系

1
alter table tbl_tmp_log_20111202_bak no inherit public.tbl_tmp_log;

备注:这里取消表 tbl_tmp_log_20111202_bak 和父表的继承关系是因为接下来打算给父表 tbl_tmp_log 扩字段长度,如果不取消这张表的继承关系,那么这张表的数据是需要重写的。

创建同名表 tbl_tmp_log_20111202
在步骤 3.3,3.4 结束后,迅速执行步骤 3.2 导出的建表脚本, 脚本中包括表 tbl_tmp_log_20111202 的建表语句,索引创建语句和权限信息,所以执行后,和原表的信息是一样的。只不过是一张空表而已。

给父亲表字段扩容

1
2
skytf=> alter table public.tbl_tmp_log alter column refer type character varying(5000);ALTER TABLE  
Time: 83031.807 ms

备注:在步骤 3.5 完成后,接下来终于可以给字段扩容了,语句如上,耗时 83 秒,这个时间已经算很少了。这个字段扩容语句可以同时给了 1000 多张字表同样字段也扩容了。

将备份表数据插回新表

1
insert into tbl_tmp_log_20111202 select * from tbl_tmp_log_20111202_bak;

删除备份表

1
drop table tbl_tmp_log_20111202_bak;

备注:到了这步,就算完成了字段扩容操作。

总结

  1. 以上是实现给大表 varchar 字段扩长的一个方法,当然方法也有很多。
  2. 这种方法对生产库的影响非常小的,对生产库的影响在一分钟左右。
  3. 上面方法的一个重要步骤是恰当地拿掉了备份表和父表的继承关系,正因为这样,避免了重写 6 G的日表数据。
  4. 由于这次的场景是日志库,如果碰到了业务库的分区表需要扩字段长度,还得另外考虑方案。

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

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

相关推荐

发表回复

登录后才能评论