3

3 つのテーブルがあるとします:
-Buildings
-Rooms
-People

建物には 1 から 30 の部屋 (平均は 3 としましょう)
を持つことができ、建物には 0 から 30 人 (平均は 3 とします) を
持つことができます。部屋と人は 1 つの建物にしか所属できません。

毎月、約 50.000 の新しい建物とその部屋と人々がデータベースに追加されます。
2 年以上前のデータを削除できるため、建物の行は約 120 万になります。

主な問題は、通常 (常にではないが) 少なくとも 2 つのテーブル (常に存在する建物) を含むデータを検索して返す必要があるため、結合を実行する必要があることです。

私は3つの解決策を研究しました。

  • 正規化されたデータを持つ (結合によるパフォーマンスの低下とスケーラビリティの低下)
  • Rooms and People テーブルの建物データを複製します。(高速ですが、一般的に非正規化は好きではありません)
  • Oracle クラスタ テーブル。(良いパフォーマンスを提供しているようで、データはまだ正規化されています)

質問は次のとおり
です。Oracle Cluster はこの状況に適していますか?
このようなクラスターに連続して行を追加しても問題ありませんか?
クラスターをお勧めしない場合、なぜ、そして何がより適しているのでしょうか?

詳細:

集まる:

SELECT *
FROM
  (SELECT *
    /*+ FIRST_ROWS(200)*/
  FROM BUILDING_C R
  INNER JOIN PEOPLE_C C
  ON (R.BUILDING_id = C.BUILDING_id)  
  INNER JOIN ROOM_C S
  ON (S.BUILDING_id = R.BUILDING_id)
  WHERE S.OPEN_DATE               >= SYSDATE - 60 -1
  AND S.OPEN_DATE               <= SYSDATE - 60
  ORDER BY S.OPEN_DATE
  )
WHERE rownum < 200;--17 consistent gets

自動トレース出力

正規化:

SELECT *
FROM
  (SELECT *
    /*+ FIRST_ROWS(200)*/
  FROM BUILDING_N R
  INNER JOIN PEOPLE_N C
  ON (R.BUILDING_id = C.BUILDING_id)  
  INNER JOIN ROOM_N S
  ON (S.BUILDING_id = R.BUILDING_id)
  WHERE S.OPEN_DATE               >= SYSDATE - 60 -1
  AND S.OPEN_DATE               <= SYSDATE - 60
  ORDER BY S.OPEN_DATE
  )
WHERE rownum < 200;--44 consistent gets

自動トレース出力

4

2 に答える 2

6

クラスタリングは、密接に関連し、多くの場合ディスク上の同じ領域に結合されているテーブルを格納する方法です。クラスター キーは、IO を節約するためにクエリでテーブルが通常結合される 1 つまたは複数の列です。ただし、単一のクラスター行内のすべてのテーブル行の合計サイズがディスク ブロックのサイズを超えると、連鎖することになり、ある日、クラスターのすべての利点が失われます。私の意見では、クラスター内の 3 つのテーブルすべてで 1.2 M のローリング ボリュームがあり、明らかに HWM の影響があることを考えると、オーバーヘッドになるため、回避することをお勧めします。

JOINSに行くほうがいいです。

例えば。

CREATE TABLE BUILDING_C ( BUILDING_ID NUMBER PRIMARY KEY,
                     ADDRESS_FIELD VARCHAR2 ( 25 ) );

CREATE TABLE PEOPLE_C ( BUILDING_ID NUMBER PRIMARY KEY,
                    CUSTOMER_ID NUMBER,
                    ROOM_ID NUMBER,
                    CUSTOMER_DETAILS VARCHAR2 ( 25 ) );

CREATE TABLE ROOM_C ( BUILDING_ID NUMBER PRIMARY KEY,
                  ROOM_ID NUMBER,
                  OPEN_DATE DATE,
                  CURRENT_OCCUPANCY CHAR ( 1 ) );

BEGIN
    DBMS_STATS.SET_TABLE_STATS ( OWNNAME     => 'REALSPIRITUALS',
                            TABNAME  => 'BUILDING_C',
                            NUMROWS  => 20000000 );
END;
/

