PostgreSQL資料rotate用法介紹
標籤
PostgreSQL , 按時間覆蓋歷史資料
背景
在某些業務場景中,資料有冷熱之分,例如業務只關心最近一天、一週或者一個月的資料。對於歷史的資料可以丟棄。
比如某些指標的監控場景,保留一週的監控資料,歷史的都可以丟棄。
如何丟棄歷史資料?或者說如何實現rotate?
1. 使用delete, 刪除7天前的資料。
delete from table where crt_time<=now()-interval `7 day`;
這種方法會帶來額外的開銷,包括寫REDO日誌,垃圾回收等。如果刪除的資料量很大,還需要重新收集統計資訊,甚至收集不及時會導致統計資訊不準確。
另一方面,還可能引入merge join的問題。
《PostgreSQL merge join 評估成本時可能會查詢索引 – 硬解析務必引起注意 – 批量刪除資料後, 未釋放empty索引頁導致mergejoin執行計劃變慢 case》
2. 使用分割槽表,輪詢使用,並且使用truncate清理分割槽。
這種方法可以避免DELETE帶來的問題。
但是使用不當也可能引入新的問題: truncate是DDL操作,rename table也是DDL操作,建議對DDL加上鎖超時,否則DDL等待會堵塞任意其他SQL。
交換表名時,需要一個臨時表名,名字不能被佔用。
下面就以分割槽表為例,講一下資料rotate用法。
例子
以保留一週資料為例,看看第二種方法如何來實施。
得益於PostgreSQL支援DDL事務。
方法
一共9張表,一張主表,8張分割槽表,其中7個對應dow,還有一個對應預設分割槽(交換分割槽)。
dow分割槽使用約束,好處是查詢時可以根據約束直接過濾分割槽。
1. 建立主表
create table test(id int primary key, info text, crt_time timestamp);
2. 建立分割槽
create table test0(like test including all, constraint ck check(extract(dow from crt_time)=0)) inherits(test);
create table test1(like test including all, constraint ck check(extract(dow from crt_time)=1)) inherits(test);
create table test2(like test including all, constraint ck check(extract(dow from crt_time)=2)) inherits(test);
create table test3(like test including all, constraint ck check(extract(dow from crt_time)=3)) inherits(test);
create table test4(like test including all, constraint ck check(extract(dow from crt_time)=4)) inherits(test);
create table test5(like test including all, constraint ck check(extract(dow from crt_time)=5)) inherits(test);
create table test6(like test including all, constraint ck check(extract(dow from crt_time)=6)) inherits(test);
create table test_def(like test including all) inherits(test);
3. SELECT/UPDATE/DELETE資料時,直接操作主表,代入時間條件,可以過濾分割槽
postgres=# explain select * from test where crt_time=now() and extract(dow from crt_time)=0;
QUERY PLAN
-----------------------------------------------------------------------------------------------------
Append (cost=0.00..65.20 rows=3 width=44)
-> Seq Scan on test (cost=0.00..0.00 rows=1 width=44)
Filter: ((crt_time = now()) AND (date_part(`dow`::text, crt_time) = `0`::double precision))
-> Seq Scan on test0 (cost=0.00..32.60 rows=1 width=44)
Filter: ((crt_time = now()) AND (date_part(`dow`::text, crt_time) = `0`::double precision))
-> Seq Scan on test_def (cost=0.00..32.60 rows=1 width=44)
Filter: ((crt_time = now()) AND (date_part(`dow`::text, crt_time) = `0`::double precision))
(7 rows)
4. INSERT時,建議程式根據crt_time的dow自動拼接表名,直接對分割槽表進行操作。
如果程式不想對分割槽進行操作,那麼可以使用觸發器或規則。
例子
create or replace function ins_tg() returns trigger as $$
declare
begin
case extract(dow from NEW.crt_time)
when 0 then insert into test0 values (NEW.*);
when 1 then insert into test1 values (NEW.*);
when 2 then insert into test2 values (NEW.*);
when 3 then insert into test3 values (NEW.*);
when 4 then insert into test4 values (NEW.*);
when 5 then insert into test5 values (NEW.*);
when 6 then insert into test6 values (NEW.*);
end case;
return null;
end;
$$ language plpgsql strict;
create trigger tg before insert on test for each row execute procedure ins_tg();
insert into test values (1,`test`,now()+interval `1 day`);
insert into test values (1,`test`,now()+interval `2 day`);
insert into test values (1,`test`,now()+interval `3 day`);
insert into test values (1,`test`,now()+interval `4 day`);
insert into test values (1,`test`,now()+interval `5 day`);
insert into test values (1,`test`,now()+interval `6 day`);
insert into test values (1,`test`,now()+interval `7 day`);
postgres=# select tableoid::regclass , * from test;
tableoid | id | info | crt_time
----------+----+------+----------------------------
test0 | 1 | test | 2017-03-26 14:40:48.066905
test1 | 1 | test | 2017-03-27 14:40:50.450942
test2 | 1 | test | 2017-03-28 14:40:52.271922
test4 | 1 | test | 2017-03-23 14:40:22.551928
test5 | 1 | test | 2017-03-24 14:40:24.643933
test6 | 1 | test | 2017-03-25 14:40:28.138913
test3 | 1 | test | 2017-03-22 14:40:20.586945
(7 rows)
rotate用法(每天在空閒時間點處理一次即可,DDL支援事務,如果事務失敗,可重新發起重試)
在一個事務中完成如下動作
1. 計算明天的dow
2. 清除test_def約束
3. 清除test_def資料
4. test_def重新命名test_def_tmp(一個不存在的表名)
5. 明天的分割槽表,重新命名為test_def
6. test_def_tmp新增約束
7. test_def_tmp重新命名為明天的分割槽
例子
1. 開始事務
begin;
2. 設定鎖超時
set lock_timeout = `60s`;
3. 查詢明天的dow
select extract(dow from current_date+1);
date_part
-----------
3
(1 row)
4. 清除test_def約束
alter table test_def drop constraint IF EXISTS ck;
5. 清除test_def資料
truncate test_def;
6. 重新命名test_def
alter table test_def rename to test_def_tmp;
7. 重新命名明天的分割槽表
alter table test3 rename to test_def;
8. test_def_tmp新增約束
alter table test_def_tmp add constraint ck check(extract(dow from crt_time)=3);
9. test_def_tmp重新命名為明天的分割槽
alter table test_def_tmp rename to test3;
10. 提交或回滾
commit;
如果失敗,回滾事務。
rollback;
注意事項:
1. 鎖超時
2. 事務失敗注意回滾
3. 中間表名必須不存在
4. 約束名統一
小結
使用DELETE的方法清除歷史資料,會帶來額外的開銷,包括寫REDO日誌,垃圾回收等。如果刪除的資料量很大,還需要重新收集統計資訊,甚至收集不及時會導致統計資訊不準確。
另一方面,還可能引入merge join的問題。
《PostgreSQL merge join 評估成本時可能會查詢索引 – 硬解析務必引起注意 – 批量刪除資料後, 未釋放empty索引頁導致mergejoin執行計劃變慢 case》
因為PostgreSQL支援DDL封裝在事務中,所以也可以使用分割槽表,輪詢使用,並且使用truncate清理分割槽。
這種方法可以避免DELETE帶來的問題。
但是使用不當也可能引入新的問題: truncate是DDL操作,rename table也是DDL操作,建議對DDL加上鎖超時,否則DDL等待會堵塞任意其他SQL。
交換表名時,需要一個臨時表名,名字不能被佔用。
祝使用愉快。
相關文章
- postgresql資料庫鎖介紹SQL資料庫
- 【INDEX】Postgresql索引介紹IndexSQL索引
- PostgreSQL:psql 介紹SQL
- 達夢資料庫Disql用法詳解之Disql命令列命令用法介紹資料庫SQL命令列
- css url()用法介紹CSS
- getElementsByClassName()方法用法介紹
- css vm用法介紹CSS
- getCurrentPosition用法介紹
- PostgreSQL 事務模型介紹SQL模型
- PostgreSQL 事務模型介紹SQL模型
- Sqlserver_Oracle_Mysql_Postgresql不同資料庫之隔離級別介紹ServerOracleMySql資料庫
- 資料庫介紹資料庫
- python BeautifulSoup用法介紹Python
- MySQL 5.7 NOT EXISTS用法介紹MySql
- jQuery css()方法用法介紹jQueryCSS
- javascript中加號(+)用法介紹JavaScript
- jQuery(html,[ownerDocument])用法介紹jQueryHTML
- replaceChild()函式用法介紹函式
- Object.isSealed()用法介紹Object
- require.js用法介紹UIJS
- postgreSQL 索引(二)型別介紹SQL索引型別
- PostgreSQL中的索引介紹-GiSTSQL索引
- PostgreSQL-search_path介紹(八)SQL
- MNIST資料集介紹
- Cora 資料集介紹
- IndexedDB資料庫介紹Index資料庫
- MongoDB資料模型介紹MongoDB模型
- json資料介紹JSON
- nuPlan資料集介紹
- css em單位用法介紹CSS
- jQuery filter() 用法簡單介紹jQueryFilter
- css transition屬性用法介紹CSS
- javascript的this用法簡單介紹JavaScript
- js WebSocket用法簡單介紹JSWeb
- javascript arguments用法簡單介紹JavaScript
- onerror事件用法簡單介紹Error事件
- Android.mk 用法介紹Android
- spam和saint的用法介紹AI