从日期范围之间的表中删除数百万的记录

时间:2017-07-14 12:00:37

标签: sql postgresql postgresql-performance

我有一个名为camera_activities的表,其中有超过数百万条记录,表结构就像

CREATE TABLE camera_activities
(
  id serial NOT NULL,
  camera_id integer NOT NULL,
  access_token_id integer,
  action text NOT NULL,
  done_at timestamp with time zone NOT NULL,
  ip inet,
  extra json,
  camera_exid text,
  name text
)
WITH (
  OIDS=FALSE
);
ALTER TABLE camera_activities
  OWNER TO 8hhjhjgghg7;

-- Index: camera_activities_camera_id_done_at_index

-- DROP INDEX camera_activities_camera_id_done_at_index;

CREATE UNIQUE INDEX camera_activities_camera_id_done_at_index
  ON camera_activities
  USING btree
  (camera_id, done_at);

并且单个记录就像

record

这是问题,

我想在2016年之前删除所有记录,它们将会有很多记录,范围介于2014年之间,依此类推,我们开始在2014年添加数据。

我尝试过一个简单的查询,但效果很好,例如,如果我在两个日期之间删除

delete from camera_activities where done_at>'2017-07-12 10:55:37+00' and done_at<='2017-07-13 09:23:00+00

这可行,但它耗费了大量时间,有超过数百万条记录,有没有快速的方法来完成这项工作?

注意:如果我可以将日期范围增加到一个月或一个月以上,则查询将继续运行,并且永远不会返回任何结果。

任何帮助或指导都会有所帮助,

1 个答案:

答案 0 :(得分:2)

执行大规模DELETE操作有两种基本方法。

1)创建另一个表,删除旧表并重命名新表,最后ANALYZE新表:

begin;
create table camera_activities_new (like camera_activities including all);

insert into camera_activities_new
select * from camera_activities
where done_at >= ''2016-01-01'::date;

alter sequence camera_activities_id_seq owned by camera_activities_new;
drop table camera_activities;
alter table camera_activities_new rename to camera_activities;
alter index camera_activities_new_camera_id_done_at_idx rename to camera_activities_camera_id_done_at_idx;
commit;

analyze camera_activities;

此方法可确保生成的表格处于最佳状态(无膨胀)。但是,您的系统负载繁重并且涉及到表格可能不太方便。在这种情况下,“平滑删除”可能看起来更好。

2)“平滑”删除:每次只删除相对少量的行,使用更积极的autovacuum设置并控制膨胀。

示例,显示如何将删除拆分为多个独立事务(在bash中;依赖于$PGDATABASE$PGHOST$PGUSER$PGPASSWORD环境变量):

while true; do
  res=$(psql -c "delete from camera_activities where id in (select id camera_activities where done_at < '2016-01-01'::date limit 500);" \
    | grep DELETE | awk {'print $2'} )
  if [[ $res = '0' ]]; then break; fi;
  sleep 0.3; # control speed here; check bloating level
done

- 当没有行要删除时,这将自动停止。

(camera_id, done_at)上的索引应该加快子选择,进行位图索引扫描 - 使用EXPLAIN进行检查。但是在done_at上有一个单独的索引可能是值得的,在这种情况下它可以是btreebrin(有损但是更小):

create i_camera_activities_done_at on camera_activities using brin(done_at);

“更具攻击性”(默认)autovacuum设置的示例:

log_autovacuum_min_duration = 0
autovacuum_vacuum_scale_factor = 0.01
autovacuum_analyze_scale_factor = 0.05
autovacuum_naptime = 60
autovacuum_vacuum_cost_delay = 20

有助于查看表格膨胀程度的不同查询: