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.一般查询如果耗费大量的临时文件,有可能是没有索引导致

  • 相关阅读:
    Java实现 蓝桥杯VIP 算法提高 高精度乘法
    Java实现 蓝桥杯VIP 算法提高 高精度乘法
    Java实现 蓝桥杯VIP 算法提高 高精度乘法
    Java实现 蓝桥杯VIP 算法提高 高精度乘法
    Java实现 蓝桥杯VIP 算法提高 高精度乘法
    Java实现 蓝桥杯VIP 算法提高 现代诗如蚯蚓
    Java实现 蓝桥杯VIP 算法提高 现代诗如蚯蚓
    Java实现 蓝桥杯VIP 算法提高 现代诗如蚯蚓
    Java实现 蓝桥杯VIP 算法提高 现代诗如蚯蚓
    ddd
  • 原文地址:https://www.cnblogs.com/zhangfx01/p/10563558.html
Copyright © 2011-2022 走看看