zoukankan      html  css  js  c++  java
  • postgresql-磁盘空间不足问题排查

    问题背景

    加压测试过程中发现插入数据过程中报错: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.一般查询如果耗费大量的临时文件,有可能是没有索引导致

  • 相关阅读:
    汇编语言 第二单元 整理
    iOS10推送必看UNNotificationServiceExtension
    RSA加,解密
    添加购物车动画
    长按移动cell
    http live streming
    修改工程
    searbar
    tableView 编辑模式
    iOS 3D touch
  • 原文地址:https://www.cnblogs.com/zhangfx01/p/10563558.html
Copyright © 2011-2022 走看看