本文来源:www.codacy.com/blog/how-to…sql
在Postgres中更新大型表并不像看起来那样简单。若是您的表包含数亿行,您将发现很难及时进行简单的操做,例如添加列或更改列类型。数据库
在不停机的状况下进行这类操做是一个更大的挑战。在这篇博客文章中,我将尝试概述一些策略,以在管理大型数据集的同时最大程度地减小表不可用性。安全
当您更新列中的值时,Postgres将在磁盘中写入一个新行,弃用旧行,而后继续更新全部索引。此过程等同于INSERT
加上每一行后再DELETE
,这会占用大量资源。bash
除此以外,须要更新大表时还应了解的事项列表:并发
考虑到这一点,让咱们看一些能够用来有效更新表中大量数据行的策略:ide
若是您可使用例如顺序ID对数据进行细分,则能够批量更新行。因为您只须要保持较短期的锁定,所以能够最大化表的可用性。若是添加新列,则能够将其临时设置为可为空,而后开始逐渐用新值填充它。post
这种方法的主要问题是性能,这是一个很是缓慢的过程,由于就地更新成本很高。在迁移期间,它可能还须要更复杂的应用程序逻辑。性能
更新大表的最快方法是建立一个新表。优化
若是能够安全地删除现有表,而且有足够的磁盘空间,则执行更新的最简单方法是将数据插入到新表中,而后对其进行重命名。如下是此操做的基本执行脚本:spa
create table user_info_copy (LIKE user_info INCLUDING INDEXES INCLUDING COMMENTS);
INSERT INTO user_info_copy
SELECT user_no, idcard_no, real_name, bankcard_no, bind_mobile
, false, bind_status, user_identity, create_time, creator
, edit_time, editor, is_del, VERSION, customer_id
, id_card_type, source_id, platform_no, one_passport_no, bank_code
FROM user_info;
drop TABLE user_info;
alter table user_info_copy rename to user_info;
复制代码
若是因为不想从新建立视图或因为其余限制而不能删除原始表,则可使用临时表保存新值,截断旧表并在那里重写数据。当您有未决的写请求时,此方法也有一些优势,如咱们将在下一部分中看到的。
若是您的表能够容纳在内存中,则应在此事务期间增长temp_buffers
属性。使用RAM代替磁盘来存储临时表将明显提升性能:
SET temp_buffers = 3000MB; ----相应地更改此值
# 建立临时表
CREATE TABLE temp_user_info(
user_no BIGINT,
PRIMARY KEY( user_no )
);
# 若是须要提速能够从表中删除索引
# 复制数据到临时表中
insert into temp_user_info select user_no from user_info;
# 改变表结构,好比须要添加新列
TRUNCATE user_no;
# 执行插入列字段语句
# 再把数据反写到user_info表
复制代码
即便进行了上述优化,从新建立表仍然是缓慢的操做。若是您正在实时数据库中运行查询,则可能须要处理并发写入请求。
最简单的方法是在事务期间在表上强制使用SHARE LOCK
, 语句以下
LOCK TABLE user_info IN SHARE MODE;
复制代码
若是花费太长时间,全部写请求将一直等到锁释放或超时为止。若是未删除原始表,则一旦事务结束,将执行未超时的请求。请注意,即便使用相同的名称建立新表,请求仍将失败,由于它们使用表OID。
根据写请求的性质,您还能够建立自定义规则来存储对表所作的更改。例如,您能够设置一个规则,以在开始数据迁移以前记录已删除的行:
CREATE RULE deleted_rule AS ON DELETE
TO tbl
DO INSERT INTO tbl_deletes VALUES
(
OLD.id
);
复制代码
迁移结束时,您只需从tbl_deletes中读取ID,而后在新表上将其删除。可使用相似的方法来处理其余类型的请求。
一旦达到必定大小,曾经瞬时的操做可能须要几个小时来准备和执行。我的实验结论: