问题背景

加压测试过程中发现插入数据过程中报错:could not write to hash-join temporary file: 设备上没有空间。但是查看服务器还有很多空闲空间,是什么导致这样的错误呢?

查看执行脚本

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)             Filter: (d_pjsxrq IS NOT NULL)

通过执行计划发现表连接使用了全表扫描以及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%,当临时文件占用磁盘过高,自动取消该查询,记录查询语句

4.一般查询如果耗费大量的临时文件,有可能是没有索引导致

postgresql-磁盘空间不足问题排查的更多相关文章

  1. Linux磁盘空间满了的排查与解决思路

    block正常满 (磁盘实际不足)inode 满 大量的小文件block 满 文件没有被彻底删除(硬链接数0 进程调用数不为0) 解放方法: 1 查看df -h 磁盘使用量根据占用量大小逐步逐步排查 ...

  2. linux 磁盘空间满了,排查记录

    先贴命令:du -m --max-depth=1或du -h --max-depth=1du:用于统计linux中文件或目录所占磁盘空间的大小du参数######-m:以M为单位展示查询结果-h:以K ...

  3. How to get the free disk space in PostgreSQL (PostgreSQL获取磁盘空间)

    Get the current free disk space in PostgreSQL PostgreSQL获取磁盘空间 from eshizhan Here has a simple way t ...

  4. linux硬件资源问题排查:cpu负载、内存使用情况、磁盘空间、磁盘IO

    在使用过程中之前正常的功能,突然无法使用,性能变慢,通常都是资源消耗问题,资源消耗可以从以下几个方面去排查.对于已经安装硬件资源监控软件(zabbix)的环境,直接使用硬件资源监控软件(zabbix) ...

  5. 云服务器 ECS Linux 磁盘空间满(含 innode 满)问题排查方法

    问题描述 在云服务器 ECS Linux 系统内创建文件时,出现类似如下空间不足提示: No space left on device … 问题原因 导致该问题的可能原因包括: 磁盘分区空间使用率达到 ...

  6. PostgreSQL表空间、模式、表、用户/角色之间的关系

    PostgreSQL表空间.模式.表.用户/角色之间的关系是本文我们主要要介绍的内容,表空间,数据库,模式,表,用户,角色之间的关系到底是怎样的呢?接下来我们就开始介绍这一过程. 实验出角色与用户的关 ...

  7. linux磁盘空间用满的处理方法

    linux下空间满可能有两种情况 可以通过命令 df -h  查看磁盘空间占用,实际上是查看磁盘块占用的文件(block) df -i  查看索引节点的占用(Inodes) 磁盘块和索引节点其中之一满 ...

  8. linux命令----查看磁盘空间

    今天用“web发布平台”发布测试的服务,两个节点中发现有一个节点没有发布成功,压测TPS始终上不去,排查后发现只有一个节点在打日志,另一个节点的服务进程都没有在运行,由此断定应该是没有发布成功,有点坑 ...

  9. jenkins log文件突然占满磁盘空间

    今天早上同事反应jenkins构建job发生异常,于是登录机器查看发现磁盘空间已满.进一步排查之后发现jenkins的catalina.out文件已占满磁盘空间. 用tail看了下日志后面都是关于DN ...

  10. linux磁盘空间占满问题快速定位并解决

    经常会遇到这样的场景:测试环境磁盘跑满了,导致系统不能正常运行!此时就需要查看是哪个目录或者文件占用了空间.常使用如下几个命令进行排查:df, lsof,du. 通常的解决步骤如下:1. df -h ...

随机推荐

  1. jieba分词/jieba-analysis(java版)

    简介 支持分词模式Search模式,用于对用户查询词分词Index模式,用于对索引文档分词特性支持多种分词模式全角统一转成半角用户词典功能conf 目录有整理的搜狗细胞词库因为性能原因,最新的快照版本 ...

  2. Linux - 操作系统的发展史

    操作系统的发展史(科普章节) 目标 了解操作系统的发展历史 知道 Linux 内核及发行版的区别 知道 Linux 的应用领域 01. 操作系统的发展历史 1.1 Unix 1965 年之前的时候,电 ...

  3. Willem, Chtholly and Seniorious

    Willem, Chtholly and Seniorious https://codeforces.com/contest/897/problem/E time limit per test 2 s ...

  4. 46 【golang项目】完成了一个小小的播放器功能

    项目地址:https://github.com/helww/mylab/tree/master/go/player 这个项目中用到了readme说明文件是一个markdown文件. 基础的控制语法,网 ...

  5. Python开发——数据类型【字符串】

    字符串定义 字符串是一个有序的字符的集合,用于存储和表示基本的文本信息 在Python中加了引号的字符,都被认为是字符串! 单引号.双引号.多引号之间的区别? 答案:单双引号没有区别 多引号的作用? ...

  6. Python开发——数据类型【字典】

    字典的定义 # Python语言中唯一的类型映射 # 键与值之间用“:”分开 # 项与项之间用“,”分开 person = {"name":"yuan",&qu ...

  7. java【基础】正则表达式

    1 字符串判断 java的正则使用的是Pattern以及Matcher来配合使用的. 如果只是用来判断输入的字符串是否符合格式,推荐使用Matcher的matches方法. public static ...

  8. typescript里面调用javasript

    index.html 里面加入函数: function tellYou() { egret.log("tell you."); javascript:android.funA(); ...

  9. python02 运算符,基本数据类型,整型,字符串

    1.python开发IDE pycharm,python编写工具,, #专业版 #不需要汉化 注册码问题解决 https://www.cnblogs.com/evlon/p/4934705.html整 ...

  10. SLICK基础

    1.sbt添加依赖 "com.typesafe.slick" %% "slick" % "3.2.3", "org.slf4j&q ...