2

最近、クエリのパフォーマンスに問題がありました。ここで説明されているのは、直接実行する場合と比べて Hibernate select のパフォーマンスが低い - どのようにデバッグするのですか? 長い間苦労した後、selectプレフィックスを持つクエリが次のようなものであることを最終的に発見しました。

    SELECT sth.* FROM Something as sth...

この方法でクエリを開始すると、300 倍遅くなります。

    SELECT * FROM Something as sth..

誰かが私を助けて、なぜそうなのか答えてもらえますか? これに関するいくつかの外部文書は本当に役に立ちます。

テストに使用したテーブルは次のとおりです。

SALES_UNITテーブルには、名前などの販売ユニット ノードに関する基本的な情報が含まれています。主キーは ID で、フィールド VALID_FROM_DTTM は日付です。

SALES_UNIT_RELATIONには、販売単位ノード間の関係 PARENT-CHILD が含まれています。SALES_UNIT_PARENT_ID、SALES_UNIT_CHILD_ID、および VALID_TO_DTTM/VALID_FROM_DTTM で構成されます。どのテーブルとも関連付けられていません。ここでの PK は、..PARENT_ID、..CHILD_ID、および VALID_FROM_DTTM です。

私が使用した実際のクエリは次のとおりです。

    SELECT s.* 
    FROM   sales_unit s LEFT JOIN sales_unit_relation r 
               on (s.sales_unit_id = r.sales_unit_child_id) 
    WHERE  r.sales_unit_child_id IS NULL

    SELECT  * 
    FROM    sales_unit s LEFT JOIN sales_unit_relation r 
               on (s.sales_unit_id = r.sales_unit_child_id) 
    WHERE   r.sales_unit_child_id  IS NULL

同じクエリ、両方とも左結合を使用し、唯一の違いは選択です。

4

1 に答える 1

4

もちろん、これらは 2 つの異なるクエリです。計画は、選択が異なると変更される可能性があります。つまり、sth.* では、左側の結合テーブルでフル/高速フル インデックス スキャンを選択している可能性があります。一方、最初は完全なテーブル スキャンになる可能性があります。

さらにお手伝いさせていただくために、プランをご覧いただけますか? できればSQL*PLUSでこれを行う

set timing on
set autotrace on traceonly

select s.* from sales_unit s left join sales_unit_relation r on (s.sales_unit_id = r.sales_unit_child_id) where r.sales_unit_child_id is null;

select * from sales_unit s left join sales_unit_relation r on (s.sales_unit_id = r.sales_unit_child_id) where r.sales_unit_child_id is null;

編集

説明計画を考えると、すべてのステップで CARDINALITY=1 が表示されますか? テーブルが空のときに統計を収集しました! これを参照してください:

SQL> select s.* from sales_unit s left join sales_unit_relation r on (s.sales_unit_id = r.child_sales_unit_id) where r.child_sales_unit_id is null;

no rows selected

Elapsed: 00:00:03.19

Execution Plan
----------------------------------------------------------
Plan hash value: 1064670292

------------------------------------------------------------------------------------
| Id  | Operation          | Name          | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |               |     1 |    48 |    27  (86)| 00:00:01 |
|   1 |  NESTED LOOPS ANTI |               |     1 |    48 |    27  (86)| 00:00:01 |
|   2 |   TABLE ACCESS FULL| SALES_UNIT    |     1 |    35 |     2   (0)| 00:00:01 |
|*  3 |   INDEX RANGE SCAN | SALES_REL_IX1 |     1 |    13 |    25  (92)| 00:00:01 |
------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - access("S"."SALES_UNIT_ID"="R"."CHILD_SALES_UNIT_ID")


Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
     200314  consistent gets
       2220  physical reads
          0  redo size
        297  bytes sent via SQL*Net to client
        339  bytes received via SQL*Net from client
          1  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          0  rows processed

200314 IO を使用し、数秒かかったことがわかります。また、すべてのステップで ROWS = 1 を参照してください (つまり、フル スキャン)。統計を収集します。

SQL> begin dbms_stats.gather_table_stats(user, 'SALES_UNIT', degree=>8, cascade=>true); end;
  2  /

PL/SQL procedure successfully completed.

SQL> begin dbms_stats.gather_table_stats(user, 'SALES_UNIT_RELATION', degree=>8, cascade=>true); end;
  2  /

PL/SQL procedure successfully completed.

SQL> select s.* from sales_unit s left join sales_unit_relation r on (s.sales_unit_id = r.child_sales_unit_id) where r.child_sales_unit_id is null;

no rows selected

Elapsed: 00:00:00.84

Execution Plan
----------------------------------------------------------
Plan hash value: 2005864719

-----------------------------------------------------------------------------------------------
| Id  | Operation             | Name          | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |               |   912 | 18240 |       |  1659   (3)| 00:00:20 |
|*  1 |  HASH JOIN ANTI       |               |   912 | 18240 |  2656K|  1659   (3)| 00:00:20 |
|   2 |   TABLE ACCESS FULL   | SALES_UNIT    |   100K|  1472K|       |    88   (3)| 00:00:02 |
|   3 |   INDEX FAST FULL SCAN| SALES_REL_IX1 |   991K|  4841K|       |   618   (3)| 00:00:08 |
-----------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - access("S"."SALES_UNIT_ID"="R"."CHILD_SALES_UNIT_ID")


Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
       2537  consistent gets
          0  physical reads
          0  redo size
        297  bytes sent via SQL*Net to client
        339  bytes received via SQL*Net from client
          1  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          0  rows processed

SQL>

今は 2537 の取得のみを使用し、計画は正しい ROWS と HASH 結合を示しています (私たちのニーズにより適しています)。私のテストテーブルはおそらくあなたの実際のテーブルよりも小さいので、タイミングが近づいています

于 2012-11-15T13:57:53.780 に答える