BEGIN
    DBMS_STATS.SET_TABLE_STATS ( OWNNAME     => 'REALSPIRITUALS',
                            TABNAME  => 'PEOPLE_C',
                            NUMROWS  => 20000000 );
END;
/

BEGIN
    DBMS_STATS.SET_TABLE_STATS ( OWNNAME     => 'REALSPIRITUALS',
                            TABNAME  => 'ROOM_C',
                            NUMROWS  => 20000000 );
END;
/

クエリでは、SELECT * /*+ FIRST_ROWS(200)*/代わりに使用しているため、SELECT /*+ FIRST_ROWS(200)*/ *ヒントは有効になりません

SET AUTOTRACE ON

SELECT
      *
FROM
      (SELECT
            /*+ FIRST_ROWS(200)*/
            *
       FROM
            BUILDING_C R
            INNER JOIN PEOPLE_C C
                ON ( R.BUILDING_ID = C.BUILDING_ID )
            INNER JOIN ROOM_C S
                ON ( S.BUILDING_ID = R.BUILDING_ID )
       WHERE
            S.OPEN_DATE >=   SYSDATE - 60 - 1
            AND S.OPEN_DATE <= SYSDATE - 60
       ORDER BY
            S.OPEN_DATE)
WHERE
      ROWNUM < 200;


Execution Plan
----------------------------------------------------------
   0       SELECT STATEMENT Optimizer Mode=HINT: FIRST_ROWS (Cost=54189 Card=199 Bytes=38 K)
   1    0    COUNT STOPKEY
   2    1      VIEW (Cost=54189 Card=50 K Bytes=9 M)
   3    2        SORT ORDER BY STOPKEY (Cost=54189 Card=50 K Bytes=9 M)
   4    3          FILTER
   5    4            NESTED LOOPS
   6    5              NESTED LOOPS (Cost=52041 Card=50 K Bytes=9 M)
   7    6                MERGE JOIN (Cost=2020 Card=50 K Bytes=5 M)
   8    7                  TABLE ACCESS BY INDEX ROWID REALSPIRITUALS.BUILDING_C (Cost=826 Card=20 M Bytes=1G)
   9    8                    INDEX FULL SCAN REALSPIRITUALS.SYS_C00504893 (Cost=26 Card=20 M)
  10    7                  SORT JOIN (Cost=1194 Card=50 K Bytes=1 M)
  11   10                    TABLE ACCESS FULL REALSPIRITUALS.ROOM_C (Cost=660 Card=50 K Bytes=1 M)
  12    6                INDEX UNIQUE SCAN REALSPIRITUALS.SYS_C00504894 (Cost=0 Card=1)
  13    5              TABLE ACCESS BY INDEX ROWID REALSPIRITUALS.PEOPLE_C (Cost=1 Card=1 Bytes=91)

Statistics
----------------------------------------------------------
          1  recursive calls
          0  spare statistic 3
          0  gcs messages sent
          0  db block gets from cache
          0  physical reads direct (lob)
          0  queue position update
          0  queue single row
          0  queue ocp pages
          0  HSC OLTP Compressed Blocks
          0  HSC IDL Compressed Blocks
          0  rows processed

提案:

  1. OPEN_DATE 列にインデックスを使用する
  2. /*+ parallel (table,n) */ を高速化する必要がある場合は、並列ヒントを使用します。
  3. レンジパーティションを試す
于 2013-11-19T10:26:56.593 に答える
3

「検索を改善する」とおっしゃっていますが、実際にこのデータモデルを実装したことはありますか? いくつかの候補クエリを試しましたか? 彼らは十分に機能していませんか?Oracle にとって 120 万行はピーナッツです。数十億行のテーブルがいくつかあります。5兆行 を超える分割された IOT を持っている人を知っています。

まず、すべてを正規化し、いくつかの候補クエリをテストします。彼らのパフォーマンスはどうですか?問題がある場合、不足しているインデックスはありますか? 適切なパフォーマンスは何だと思いますか?

私が読んだ内容に基づいて、正規化されたデータ モデルから適切なインデックス セットを使用して満足のいくパフォーマンスを得ることができなかったとしたら、私は非常に驚かれることでしょう。

于 2013-11-15T15:17:12.663 に答える