您好 我们需要修改一个大型产品表的列,通常是normall ddl的参数 速度很快,但上面的ddl statmens需要大约10分钟。我想知道原因! 我只想扩展varchar列。以下是详细信息
--table size wapreader_log=> select pg_size_pretty(pg_relation_size('log_foot_mark')); pg_size_pretty ---------------- 5441 MB (1 row) --table ddl wapreader_log=> \d log_foot_mark Table "wapreader_log.log_foot_mark" Column | Type | Modifiers -------------+-----------------------------+----------- id | integer | not null create_time | timestamp without time zone | sky_id | integer | url | character varying(1000) | refer_url | character varying(1000) | source | character varying(64) | users | character varying(64) | userm | character varying(64) | usert | character varying(64) | ip | character varying(32) | module | character varying(64) | resource_id | character varying(100) | user_agent | character varying(128) | Indexes: "pk_log_footmark" PRIMARY KEY, btree (id) --alter column wapreader_log=> \timing Timing is on. wapreader_log=> ALTER TABLE wapreader_log.log_foot_mark ALTER column user_agent TYPE character varying(256); ALTER TABLE Time: 603504.835 ms
答案 0 :(得分:4)
ALTER ... TYPE需要完整的表重写,这就是为什么在大型表上完成可能需要一些时间。如果您不需要长度约束,则不要使用约束。一劳永逸地删除这些约束,并且由于过时的约束,您永远不会遇到新的问题。只需使用TEXT或VARCHAR。
答案 1 :(得分:3)
当您更改表时,PostgreSQL必须确保旧版本在某些情况下不会消失,以便在服务器提交和/或写入磁盘之前崩溃时允许回滚更改。出于这些原因,即使在看似微不足道的变化的情况下,它实际上做的是先在其他地方写出一个全新的表副本。当它完成后,它会转换到新的。请注意,当发生这种情况时,您还需要足够的磁盘空间来容纳这两个副本。
有些类型的DDL更改可以在不制作表的第二个副本的情况下进行,但这不是其中之一。例如,您可以快速添加一个默认为NULL的新列。但是添加一个非NULL默认值的新列需要改为创建一个新副本。
答案 2 :(得分:0)
不确定这是否更快,但可能是你必须测试它。 试试这个,直到PostgreSQL可以处理你想要的改变类型,而无需重写整个臭味表。
ALTER TABLE log_foot_mark RENAME refer_url TO refer_url_old;
ALTER TABLE log_foot_mark ADD COLUMN refer_url character varying(256);
然后使用表的索引主键或唯一键执行循环事务。我认为你必须通过Perl或某种语言来完成这项工作,你可以在每次循环迭代时提交它。
WHILE (end < MAX_RECORDS)LOOP
BEGIN TRANSACTION;
UPDATE log_foot_mark
SET refer_url = refer_url_old
WHERE id >= start AND id <= end;
COMMIT TRANSACTION;
END LOOP;
ALTER TABLE log_foot_mark DROP COLUMN refer_url_old;
请记住,循环逻辑需要在PL \ PGSQL之外的其他东西才能让它提交每个循环迭代。根本没有循环测试它,并以10k 20k 30k等交易大小循环,直到找到最佳位置。
答案 3 :(得分:0)
避免表重写的一种方法是使用SQL域(请参阅CREATE DOMAIN)而不是表中的varchars。然后,您可以在域上添加和删除约束。
请注意,这也不会立即生效,因为所有使用域的表都会检查约束有效性,但它比完整表重写便宜,并且不需要额外的磁盘空间。