扩展varchar列非常慢,为什么?

时间:2011-02-15 11:49:26

标签: postgresql

您好     我们需要修改一个大型产品表的列,通常是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    

4 个答案:

答案 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。然后,您可以在域上添加和删除约束。

请注意,这也不会立即生效,因为所有使用域的表都会检查约束有效性,但它比完整表重写便宜,并且不需要额外的磁盘空间。