从 table 日期范围内删除数百万条记录
delete millions for records from table between date range
我有一个名为camera_activities
的table,里面有超过数百万条记录,table结构就像
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);
单条记录就像
问题来了,
我想把2016年以前的记录全部删掉,那么多,范围是2014年等等,我们从2014年开始加数据..
我尝试了一个简单的查询并且效果很好,例如,如果我在 2 个日期之间删除
delete from camera_activities where done_at>'2017-07-12 10:55:37+00' and done_at<='2017-07-13 09:23:00+00
这个可以,但是比较费时,有几百万条记录,有什么快速的方法吗?
注意:如果我可以将日期范围增加到一个月或一个月以上,查询只会继续 运行 并且不会返回任何结果。
任何帮助或指导都会有所帮助,
执行大量 DELETE 操作有两种基本方法。
1) 创建另一个 table,删除旧的并重命名新的,最后 ANALYZE
新的 table:
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;
这种方法保证了生成的 table 将处于最佳状态(没有膨胀)。但是如果你的系统负载很重并且涉及到 table,那么它可能不太方便。在这种情况下,"smooth deletion" 可能看起来更好。
2) "Smooth" 删除:每次只删除相对少量的行,使用更积极的 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 '} )
if [[ $res = '0' ]]; then break; fi;
sleep 0.3; # control speed here; check bloating level
done
– 当没有行要删除时,这将自动停止。
您在 (camera_id, done_at)
上的索引应该会加速子选择,进行位图索引扫描 – 检查 EXPLAIN
。但是在 done_at
上有一个单独的索引可能是值得的,在这种情况下它可以是 btree
或 brin
(有损但大小更小):
create i_camera_activities_done_at on camera_activities using brin(done_at);
"more aggressive"(默认情况下)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
有助于查看您的 table 腹胀程度的不同查询:
- https://wiki.postgresql.org/wiki/Show_database_bloat
- http://blog.ioguix.net/postgresql/2014/09/10/Bloat-estimation-for-tables.html
- https://github.com/ioguix/pgsql-bloat-estimation/blob/master/table/table_bloat-82-84.sql
- https://github.com/dataegret/pg-utils/blob/master/sql/table_bloat.sql(对于索引:
- https://github.com/dataegret/pg-utils/blob/master/sql/index_bloat.sql;这些查询需要 pgstattuple 扩展)
我有一个名为camera_activities
的table,里面有超过数百万条记录,table结构就像
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);
单条记录就像
问题来了,
我想把2016年以前的记录全部删掉,那么多,范围是2014年等等,我们从2014年开始加数据..
我尝试了一个简单的查询并且效果很好,例如,如果我在 2 个日期之间删除
delete from camera_activities where done_at>'2017-07-12 10:55:37+00' and done_at<='2017-07-13 09:23:00+00
这个可以,但是比较费时,有几百万条记录,有什么快速的方法吗?
注意:如果我可以将日期范围增加到一个月或一个月以上,查询只会继续 运行 并且不会返回任何结果。
任何帮助或指导都会有所帮助,
执行大量 DELETE 操作有两种基本方法。
1) 创建另一个 table,删除旧的并重命名新的,最后 ANALYZE
新的 table:
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;
这种方法保证了生成的 table 将处于最佳状态(没有膨胀)。但是如果你的系统负载很重并且涉及到 table,那么它可能不太方便。在这种情况下,"smooth deletion" 可能看起来更好。
2) "Smooth" 删除:每次只删除相对少量的行,使用更积极的 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 '} )
if [[ $res = '0' ]]; then break; fi;
sleep 0.3; # control speed here; check bloating level
done
– 当没有行要删除时,这将自动停止。
您在 (camera_id, done_at)
上的索引应该会加速子选择,进行位图索引扫描 – 检查 EXPLAIN
。但是在 done_at
上有一个单独的索引可能是值得的,在这种情况下它可以是 btree
或 brin
(有损但大小更小):
create i_camera_activities_done_at on camera_activities using brin(done_at);
"more aggressive"(默认情况下)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
有助于查看您的 table 腹胀程度的不同查询:
- https://wiki.postgresql.org/wiki/Show_database_bloat
- http://blog.ioguix.net/postgresql/2014/09/10/Bloat-estimation-for-tables.html
- https://github.com/ioguix/pgsql-bloat-estimation/blob/master/table/table_bloat-82-84.sql
- https://github.com/dataegret/pg-utils/blob/master/sql/table_bloat.sql(对于索引:
- https://github.com/dataegret/pg-utils/blob/master/sql/index_bloat.sql;这些查询需要 pgstattuple 扩展)