为什么 postgresql 写入巨大的临时文件并在循环中填满我的磁盘?

Why postgresql writes huge temporary files and fills my disk within a loop?

问题: 函数(下面,在 PostgreSQL 9.3 中)运行 迭代次数很少,但迭代次数很多,它会写入一个约 1 GB 的文件每次迭代磁盘,直到磁盘已满,然后代码以 failed to write.

终止

问题:有没有办法不把这些文件写入磁盘?或者找到其他方法来规避这个问题?理想情况下,我想将代码放在 运行 过夜,以便第二天分析结果。

tables 应该在每次迭代时被覆盖,所以我不明白为什么它填满了我的磁盘。在我之前的尝试中,它也 运行 内存不足,但我在 postgresql.conf.

中从 64 增加了 max_locks_per_transaction = 256

我在做什么:

我有一个函数可以获取控制内部循环的参数:开始和结束时间戳、时间 bin delta 时间 span 和时间 jump。像这样:SELECT ib_run2('2009-06-28 13:30:00', '2009-06-29 13:50:59', '10 minute', '0.5 hour', '24 hour');

因此该函数将开始和停止之间的时间划分为 bins,在此示例中,从 2009-06-28 13:30:00 开始的时间被划分为 10 minute 间隔 0.5 hour 然后跳转到 24 hour 和再次这样做,直到 2009-06-29 13:50:59

对于每个 10 分钟的区间,都会对时空数据集进行一些计算,包括按时间和位置进行选择以及计算距离。

函数内部不可避免地顺序扫描一个大的 table(6,154,794 行)和几个较小的扫描,并从每个扫描中选择一个子集。该函数对这些子集执行计算并将结果写入 created tables。

所有 table 都是用 CREATE TABLE 创建的。以 IB_000_ 开头的表在循环之前创建,并在循环内用 INSERT INTO 更新。以 IB_i_ 开头的表在每次迭代的循环中被删除并重新创建。

使用 IB_i_ 计算 table 涉及在同一迭代中创建的其他 table 使用 IB_i_ 或用于计算的外部 table。

函数:

CREATE OR REPLACE FUNCTION ib_run2(
        start_dt TEXT DEFAULT '2009-06-28 13:30:00'
      , end_dt   TEXT DEFAULT '2009-06-28 13:59:59'
      , deltat   TEXT DEFAULT '10 minute'
      , spant    TEXT DEFAULT '2 hour'
      , jump_txt TEXT DEFAULT '24 hour'
  ) RETURNS TEXT AS
$func$
      DECLARE n INT DEFAULT 1; DECLARE m INT DEFAULT 1; DECLARE iteration INT DEFAULT 0;
      DECLARE delta INTERVAL; DECLARE span INTERVAL; DECLARE jump INTERVAL;
      DECLARE mytext TEXT DEFAULT 'iMarinka';
      DECLARE start_time_query TIMESTAMP DEFAULT now();
      DECLARE dt0 TIMESTAMP;
      DECLARE dt1 TIMESTAMP;
      DECLARE dt TIMESTAMP;
BEGIN
    dt0:=start_dt :: TIMESTAMP;
    dt1:=end_dt :: TIMESTAMP;
    delta:=deltat :: INTERVAL;
    span:=spant :: INTERVAL;
    jump:=jump_txt :: INTERVAL;
    iteration:=0;

    n:=ceiling(extract(EPOCH FROM (dt1-dt0)          )*1.0/extract(EPOCH FROM (jump ) ));
    m:=ceiling(extract(EPOCH FROM ( (dt0+span) -dt0) )*1.0/extract(EPOCH FROM (delta) ));

    DROP TABLE IF EXISTS IB_000_times;
    CREATE TABLE IB_000_times (
              gid serial primary key, i INT, j INT
            , t_from_v TIMESTAMP, t_to_v TIMESTAMP
            , t_from_c TIMESTAMP, t_to_c TIMESTAMP
            , t_day TEXT, date_t DATE, t TIME
            , delta_t INTERVAL
            , dt0 TIMESTAMP, dt1 TIMESTAMP
            , dt TIMESTAMP, delta INTERVAL, span INTERVAL , jump INTERVAL );

    mytext:=(m+1)*(n+1)||' iterations '||n+1||' of i '||m+1||' of j'; RAISE NOTICE '%', mytext;

    FOR i IN 0..n LOOP  -----------------------------------------
    FOR j IN 0..m LOOP  -----------------------------------------

      dt := dt0 + j * delta + i * jump;
      iteration := iteration + 1;

      DROP TABLE IF EXISTS IB_i_times; 
      CREATE TABLE IB_i_times AS (
        WITH a AS (SELECT dt::DATE date_t, dt::TIME t , delta delta_t)
        SELECT date_t+ t - delta_t AS t_from_v
          , date_t+ t AS t_to_v
          , date_t+ t AS t_from_c
          , date_t+ t + delta_t AS t_to_c
          , to_char(date_t, 'day') AS t_day
          , a.date_t , a.t, a.delta_t
        FROM a
      );

      INSERT INTO IB_000_times (i , j,
          t_from_v , t_to_v , t_from_c , t_to_c , t_day , date_t , t , delta_t ,
          dt0 , dt1 , delta , span , jump , dt)
      SELECT i,j, t.t_from_v, t.t_to_v, t.t_from_c, t.t_to_c, t.t_day, t.date_t, t.t, t.delta_t ,
            dt0 , dt1 , delta , span , jump , dt
      FROM IB_i_times t;

      COPY ( select * FROM IB_000_times ) TO '/Volumes/1TB/temp/IB_000_times.csv' CSV HEADER DELIMITER ';' ;

      mytext := iteration||'/'||(n+1)*(m+1)||' -----> '||'   dt= '||to_char(dt,'YYYY-MM-DD HH24:MI:SS'); RAISE NOTICE '%', mytext;
      mytext := 'Fin '||': i='||i||', dt='|| to_char(dt,'YYYY-MM-DD HH24:MI:SS')||', started  '||start_time_query;

    END LOOP;----------------------------------------------------
    END LOOP;----------------------------------------------------
  RETURN mytext;
END;
$func$
LANGUAGE plpgsql;

除了tables IB_i_timesIB_000_times还有一堆其他的tables(为了保存space这里没有显示,代码有 ~500 行)函数在循环之前(和一些内部)创建并在循环内更新。

很难说,为什么 Postgres 会从此源代码生成临时文件。使用日志临时文件 - log_temp_files - 当您确定生成临时文件的语句时,您可以找出原因。通常是有限的work_mem

Controls logging of temporary file names and sizes. Temporary files can be created for sorts, hashes, and temporary query results. A log entry is made for each temporary file when it is deleted. A value of zero logs all temporary file information, while positive values log only files whose size is greater than or equal to the specified number of kilobytes. The default setting is -1, which disables such logging. Only superusers can change this setting. https://www.postgresql.org/docs/current/static/runtime-config-logging.html