2

質問: クエリの処理時に処理された行の行 ID のリストを取得するにはどうすればよいですか?

編集注:返された行を探していません。ユーザーが Facebook に 5 つの投稿を持っていて、'''SELECT * FROM posts WHERE user=Mark ORDER BY date desc LIMIT 1''' を実行すると、返される数が 1 になることはわかっていますが、知りたいです処理された行数 (この場合、インデックスなし、おそらくすべての行)。そして、私は主に SELECT ステートメントを見ています。

こんにちは、

私は現在、データの老化の方向を目指すプロジェクトに取り組んでいます。つまり、どのタプルが定期的にアクセスされ、どのタプルがそうでないかを判断しようとしています。適切なワークロード (つまり、システムのクエリ ログ) と対応するデータがあり、どの行が処理されたかを知りたいと考えています。

質問のほかに、どの行、どの属性にも関心がありますが、それはクエリを解析することで実行できます (プロジェクション、結合属性、および where 条件)。実際に処理された行を取得する方法については、質問を開いたままにします。

完全なテーブル スキャンを必要とする where 条件があるため、多くのクエリ (インデックスがないと仮定します) がすべての行を処理することを認識しています。私たちはその問題を認識していますが、どの行がアクセスされたかを知りたくありません。

私の最後の質問は次のとおりです。どうすればそれを達成できますか?

MySQL と Postgres を調べましたが、十分な情報が見つかりませんでした (たとえば、MySQL の「explain」は、処理された行数の推定値を返すだけで、行 ID は返しません)。その種のロギングを実現するには、DB のソース コードを変更する必要があると思います (ロギングのパフォーマンスは問題ではなく、オフライン分析です)。それを達成する/それを行う方法を誰かが推奨していますか?

Davidのコメントに関する編集:私が達成しようとしているのは、(特定のワークロードを見て)どのタプルが決してアクセスされないかを知ることです。典型的な老化の問題。たとえば、2 年以上前の Facebook の投稿は、ほとんど閲覧、いいね、コメントされていないため、外部の (安価な) システムに保存できます。したがって、どの行が定期的にアクセスされるかを今すぐ確認する必要があります。

4

4 に答える 4

2

ソースコードを微調整する気がある場合は、PostgreSQL で次の操作を実行できます。

まず、いくつかの注意事項:

  • それを行うのは本当に悪い考えです。これは教育目的でのみ役立ちます。1 つ目は速度が低下するため、2 つ目はサーバーがクラッシュする可能性があるためです (適切にテストしていません)。
  • PostgreSQL 9.2 で次のことを試しました。他のバージョンでも動作するはずですが、試していません。

ここでのアイデアは、まず、タプルを文字列にダンプする関数を作成することです。簡単にするためにsrc/include/debugtuple.h、次のように(pgソース内に)という名前のファイルを作成しました。

#ifndef _DEBUGTUPLE_H_
#define _DEBUGTUPLE_H_

#include "postgres.h"

#include "access/relscan.h"
#include "executor/execdebug.h"
#include "utils/rel.h"

static void InitScanRelation(SeqScanState *node, EState *estate);
static TupleTableSlot *SeqNext(SeqScanState *node);

static char *
ExecBuildSlotValueDescription(TupleTableSlot *slot, int maxfieldlen)
{
    StringInfoData buf;
    TupleDesc   tupdesc = slot->tts_tupleDescriptor;
    int         i;

    /* Make sure the tuple is fully deconstructed */
    slot_getallattrs(slot);

    initStringInfo(&buf);

    appendStringInfoChar(&buf, '(');

    for (i = 0; i < tupdesc->natts; i++)
    {
        char       *val;
        int         vallen;

        if (slot->tts_isnull[i])
            val = "null";
        else
        {
            Oid         foutoid;
            bool        typisvarlena;

            getTypeOutputInfo(tupdesc->attrs[i]->atttypid,
                              &foutoid, &typisvarlena);
            val = OidOutputFunctionCall(foutoid, slot->tts_values[i]);
        }

        if (i > 0)
            appendStringInfoString(&buf, ", ");

        /* truncate if needed */
        vallen = strlen(val);
        if (vallen <= maxfieldlen)
            appendStringInfoString(&buf, val);
        else
        {
            vallen = pg_mbcliplen(val, vallen, maxfieldlen);
            appendBinaryStringInfo(&buf, val, vallen);
            appendStringInfoString(&buf, "...");
        }
    }

    appendStringInfoChar(&buf, ')');

    return buf.data;
}

#endif

src/backend/executor/ここで、nodeSeqscan.cとにある 2 つのファイルを編集する必要があります。nodeIndexscan.c両方に、上記で作成したファイルを含めます (ファイルの先頭に)。

#include "debugtuple.h"

nodeSeqscan.c、関数を見つけてSeqNext、次のように編集します (これらの 2 行だけを追加します)。

static TupleTableSlot *
SeqNext(SeqScanState *node)
{
    [...]
        ExecClearTuple(slot);

    /* ADD THE TWO FOLLOWING LINES: */
    if (slot && slot->tts_tuple)
        elog(NOTICE, "Seq Scan processed: %s", ExecBuildSlotValueDescription(slot, 1000));
    return slot;
}

nodeIndexscan.c次に、関数で同じことを行いますIndexNext:

