0

基本的に価格要因により、時系列データベース (ECHO ヒストリアン) からオープン ソース データベースに移行しています。オープン ソースの時系列データベースがないため、PostgreSQL を選択しました。ECHO に保存していたのは、時間と値のペアだけでした。ここに問題があります。postgre で作成したテーブルは 2 つの列で構成されています。1 つ目は UTC ミリ秒 (13 桁の数字) で時刻を格納する「bigint」型で、2 つ目はデータ型が「real」型に設定された値です。約 360 万行 (30 日間の時間範囲に広がる) のデータを埋めました。短い時間範囲 (たとえば 1 日) をクエリすると、クエリに 4 秒かかりますが、ECHO の同じ時間範囲での応答時間は150ミリ秒です!これは大きな違いです。時間が遅いのは時間がかかるためと思われますが、確かではありません。クエリ時間を改善する方法を教えてください。データ型「timestamp」と「timestamptz」の使用についても読みましたが、日付と時刻を UTC 秒ではなく通常の形式で保存する必要があるようです。これはクエリ時間を短縮するのに役立ちますか?

これが私のテーブル定義です:

            Table "public. MFC2 Flow_LCL "
Column  |  Type  | Modifiers | Storage | Stats target | Description  
----------+--------+-----------+---------+--------------+-------------

 the_time | bigint |           | plain   |              |
 value    | real   |           | plain   |              |

Indexes:
"MFC2 Flow_LCL _time_idx" btree (the_time)

Has OIDs: no

現在、時間を UTC ミリ秒単位で保存しています (bigint を使用)。ここでの課題は、時間値のペアが重複している可能性があることです。

これは私が使用しているクエリです(テーブル名、開始時刻、終了時刻を渡す単純な API を介して呼び出されます)

PGresult *res;

int rec_count;
std::string sSQL;

sSQL.append("SELECT * FROM ");
sSQL.append(" \" ");
sSQL.append(table);
sSQL.append(" \" ");
sSQL.append(" WHERE");
sSQL.append(" time >= ");
CString sTime;
sTime.Format("%I64d",startTime);
sSQL.append(sTime);
sSQL.append(" AND time <= ");
CString eTime;
eTime.Format("%I64d",endTime);
sSQL.append(eTime);
sSQL.append(" ORDER BY time ");

res = PQexec(conn, sSQL.c_str());
4

3 に答える 3

0

あなたは本当に2038年問題をすでに計画していますか?標準のUNIXのように、単にintを時間に使用しないのはなぜですか?

于 2013-03-13T16:21:37.100 に答える
0
SET search_path=tmp;

  -- -------------------------------------------
  -- create table and populate it with 10M rows
  -- -------------------------------------------
DROP SCHEMA tmp CASCADE;
CREATE SCHEMA tmp ;

SET search_path=tmp;

CREATE TABLE old_echo
        ( the_time timestamp NOT NULL PRIMARY KEY
        , payload DOUBLE PRECISION NOT NULL
        );

INSERT INTO old_echo (the_time, payload)
SELECT now() - (gs * interval '1 msec')
        , random()
FROM generate_series(1,10000000) gs
        ;

-- DELETE FROM old_echo WHERE random() < 0.8;

VACUUM ANALYZE old_echo;

SELECT MIN(the_time) AS first
        , MAX(the_time) AS last
        , (MAX(the_time) - MIN(the_time))::interval AS width
FROM old_echo
        ;

EXPLAIN ANALYZE
SELECT *
FROM old_echo  oe
JOIN (
        SELECT MIN(the_time) AS first
        , MAX(the_time) AS last
        , (MAX(the_time) - MIN(the_time))::interval AS width
        , ((MAX(the_time) - MIN(the_time))/2)::interval AS half
        FROM old_echo
        ) mima ON 1=1
WHERE oe.the_time >= mima.first + mima.half
AND  oe.the_time < mima.first + mima.half + '1 sec':: interval
        ;

結果:

                                                                               QUERY PLAN                                                                                
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 Nested Loop  (cost=0.06..59433.67 rows=1111124 width=64) (actual time=0.101..1.307 rows=1000 loops=1)
   ->  Result  (cost=0.06..0.07 rows=1 width=0) (actual time=0.049..0.050 rows=1 loops=1)
         InitPlan 1 (returns $0)
           ->  Limit  (cost=0.00..0.03 rows=1 width=8) (actual time=0.022..0.022 rows=1 loops=1)
                 ->  Index Scan using old_echo_pkey on old_echo  (cost=0.00..284873.62 rows=10000115 width=8) (actual time=0.021..0.021 rows=1 loops=1)
                       Index Cond: (the_time IS NOT NULL)
         InitPlan 2 (returns $1)
           ->  Limit  (cost=0.00..0.03 rows=1 width=8) (actual time=0.009..0.010 rows=1 loops=1)
                 ->  Index Scan Backward using old_echo_pkey on old_echo  (cost=0.00..284873.62 rows=10000115 width=8) (actual time=0.009..0.009 rows=1 loops=1)
                       Index Cond: (the_time IS NOT NULL)
   ->  Index Scan using old_echo_pkey on old_echo oe  (cost=0.01..34433.30 rows=1111124 width=16) (actual time=0.042..0.764 rows=1000 loops=1)
         Index Cond: ((the_time >= (($0) + ((($1 - $0) / 2::double precision)))) AND (the_time < ((($0) + ((($1 - $0) / 2::double precision))) + '00:00:01'::interval)))
 Total runtime: 1.504 ms
(13 rows)

更新: タイムスタンプが一意ではないように見えるため (ところで: その場合、重複はどういう意味ですか?) キー列を追加しました。醜いハックですが、ここでは機能します。10M -80% 行のクエリ時間は 11 ミリ秒。(ヒットした行数 210/222067):

CREATE TABLE old_echo
        ( the_time timestamp NOT NULL
        , the_seq SERIAL NOT NULL -- to catch the duplicate keys
        , payload DOUBLE PRECISION NOT NULL
        ,       PRIMARY KEY(the_time, the_seq)
        );

    -- Adding the random will cause some timestamps to be non-unique.
    -- (and others to be non-existent)
INSERT INTO old_echo (the_time, payload)
SELECT now() - ((gs+random()*1000::integer) * interval '1 msec')
        , random()
FROM generate_series(1,10000000) gs
        ;

DELETE FROM old_echo WHERE random() < 0.8;
于 2013-03-13T18:05:01.407 に答える
0

あなたの時系列データベースは、私がかつて調べた競合他社のように機能する場合、データを「時間」列の順序で自動的にヒープのような構造に格納します。Postgre はそうではありませ。その結果、O(n) 検索 [n = テーブル内の行数] を実行しています。時間フィルターに一致する行を探すには、テーブル全体を読み取る必要があります。タイムスタンプの主キー (一意のインデックスを作成します)、またはタイムスタンプが一意でない場合は、通常のインデックスを使用すると、単一レコードのバイナリ O(log n) 検索が可能になり、すべてのクエリのパフォーマンスが向上し、約 5% 未満を取得します。テーブル。Postgres は、インデックス スキャンとテーブル全体のスキャンのどちらが優れているかのクロスオーバー ポイントを推定します。

おそらく、そのインデックスのテーブルをCLUSTER( PG Docs ) したいでしょう。

また、上記のアドバイスに従って、timeまたはその他の SQL 予約語を列名として使用しないでください。合法であっても、トラブルを求めています。

[これはコメントとしてはよいのですが、長すぎます。]

于 2013-03-13T17:58:25.943 に答える