問題背景
查看執行腳本
insert into db_zjgj.result_rule_cwjbxx_db_sacw_t_cw_cwjbxx
select db_zjgj.uuid(),c_bh,'2E810338E4F2CEE0462E9A021A0E0816','財物-財物處置信息中,非先行處置類的處置信息,處置日期不能小於裁判生效日期','7B7DCB103239F5CBAB4106016DE258D1'
from db_zjgj.temp_cwjbxx_db_sacw_t_cw_cwjbxx where EXISTS (
SELECT
1
FROM
db_zjgj.temp_cwczxx_db_sacw_t_cw_cwczxx
WHERE
db_zjgj.temp_cwczxx_db_sacw_t_cw_cwczxx.c_cwbh = db_zjgj.temp_cwjbxx_db_sacw_t_cw_cwjbxx.c_bh
AND db_zjgj.temp_cwczxx_db_sacw_t_cw_cwczxx.d_czsj IS NOT NULL
)
AND EXISTS (
SELECT
1
FROM
db_zjgj.temp_ajjbxx_db_sacw_t_aj_ajjbxx AS ajjbxx
WHERE
db_zjgj.temp_cwjbxx_db_sacw_t_cw_cwjbxx.c_ajbh = ajjbxx.c_bh
AND ajjbxx.d_pjsxrq IS NOT NULL
)
AND EXISTS (
SELECT
1
FROM
db_zjgj.temp_ajjbxx_db_sacw_t_aj_ajjbxx AS ajjbxx,
db_zjgj.temp_cwczxx_db_sacw_t_cw_cwczxx
WHERE
db_zjgj.temp_cwjbxx_db_sacw_t_cw_cwjbxx.c_ajbh = ajjbxx.c_bh
AND db_zjgj.temp_cwjbxx_db_sacw_t_cw_cwjbxx.c_bh = db_zjgj.temp_cwczxx_db_sacw_t_cw_cwczxx.c_cwbh
AND db_zjgj.temp_cwczxx_db_sacw_t_cw_cwczxx.d_czsj < ajjbxx.d_pjsxrq
)
--查看執行計划
Hash Semi Join (cost=270531577.85..324240042.87 rows=113055 width=33)
Hash Cond: ((temp_cwjbxx_db_sacw_t_cw_cwjbxx.c_ajbh)::text = (ajjbxx.c_bh)::text)
-> Hash Semi Join (cost=270527939.60..324202660.37 rows=113055 width=99)
Hash Cond: (((temp_cwczxx_db_sacw_t_cw_cwczxx.c_cwbh)::text = (temp_cwczxx_db_sacw_t_cw_cwczxx_1.c_cwbh)::text) AND ((temp_cwjbxx_db_sacw_t_cw_cwjbxx.c_ajbh)::text = (ajjbxx_1.c_bh)::text))
-> Hash Semi Join (cost=10073.78..43895.94 rows=225857 width=99)
Hash Cond: ((temp_cwjbxx_db_sacw_t_cw_cwjbxx.c_bh)::text = (temp_cwczxx_db_sacw_t_cw_cwczxx.c_cwbh)::text)
-> Seq Scan on temp_cwjbxx_db_sacw_t_cw_cwjbxx (cost=0.00..17784.08 rows=451208 width=66)
-> Hash (cost=5485.57..5485.57 rows=225857 width=33)
-> Seq Scan on temp_cwczxx_db_sacw_t_cw_cwczxx (cost=0.00..5485.57 rows=225857 width=33)
Filter: (d_czsj IS NOT NULL)
-> Hash (cost=169739766.32..169739766.32 rows=3771811900 width=66)
-> Nested Loop (cost=0.00..169739766.32 rows=3771811900 width=66)
Join Filter: (temp_cwczxx_db_sacw_t_cw_cwczxx_1.d_czsj < ajjbxx_1.d_pjsxrq)
-> Seq Scan on temp_cwczxx_db_sacw_t_cw_cwczxx temp_cwczxx_db_sacw_t_cw_cwczxx_1 (cost=0.00..5485.57 rows=225857 width=41)
-> Materialize (cost=0.00..2870.50 rows=50100 width=41)
-> Seq Scan on temp_ajjbxx_db_sacw_t_aj_ajjbxx ajjbxx_1 (cost=0.00..2620.00 rows=50100 width=41)
-> Hash (cost=2620.00..2620.00 rows=50100 width=33) -> Seq Scan on temp_ajjbxx_db_sacw_t_aj_ajjbxx ajjbxx (cost=0.00..2620.00 rows=50100 width=33)
通過執行計划發現表連接使用了全表掃描以及nested loop連接。
查看表數據量和索引情況
--查看個表數據量
db_zjgj.temp_ajjbxx_db_sacw_t_aj_ajjbxx:50100
db_zjgj.temp_cwczxx_db_sacw_t_cw_cwczxx:225857
db_zjgj.temp_cwjbxx_db_sacw_t_cw_cwjbxx:451208
發現各表均無索引,且沒有主鍵。(了解到這些表都是抽數過程中生成的,在抽數完成后均會刪除)。
優化
--添加主鍵和邏輯外鍵索引
alter table db_zjgj.temp_ajjbxx_db_sacw_t_aj_ajjbxx add primary key(c_bh);
create index i_ajjbxx_d_pjsxrq on db_zjgj.temp_ajjbxx_db_sacw_t_aj_ajjbxx(d_pjsxrq);
alter table db_zjgj.temp_cwczxx_db_sacw_t_cw_cwczxx add primary key(c_bh);
create index i_cwczxx_c_cwbh on db_zjgj.temp_cwczxx_db_sacw_t_cw_cwczxx(c_cwbh);
create index i_cwczxx_d_czsj on db_zjgj.temp_cwczxx_db_sacw_t_cw_cwczxx(d_czsj);
alter table db_zjgj.temp_cwjbxx_db_sacw_t_cw_cwjbxx add primary key(c_bh);
create index i_cwjbxx_c_ajbh on db_zjgj.temp_cwjbxx_db_sacw_t_cw_cwjbxx(c_ajbh);
--查看執行計划
Hash Semi Join (cost=13712.87..298118.93 rows=113055 width=33)
Hash Cond: ((temp_cwjbxx_db_sacw_t_cw_cwjbxx.c_ajbh)::text = (ajjbxx.c_bh)::text)
-> Nested Loop Semi Join (cost=10074.62..260736.42 rows=113055 width=99)
Join Filter: ((temp_cwczxx_db_sacw_t_cw_cwczxx.c_cwbh)::text = (temp_cwczxx_db_sacw_t_cw_cwczxx_1.c_cwbh)::text)
-> Hash Semi Join (cost=10073.78..43895.94 rows=225857 width=99)
Hash Cond: ((temp_cwjbxx_db_sacw_t_cw_cwjbxx.c_bh)::text = (temp_cwczxx_db_sacw_t_cw_cwczxx.c_cwbh)::text)
-> Seq Scan on temp_cwjbxx_db_sacw_t_cw_cwjbxx (cost=0.00..17784.08 rows=451208 width=66)
-> Hash (cost=5485.57..5485.57 rows=225857 width=33)
-> Seq Scan on temp_cwczxx_db_sacw_t_cw_cwczxx (cost=0.00..5485.57 rows=225857 width=33)
Filter: (d_czsj IS NOT NULL)
-> Nested Loop (cost=0.83..0.95 rows=1 width=66)
Join Filter: (temp_cwczxx_db_sacw_t_cw_cwczxx_1.d_czsj < ajjbxx_1.d_pjsxrq)
-> Index Scan using temp_ajjbxx_db_sacw_t_aj_ajjbxx_pkey on temp_ajjbxx_db_sacw_t_aj_ajjbxx ajjbxx_1 (cost=0.41..0.45 rows=1 width=41)
Index Cond: ((c_bh)::text = (temp_cwjbxx_db_sacw_t_cw_cwjbxx.c_ajbh)::text)
-> Index Scan using i_cwczxx_c_cwbh on temp_cwczxx_db_sacw_t_cw_cwczxx temp_cwczxx_db_sacw_t_cw_cwczxx_1 (cost=0.42..0.48 rows=1 width=41)
Index Cond: ((c_cwbh)::text = (temp_cwjbxx_db_sacw_t_cw_cwjbxx.c_bh)::text)
-> Hash (cost=2620.00..2620.00 rows=50100 width=33)
-> Seq Scan on temp_ajjbxx_db_sacw_t_aj_ajjbxx ajjbxx (cost=0.00..2620.00 rows=50100 width=33)
Filter: (d_pjsxrq IS NOT NULL)
添加索引后cost降了下來,數據能夠順利插入,最終sql大概需要6s左右。
疑問
為什么查看服務器還有空閑空間,但是執行sql卻報錯磁盤空間不足呢。
--查看abase數據文件目錄:/opt/thunisoft/abdata/3.6/abase1/base/pgsql_tmp
[thunisoft@localhost base]$ du -sh *|sort
19M pgsql_tmp
3.2G 408143
6.3G 410629
7.0M 13236
7.1M 1
7.2M 13241
7.2M 16444
--臨時目錄下面有許多個臨時文件
[thunisoft@localhost pgsql_tmp]$ ls |wc -w
65551
--臨時文件均已pg_sql_tmp23277開頭
[thunisoft@localhost pgsql_tmp]$ ll
...
-rw-------. 1 thunisoft thunisoft 0 Aug 27 14:24 pgsql_tmp23277.9998
-rw-------. 1 thunisoft thunisoft 0 Aug 27 14:24 pgsql_tmp23277.9999
...
--23277標識進程號,從pg_log日志文件中可以找到該進程為當前正在執行sql
可以看出臨時目錄下面有許多文件,大小為0,pgsql_tmp所占用的空間為19M。
場景還原
--磁盤空間使用情況
[thunisoft@localhost base]$ df -lh
Filesystem Size Used Avail Use% Mounted on
/dev/mapper/VolGroup-lv_root 18G 12G 4.6G 73% /
tmpfs 5.9G 4.8K 5.9G 1% /dev/shm
/dev/sda1 485M 33M 427M 8% /boot
--刪除剛剛所建的索引,重新執行該sql,發現pgsql_tmp會不斷地增大
[thunisoft@localhost base]$ du -sh *|sort
1.6G pgsql_tmp
3.2G 408143
6.3G 410629
7.0M 13236
7.1M 1
7.2M 13241
7.2M 16444
--最終直至占滿所有空間
[thunisoft@localhost base]$ df -lh
Filesystem Size Used Avail Use% Mounted on
/dev/mapper/VolGroup-lv_root 18G 17G 254M 99% /
tmpfs 5.9G 4.0K 5.9G 1% /dev/shm
/dev/sda1 485M 33M 427M 8% /boot
--空間占滿 報錯后這些臨時文件空間大部分被回收,但是文件還在,文件個數仍為65551
[thunisoft@localhost base]$ du -sh *|sort
19M pgsql_tmp
...
[thunisoft@localhost pgsql_tmp]$ ls |wc -w
65551
可以看出該sql執行時臨時文件會不斷的增大,直至占滿空間報錯,當sql報錯后臨時文件大部分被清空,磁盤空間又將得到釋放,所以開始看到的磁盤空間並沒有滿,但是報錯卻是磁盤空間滿了。
那些情況會生成這些臨時文件
據了解查詢要使用的內存超出work_mem的大小時(包括排序,DISTINCT,MERGE JOIN,HASH JOIN,笛卡爾積,哈希聚合,分組聚合,遞歸查詢)等操作時會使用臨時文件來存儲中間過程的數據。如果頻繁的進行上述操作,臨時文件將會快速增長。只有重啟能夠解決該問題,重啟后將清空所有臨時文件。
--查詢使用臨時文件相關
--1.每個進程臨時文件空間的限制,如果超過改值,查詢將取消,默認無限制
#temp_file_limit = -1 # limits per-process temp file space
# in kB, or -1 for no limit
--2.當臨時文件使用量大於設置閾值時,記錄日志,默認不記錄
#log_temp_files = -1 # log temporary files equal or larger
# than the specified size in kilobytes;
# -1 disables, 0 logs all temp files
--3.當超過work_mem時使用臨時文件
#work_mem (integer)
結語
1.回到最開始的sql,這些臨時表可以在數據插入表后建立索引,然后再執行最后的抽數,這樣效率會高一點,嵌套循環耗費cpu,磁盤io,以及臨時文件占用高
2.abase為了提高執行效率一些操作會使用內存代替臨時存儲,當內存不足時就會使用臨時文件存儲中間數據。
3.可酌情設置temp_file_limit 為磁盤空間的10%,當臨時文件占用磁盤過高,自動取消該查詢,記錄查詢語句