static TupleTableSlot *
IndexNext(IndexScanState *node)
{
    [...]
    while ((tuple = index_getnext(scandesc, direction)) != NULL)
    {
        [...]

        /* ADD THE TWO FOLLOWING LINES: */
        if (slot && slot->tts_tuple)
            elog(NOTICE, "Index Scan processed: %s", ExecBuildSlotValueDescription(slot, 1000));
        return slot;
    }

    ...
    return ExecClearTuple(slot);
}

最後に、ソース コードのルートに移動して再コンパイルします。

make && make install

seqscan現在、この変更されたバージョンは、 or indexscan(およびそれらのみ)で処理するすべてのタプルで NOTICE メッセージを発生させます。elog関数呼び出しで行を変更して、必要なことを行うことができます。

楽しむ。

于 2013-09-24T18:38:12.550 に答える
2

MySQL については何も言えませんが、PostgreSQL では を使用できますEXPLAIN (ANALYZE,VERBOSE)。このVERBOSEオプションは、処理された行数を示します。実際のケースについては、このSQL Fiddleを参照してください。出力EXPLAINは次のとおりです。

Limit (cost=17.57..17.58 rows=1 width=8) (actual time=0.145..0.146 rows=1 loops=1)
Output: a, b
-> Sort (cost=17.57..17.61 rows=15 width=8) (actual time=0.143..0.143 rows=1 loops=1)
    Output: a, b
    Sort Key: foo.a
    Sort Method: top-N heapsort Memory: 25kB
    -> Seq Scan on public.foo (cost=0.00..17.50 rows=15 width=8) (actual time=0.014..0.114 rows=15 loops=1)
        Output: a, b
        Filter: (foo.b = 1)
        Rows Removed by Filter: 985
        Total runtime: 0.168 ms

ノードを見ると、Seq Scan15 行が返されていることがわかります。次の 3 行に「Rows Removed by Filter: 985」と表示されます。これは、985 行が無視された (ただし処理された) ことを意味するため、985+15=1000スキャンしたことになります。

処理された行を実際に見るには、処理された行から値を送信するか、一時テーブルにデータを入力するだけのダミー関数を作成する (ハックのような) ソリューションしか考えられませんRAISE NOTICE/LOG/DEBUG(これの方が良いと思います)。WHERE句でこの関数を呼び出します。これに関する問題は、PostgreSQL のプランナーがANDs の実行を並べ替え、最初に関数呼び出しを実行しない可能性があることです。関数の COST を 1 に設定することはできますが、これが常に機能するという保証はありません。機能は次のとおりです。

CREATE OR REPLACE FUNCTION logit(v anyelement)
RETURNS BOOLEAN
LANGUAGE PLPGSQL AS $$
BEGIN
    INSERT INTO tmp_row_process_log VALUES(v);
    RETURN TRUE;
END;
$$
COST 1;

そして使用するには:

CREATE TEMP TABLE tmp_row_process_log(a int);

SELECT * FROM foo
WHERE logit(a) AND b = 1
ORDER BY a
LIMIT 1;

SELECT * FROM tmp_row_process_log;

実用的なソリューションについては、このSQL Fiddleを参照してください。

このソリューションを使用すると、実際にはプランナーの決定を変更できることに注意してください。そのため、関数呼び出しがある場合とない場合は同じではない可能性があります。ただし、両方のソリューションを使用して結果を比較できます。

于 2013-09-14T15:03:32.730 に答える
0

私はパーティーに少し遅れるかもしれません。しかし、あなたの質問に基づいて、これはPostgresqlで簡単に行うことができます

rahul=# select count(*) , array_agg(col1) from nametest where year > 1990 limit 2 ;
 count | array_agg 
-------+-----------
     3 | {5,6,7}
(1 row)

array_agg は、処理された行のリストを提供します。

于 2013-10-28T20:17:10.667 に答える
0

これは、リレーショナル データベース テクノロジの本質的な特性を明らかにする興味深い質問です。

SQL は手続き型言語ではなく、宣言型言語です。クライアントの手続き型言語 (Java、C#、php など) で使用すると、宣言と手続きの間の奇妙な領域に入ります。あなたが住んでいる領域は、実際の RDBMS が手続き型の方法で実装されているという事実によって、さらに奇妙になります。したがって、アプリケーション SQL を作成するときは、プロシージャル ロックの 2 つの層に挟まれた宣言型石炭の継ぎ目にいることになります。

手続き型の領域に存在するファイル システムで提案するこのデータ エージング クエリを実行できます。多くのファイル システムには、ファイルごとに最新参照日属性があります。しかし、RDBMS ではそうではありません。

SELECT操作を行うときは、特定のテーブル、つまり各行に特定の列を含む特定の一連のデータ行を宣言します。これは結果セットと呼ばれることもあります。このテーブルは、RDBMS 内の他のテーブルの内容に基づいています。

純粋主義者で申し訳ありませんが、あなたは手続き上の質問をしているのです - どの保存データをいつ取得したのですか? -- 宣言型システムの。質問は無意味であり、宣言的な領域では答えがありません。これは RDBMS の構築方法を反映しているため、理解することが重要です。

RDBMS の宣言的なベールを突き破りEXPLAIN、RDBMS にその内部手順に関するヒントを与えるような手法を使用できます。そのアプローチの限界を発見しました。

アプリケーションに手続き的なものを追加して、処理する各行におそらく列でタグを付けることができdate_processedます。それが完了したら、SQL を使用して、データの関連性を示す結果セットを宣言できます。

または、関連性を宣言する別の方法を見つけ出すこともできます。おそらく、既にdate_created列などを持つレコードのエイジングに基づいて。

于 2013-09-14T13:27:39.313 に答える