ดัชนีคอมโพสิต: คอลัมน์เลือกมากที่สุดก่อน?


17

ฉันกำลังอ่านเกี่ยวกับcomposite indexesและฉันสับสนเล็กน้อยเกี่ยวกับการสั่งซื้อ เอกสารนี้ (น้อยกว่าครึ่งทางเล็กน้อย) พูดว่า

โดยทั่วไปคุณควรใส่คอลัมน์ที่คาดว่าจะใช้บ่อยที่สุดเป็นอันดับแรกในดัชนี

อย่างไรก็ตามหลังจากนั้นไม่นานมันก็บอกว่า

สร้างดัชนีคอมโพสิตวางคอลัมน์ที่เลือกมากที่สุดก่อน นั่นคือคอลัมน์ที่มีค่ามากที่สุด

ออราเคิลยังกล่าวว่าที่นี่ในคำอื่น ๆ

หากคีย์ทั้งหมดถูกใช้ใน WHERE clauses บ่อยเท่ากันดังนั้นการสั่งซื้อคีย์เหล่านี้จากการเลือกส่วนใหญ่ไปจนถึงการเลือกอย่างน้อยที่สุดในคำสั่ง CREATE INDEX จะช่วยปรับปรุงประสิทธิภาพการสืบค้นได้ดีที่สุด

อย่างไรก็ตามฉันพบคำตอบ SOที่บอกว่าต่างออกไป มันบอกว่า

จัดเรียงคอลัมน์ด้วยคอลัมน์ที่เลือกน้อยที่สุดก่อนและคอลัมน์ที่เลือกมากที่สุดเป็นลำดับสุดท้าย ในกรณีของการผูกเน็คไทกับคอลัมน์ซึ่งมีแนวโน้มที่จะใช้ด้วยตนเอง

เอกสารแรกที่ฉันอ้างถึงบอกว่าคุณควรใช้งานบ่อยที่สุดในขณะที่คำตอบ SO บอกว่าควรจะใช้สำหรับการผูกไทเท่านั้น จากนั้นพวกเขายังแตกต่างกันในการสั่งซื้อ

นี้เอกสารยังพูดถึงเกี่ยวกับskip scanningและบอกว่า

การสแกนข้ามมีประโยชน์ถ้ามีค่าที่แตกต่างกันเล็กน้อยในคอลัมน์นำของดัชนีคอมโพสิตและค่าที่แตกต่างจำนวนมากในคีย์ที่ไม่เรียงลำดับชั้นของดัชนี

บทความอื่นพูดว่า

คอลัมน์คำนำหน้าควรเป็นการเลือกปฏิบัติมากที่สุดและใช้กันอย่างแพร่หลายในการสืบค้น

ซึ่งฉันเชื่อว่าการเลือกปฏิบัติส่วนใหญ่จะมีความโดดเด่นที่สุด

งานวิจัยทั้งหมดนี้ยังนำฉันไปสู่คำถามเดียวกัน คอลัมน์ที่เลือกมากที่สุดควรเป็นคอลัมน์แรกหรือสุดท้าย ควรเลือกคอลัมน์แรกที่มีการใช้งานมากที่สุดและมีเฉพาะคอลัมน์ที่เลือกมากที่สุดเท่านั้น?

บทความเหล่านี้ดูเหมือนจะขัดแย้งกัน แต่เสนอตัวอย่างบางส่วน จากสิ่งที่ผมได้รวบรวมดูเหมือนว่าจะมีประสิทธิภาพมากขึ้นสำหรับผู้least selective columnที่จะเป็นครั้งแรกIndex Skip Scansในการสั่งซื้อถ้าคุณกำลังที่คาดการณ์ไว้ แต่ฉันไม่แน่ใจว่าถูกต้องหรือไม่


คำตอบ:


8

จาก AskTom

(ใน 9i มี "index skip scan" ใหม่ - ค้นหาว่ามีอะไรให้อ่านเกี่ยวกับมันทำให้ดัชนี (a, b) หรือ (b, a) มีประโยชน์ในทั้งสองกรณีข้างต้น!)

ดังนั้นลำดับของคอลัมน์ในดัชนีของคุณขึ้นอยู่กับวิธีเขียนคำถามของคุณ คุณต้องการให้สามารถใช้ดัชนีสำหรับแบบสอบถามจำนวนมากเท่าที่จะทำได้ (เพื่อลดจำนวนดัชนีทั้งหมดที่คุณมี) - ซึ่งจะทำให้ลำดับของคอลัมน์ ไม่มีอะไรอื่น (การเลือกของ a หรือ b ไม่นับรวม)

หนึ่งในข้อโต้แย้งสำหรับการจัดเรียงคอลัมน์ในดัชนีคอมโพสิตตามลำดับจากการแบ่งแยกน้อยที่สุด (ค่าที่แตกต่างน้อยกว่า) จนถึงการเลือกปฏิบัติมากที่สุด (ค่าที่แตกต่างกันมากขึ้น) สำหรับการบีบอัดคีย์ดัชนี

SQL> create table t as select * from all_objects;

Table created.

SQL> create index t_idx_1 on t(owner,object_type,object_name);

Index created.

SQL> create index t_idx_2 on t(object_name,object_type,owner);

Index created.

SQL> select count(distinct owner), count(distinct object_type), count(distinct object_name ), count(*)  from t;

COUNT(DISTINCTOWNER) COUNT(DISTINCTOBJECT_TYPE) COUNT(DISTINCTOBJECT_NAME)      COUNT(*)
-------------------- -------------------------- --------------------------      ----------
                 30                         45                       52205      89807

SQL> analyze index t_idx_1 validate structure; 

Index analyzed.

SQL> select btree_space, pct_used, opt_cmpr_count, opt_cmpr_pctsave from index_stats;

BTREE_SPACE   PCT_USED OPT_CMPR_COUNT OPT_CMPR_PCTSAVE
----------- ---------- -------------- ----------------
    5085584     90          2           28

SQL> analyze index t_idx_2 validate structure; 

Index analyzed.

SQL> select btree_space, pct_used, opt_cmpr_count, opt_cmpr_pctsave  from index_stats; 

BTREE_SPACE   PCT_USED OPT_CMPR_COUNT OPT_CMPR_PCTSAVE
----------- ---------- -------------- ----------------
    5085584     90          1           14

ตามสถิติดัชนีดัชนีแรกสามารถบีบอัดได้มากขึ้น

อีกวิธีคือการใช้ดัชนีในแบบสอบถามของคุณ หากคำสั่งของคุณส่วนใหญ่ใช้col1,

ตัวอย่างเช่นหากคุณมีคำค้นหาเช่น -

  • select * from t where col1 = :a and col2 = :b;
  • select * from t where col1 = :a;

    - จากindex(col1,col2)นั้นก็จะทำงานได้ดีขึ้น

    หากคำสั่งของคุณส่วนใหญ่ใช้col2,

  • select * from t where col1 = :a and col2 = :b;
  • select * from t where col2 = :b;

    - จากindex(col2,col1)นั้นก็จะทำงานได้ดีขึ้น หากข้อความค้นหาทั้งหมดของคุณระบุทั้งสองคอลัมน์เสมอมันไม่สำคัญว่าคอลัมน์ใดจะมาเป็นอันดับแรกในดัชนีคอมโพสิต

    โดยสรุปข้อควรพิจารณาที่สำคัญในการเรียงลำดับคอลัมน์ของดัชนีคอมโพสิตคือการบีบอัดคีย์ดัชนีและวิธีที่คุณจะใช้ดัชนีนี้ในแบบสอบถามของคุณ

    อ้างอิง:

  • ลำดับคอลัมน์ในดัชนี
  • มีประสิทธิภาพน้อยกว่าหรือไม่ที่จะมีคอลัมน์นำที่สำคัญที่สุดในดัชนี (ขวา)?
  • ดัชนีข้ามการสแกน - การสั่งซื้อคอลัมน์ดัชนีมีความสำคัญมากกว่านี้หรือไม่? (ป้ายเตือน)


  • 3

    ตัวเลือกอันดับแรกจะมีประโยชน์ก็ต่อเมื่อคอลัมน์นี้อยู่ในส่วนคำสั่ง WHERE จริง

    เมื่อ SELECT อยู่ในกลุ่มที่มีขนาดใหญ่กว่า (เลือกน้อยกว่า) และอาจมีค่าอื่น ๆ ที่ไม่ได้จัดทำดัชนีไว้ดัชนีที่มีคอลัมน์เลือกน้อยอาจยังคงมีประโยชน์ (ถ้ามีเหตุผลที่จะไม่สร้างใหม่)

    หากมีตารางที่อยู่ด้วย

    ถนนในเมืองประเทศอย่างอื่น ...

    การจัดทำดัชนีถนนเมืองประเทศจะให้ผลการสืบค้นที่เร็วที่สุดด้วยชื่อถนน แต่การสอบถามถนนทุกสายในเมืองดัชนีจะไร้ประโยชน์และแบบสอบถามน่าจะทำการสแกนแบบเต็มตาราง

    การทำดัชนีประเทศ, เมือง, ถนนอาจช้าลงเล็กน้อยสำหรับแต่ละถนน แต่ดัชนีสามารถใช้สำหรับการค้นหาอื่น ๆ ได้เพียงเลือกตามประเทศและ / หรือเมือง


    3

    เมื่อเลือกลำดับคอลัมน์ดัชนีข้อกังวลที่สำคัญคือ:

    จะมี (ความเสมอภาค) เปรียบเทียบกับคอลัมน์นี้ในข้อความค้นหาของฉันหรือไม่?

    หากคอลัมน์ไม่เคยปรากฏในส่วนคำสั่งที่ไหนก็ไม่คุ้มค่าการจัดทำดัชนี (1)

    ตกลงดังนั้นคุณจะมีตารางและคิวรีสำหรับแต่ละคอลัมน์ บางครั้งมากกว่าหนึ่ง

    คุณตัดสินใจเลือกสิ่งที่จะจัดทำดัชนีอย่างไร

    ลองดูตัวอย่าง นี่คือตารางที่มีสามคอลัมน์ หนึ่งค่ามี 10 ค่าอีก 1,000 ค่าล่าสุด 10,000:

    create table t(
      few_vals  varchar2(10),
      many_vals varchar2(10),
      lots_vals varchar2(10)
    );
    
    insert into t 
    with rws as (
      select lpad(mod(rownum, 10), 10, '0'), 
             lpad(mod(rownum, 1000), 10, '0'), 
             lpad(rownum, 10, '0') 
      from dual connect by level <= 10000
    )
      select * from rws;
    
    commit;
    
    select count(distinct few_vals),
           count(distinct many_vals) ,
           count(distinct lots_vals) 
    from   t;
    
    COUNT(DISTINCTFEW_VALS)  COUNT(DISTINCTMANY_VALS)  COUNT(DISTINCTLOTS_VALS)  
    10                       1,000                     10,000     

    ตัวเลขเหล่านี้จะถูกเติมด้วยเลขศูนย์ สิ่งนี้จะช่วยให้ประเด็นเกี่ยวกับการบีบอัดในภายหลัง

    ดังนั้นคุณจึงมีคำถามทั่วไปสามข้อ:

    select count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
    from   t
    where  few_vals = '0000000001';
    
    select count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
    from   t
    where  lots_vals = '0000000001';
    
    select count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
    from   t
    where  lots_vals = '0000000001'
    and    few_vals = '0000000001';

    คุณทำดัชนีอะไร

    ดัชนีเพียงเล็กน้อย __ จะดีกว่าการสแกนแบบเต็มตาราง:

    select count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
    from   t
    where  few_vals = '0000000001';
    
    select * 
    from table(dbms_xplan.display_cursor(null, null, 'IOSTATS LAST -PREDICATE'));
    
    -------------------------------------------------------------------------------------------  
    | Id  | Operation            | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  
    -------------------------------------------------------------------------------------------  
    |   0 | SELECT STATEMENT     |          |      1 |        |      1 |00:00:00.01 |      61 |  
    |   1 |  SORT AGGREGATE      |          |      1 |      1 |      1 |00:00:00.01 |      61 |  
    |   2 |   VIEW               | VW_DAG_0 |      1 |   1000 |   1000 |00:00:00.01 |      61 |  
    |   3 |    HASH GROUP BY     |          |      1 |   1000 |   1000 |00:00:00.01 |      61 |  
    |   4 |     TABLE ACCESS FULL| T        |      1 |   1000 |   1000 |00:00:00.01 |      61 |  
    -------------------------------------------------------------------------------------------
    
    select /*+ index (t (few_vals)) */
           count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
    from   t
    where  few_vals = '0000000001';
    
    select * 
    from   table(dbms_xplan.display_cursor(null, null, 'IOSTATS LAST -PREDICATE'));
    
    -------------------------------------------------------------------------------------------------------------  
    | Id  | Operation                              | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  
    -------------------------------------------------------------------------------------------------------------  
    |   0 | SELECT STATEMENT                       |          |      1 |        |      1 |00:00:00.01 |      58 |  
    |   1 |  SORT AGGREGATE                        |          |      1 |      1 |      1 |00:00:00.01 |      58 |  
    |   2 |   VIEW                                 | VW_DAG_0 |      1 |   1000 |   1000 |00:00:00.01 |      58 |  
    |   3 |    HASH GROUP BY                       |          |      1 |   1000 |   1000 |00:00:00.01 |      58 |  
    |   4 |     TABLE ACCESS BY INDEX ROWID BATCHED| T        |      1 |   1000 |   1000 |00:00:00.01 |      58 |  
    |   5 |      INDEX RANGE SCAN                  | FEW      |      1 |   1000 |   1000 |00:00:00.01 |       5 |  
    -------------------------------------------------------------------------------------------------------------

    ดังนั้นจึงไม่น่าจะมีค่าการจัดทำดัชนีด้วยตัวเอง ข้อความค้นหาบน lots_vals คืนค่าสองสามแถว (เพียง 1 ในกรณีนี้) ดังนั้นนี่คือการจัดทำดัชนีที่คุ้มค่าแน่นอน

    แต่สิ่งที่เกี่ยวกับการค้นหากับทั้งสองคอลัมน์?

    คุณควรจัดทำดัชนี:

    ( few_vals, lots_vals )

    หรือ

    ( lots_vals, few_vals )

    คำถามซ่อนเงื่อน!

    คำตอบคือไม่ใช่

    แน่ใจว่าสามมิติเป็นสตริงที่มีความยาว ดังนั้นคุณสามารถบีบอัดได้ดี และคุณ (อาจ) ได้รับการข้ามดัชนีสแกนเพื่อค้นหาโดยใช้ (2-3_vals, lots_vals) ที่มีเพรดิเคตเฉพาะใน lots_vals เท่านั้น แต่ฉันไม่ได้อยู่ที่นี่แม้ว่าจะได้ผลดีกว่าการสแกนเต็มรูปแบบ:

    create index few_lots on t(few_vals, lots_vals);
    
    select count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
    from   t
    where  lots_vals = '0000000001';
    
    select * 
    from   table(dbms_xplan.display_cursor(null, null, 'IOSTATS LAST -PREDICATE'));
    
    -------------------------------------------------------------------------------------------  
    | Id  | Operation            | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  
    -------------------------------------------------------------------------------------------  
    |   0 | SELECT STATEMENT     |          |      1 |        |      1 |00:00:00.01 |      61 |  
    |   1 |  SORT AGGREGATE      |          |      1 |      1 |      1 |00:00:00.01 |      61 |  
    |   2 |   VIEW               | VW_DAG_0 |      1 |      1 |      1 |00:00:00.01 |      61 |  
    |   3 |    HASH GROUP BY     |          |      1 |      1 |      1 |00:00:00.01 |      61 |  
    |   4 |     TABLE ACCESS FULL| T        |      1 |      1 |      1 |00:00:00.01 |      61 |  
    -------------------------------------------------------------------------------------------  
    
    select /*+ index_ss (t few_lots) */count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
    from   t
    where  lots_vals = '0000000001';
    
    select * 
    from   table(dbms_xplan.display_cursor(null, null, 'IOSTATS LAST -PREDICATE'));
    
    ----------------------------------------------------------------------------------------------------------------------  
    | Id  | Operation                              | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Reads  |  
    ----------------------------------------------------------------------------------------------------------------------  
    |   0 | SELECT STATEMENT                       |          |      1 |        |      1 |00:00:00.01 |      13 |     11 |  
    |   1 |  SORT AGGREGATE                        |          |      1 |      1 |      1 |00:00:00.01 |      13 |     11 |  
    |   2 |   VIEW                                 | VW_DAG_0 |      1 |      1 |      1 |00:00:00.01 |      13 |     11 |  
    |   3 |    HASH GROUP BY                       |          |      1 |      1 |      1 |00:00:00.01 |      13 |     11 |  
    |   4 |     TABLE ACCESS BY INDEX ROWID BATCHED| T        |      1 |      1 |      1 |00:00:00.01 |      13 |     11 |  
    |   5 |      INDEX SKIP SCAN                   | FEW_LOTS |      1 |     40 |      1 |00:00:00.01 |      12 |     11 |  
    ----------------------------------------------------------------------------------------------------------------------

    คุณชอบเล่นการพนันหรือไม่? (2)

    ดังนั้นคุณยังต้องการดัชนีที่มี lots_vals เป็นคอลัมน์นำ และอย่างน้อยในกรณีนี้ดัชนีผสม (ไม่กี่ล็อต) จะทำงานในปริมาณเดียวกันกับที่ใช้เพียงแค่ (ล็อต)

    select count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
    from   t
    where  lots_vals = '0000000001'
    and    few_vals = '0000000001';
    
    select * 
    from   table(dbms_xplan.display_cursor(null, null, 'IOSTATS LAST -PREDICATE'));
    
    -------------------------------------------------------------------------------------------------------------  
    | Id  | Operation                              | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  
    -------------------------------------------------------------------------------------------------------------  
    |   0 | SELECT STATEMENT                       |          |      1 |        |      1 |00:00:00.01 |       3 |  
    |   1 |  SORT AGGREGATE                        |          |      1 |      1 |      1 |00:00:00.01 |       3 |  
    |   2 |   VIEW                                 | VW_DAG_0 |      1 |      1 |      1 |00:00:00.01 |       3 |  
    |   3 |    HASH GROUP BY                       |          |      1 |      1 |      1 |00:00:00.01 |       3 |  
    |   4 |     TABLE ACCESS BY INDEX ROWID BATCHED| T        |      1 |      1 |      1 |00:00:00.01 |       3 |  
    |   5 |      INDEX RANGE SCAN                  | FEW_LOTS |      1 |      1 |      1 |00:00:00.01 |       2 |  
    -------------------------------------------------------------------------------------------------------------  
    
    create index lots on t(lots_vals);
    
    select /*+ index (t (lots_vals)) */count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
    from   t
    where  lots_vals = '0000000001'
    and    few_vals = '0000000001';
    
    select * 
    from   table(dbms_xplan.display_cursor(null, null, 'IOSTATS LAST -PREDICATE'));
    
    ----------------------------------------------------------------------------------------------------------------------  
    | Id  | Operation                              | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Reads  |  
    ----------------------------------------------------------------------------------------------------------------------  
    |   0 | SELECT STATEMENT                       |          |      1 |        |      1 |00:00:00.01 |       3 |      1 |  
    |   1 |  SORT AGGREGATE                        |          |      1 |      1 |      1 |00:00:00.01 |       3 |      1 |  
    |   2 |   VIEW                                 | VW_DAG_0 |      1 |      1 |      1 |00:00:00.01 |       3 |      1 |  
    |   3 |    HASH GROUP BY                       |          |      1 |      1 |      1 |00:00:00.01 |       3 |      1 |  
    |   4 |     TABLE ACCESS BY INDEX ROWID BATCHED| T        |      1 |      1 |      1 |00:00:00.01 |       3 |      1 |  
    |   5 |      INDEX RANGE SCAN                  | LOTS     |      1 |      1 |      1 |00:00:00.01 |       2 |      1 |  
    ----------------------------------------------------------------------------------------------------------------------  

    จะมีหลายกรณีที่ดัชนีผสมช่วยคุณประหยัด 1-2 IOs แต่มันก็คุ้มค่าถ้ามีดัชนีสองตัวสำหรับการออมนี้?

    และยังมีปัญหาอีกอย่างกับดัชนีคอมโพสิต เปรียบเทียบปัจจัยการจัดกลุ่มสำหรับดัชนีสามรายการซึ่งรวมถึง LOTS_VALS:

    create index lots on t(lots_vals);
    create index lots_few on t(lots_vals, few_vals);
    create index few_lots on t(few_vals, lots_vals);
    
    select index_name, leaf_blocks, distinct_keys, clustering_factor
    from   user_indexes
    where  table_name = 'T';
    
    INDEX_NAME  LEAF_BLOCKS  DISTINCT_KEYS  CLUSTERING_FACTOR  
    FEW_LOTS    47           10,000         530                
    LOTS_FEW    47           10,000         53                 
    LOTS        31           10,000         53                 
    FEW         31           10             530    

    โปรดสังเกตว่าปัจจัยการจัดกลุ่มสำหรับ few_lots นั้นสูงกว่า10xและ lots_few มากกว่า10 เท่า ! และนี่คือตารางสาธิตที่มีการจัดกลุ่มที่สมบูรณ์แบบเริ่มต้น ในฐานข้อมูลโลกแห่งความเป็นจริงผลกระทบมีแนวโน้มที่จะแย่ลง

    ดังนั้นสิ่งที่ไม่ดีเกี่ยวกับที่?

    ปัจจัยการจัดกลุ่มเป็นหนึ่งในตัวขับเคลื่อนหลักที่กำหนดว่าดัชนี "น่าสนใจ" เป็นอย่างไร ยิ่งมีโอกาสสูงที่เครื่องมือเพิ่มประสิทธิภาพก็จะเลือกน้อยลง โดยเฉพาะอย่างยิ่งถ้า lots_vals ไม่ได้มีลักษณะเฉพาะจริง ๆ แต่โดยปกติแล้วจะมีจำนวนแถวต่อค่าไม่มาก หากคุณโชคร้ายนี่อาจเพียงพอที่จะทำให้เครื่องมือเพิ่มประสิทธิภาพคิดว่าการสแกนแบบเต็มนั้นราคาถูกกว่า ...

    ตกลงดังนั้นดัชนีคอมโพสิตที่มีจำนวนน้อยมากและจำนวนมากจะได้รับสิทธิประโยชน์ขอบ

    สิ่งที่เกี่ยวกับการกรองการกรองสามมิติและหลายช่วง

    ดัชนีคอลัมน์เดี่ยวให้ประโยชน์เพียงเล็กน้อยเท่านั้น แต่เมื่อรวมมันจะคืนค่าน้อย ดังนั้นดัชนีคอมโพสิตจึงเป็นความคิดที่ดี แต่ทางไหนบ้าง

    หากคุณวางไม่กี่อันดับแรกการบีบอัดคอลัมน์นำจะทำให้ขนาดเล็กลง

    create index few_many on t(many_vals, few_vals);
    create index many_few on t(few_vals, many_vals);
    
    select index_name, leaf_blocks, distinct_keys, clustering_factor 
    from   user_indexes
    where  index_name in ('FEW_MANY', 'MANY_FEW');
    
    INDEX_NAME  LEAF_BLOCKS  DISTINCT_KEYS  CLUSTERING_FACTOR  
    FEW_MANY    47           1,000          10,000             
    MANY_FEW    47           1,000          10,000   
    
    alter index few_many rebuild compress 1;
    alter index many_few rebuild compress 1;
    
    select index_name, leaf_blocks, distinct_keys, clustering_factor 
    from   user_indexes
    where  index_name in ('FEW_MANY', 'MANY_FEW');
    
    INDEX_NAME  LEAF_BLOCKS  DISTINCT_KEYS  CLUSTERING_FACTOR  
    MANY_FEW    31           1,000          10,000             
    FEW_MANY    34           1,000          10,000      

    ด้วยค่าที่แตกต่างกันน้อยลงในคอลัมน์นำจะบีบอัดให้ดีขึ้น ดังนั้นจึงมีขอบเขตน้อยกว่าในการอ่านดัชนีนี้ แต่เพียงเล็กน้อยเท่านั้น และทั้งคู่ก็มีขนาดเล็กกว่าเดิมอยู่แล้ว (ลดขนาดลง 25%)

    และคุณสามารถไปต่อและบีบอัดดัชนีทั้งหมด!

    alter index few_many rebuild compress 2;
    alter index many_few rebuild compress 2;
    
    select index_name, leaf_blocks, distinct_keys, clustering_factor 
    from   user_indexes
    where  index_name in ('FEW_MANY', 'MANY_FEW');
    
    INDEX_NAME  LEAF_BLOCKS  DISTINCT_KEYS  CLUSTERING_FACTOR  
    FEW_MANY    20           1,000          10,000             
    MANY_FEW    20           1,000          10,000   

    ตอนนี้ดัชนีทั้งสองกลับมามีขนาดเท่าเดิม หมายเหตุสิ่งนี้ใช้ประโยชน์จากความจริงที่ว่ามีความสัมพันธ์ระหว่างคนไม่มากและคนจำนวนมาก ไม่น่าที่คุณจะเห็นประโยชน์เช่นนี้ในโลกแห่งความเป็นจริงอีกครั้ง

    จนถึงตอนนี้เราได้พูดคุยเกี่ยวกับการตรวจสอบความเท่าเทียมกันเท่านั้น บ่อยครั้งที่มีดัชนีคอมโพสิตคุณจะมีความไม่เท่าเทียมกับหนึ่งในคอลัมน์ เช่นข้อความค้นหาเช่น "รับคำสั่งซื้อ / การจัดส่ง / ใบแจ้งหนี้สำหรับลูกค้าในช่วง N วันที่ผ่านมา"

    หากคุณมีข้อความค้นหาประเภทนี้คุณต้องการความเท่าเทียมกันกับคอลัมน์แรกของดัชนี:

    select count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
    from   t
    where  few_vals < '0000000002'
    and    many_vals = '0000000001';
    
    select * 
    from   table(dbms_xplan.display_cursor(null, null, 'IOSTATS LAST -PREDICATE'));
    
    -------------------------------------------------------------------------------------------------------------  
    | Id  | Operation                              | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  
    -------------------------------------------------------------------------------------------------------------  
    |   0 | SELECT STATEMENT                       |          |      1 |        |      1 |00:00:00.01 |      12 |  
    |   1 |  SORT AGGREGATE                        |          |      1 |      1 |      1 |00:00:00.01 |      12 |  
    |   2 |   VIEW                                 | VW_DAG_0 |      1 |     10 |     10 |00:00:00.01 |      12 |  
    |   3 |    HASH GROUP BY                       |          |      1 |     10 |     10 |00:00:00.01 |      12 |  
    |   4 |     TABLE ACCESS BY INDEX ROWID BATCHED| T        |      1 |     10 |     10 |00:00:00.01 |      12 |  
    |   5 |      INDEX RANGE SCAN                  | FEW_MANY |      1 |     10 |     10 |00:00:00.01 |       2 |  
    -------------------------------------------------------------------------------------------------------------  
    
    select count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
    from   t
    where  few_vals = '0000000001'
    and    many_vals < '0000000002';
    
    select * 
    from   table(dbms_xplan.display_cursor(null, null, 'IOSTATS LAST -PREDICATE'));
    
    ----------------------------------------------------------------------------------------------------------------------  
    | Id  | Operation                              | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Reads  |  
    ----------------------------------------------------------------------------------------------------------------------  
    |   0 | SELECT STATEMENT                       |          |      1 |        |      1 |00:00:00.01 |      12 |      1 |  
    |   1 |  SORT AGGREGATE                        |          |      1 |      1 |      1 |00:00:00.01 |      12 |      1 |  
    |   2 |   VIEW                                 | VW_DAG_0 |      1 |      2 |     10 |00:00:00.01 |      12 |      1 |  
    |   3 |    HASH GROUP BY                       |          |      1 |      2 |     10 |00:00:00.01 |      12 |      1 |  
    |   4 |     TABLE ACCESS BY INDEX ROWID BATCHED| T        |      1 |      2 |     10 |00:00:00.01 |      12 |      1 |  
    |   5 |      INDEX RANGE SCAN                  | MANY_FEW |      1 |      1 |     10 |00:00:00.01 |       2 |      1 |  
    ----------------------------------------------------------------------------------------------------------------------  

    สังเกตว่าพวกเขากำลังใช้ดัชนีตรงกันข้าม

    TL; DR

    • คอลัมน์ที่มีเงื่อนไขความเท่าเทียมกันควรเป็นอันดับแรกในดัชนี
    • หากคุณมีหลายคอลัมน์ที่มีความเสมอภาคในแบบสอบถามของคุณการวางคอลัมน์ที่มีค่าต่างกันน้อยที่สุดก่อนจะให้ประโยชน์ในการบีบอัดที่ดีที่สุด
    • ในขณะที่การสแกนดัชนีข้ามเป็นไปได้คุณต้องมั่นใจว่านี่จะเป็นตัวเลือกที่มีศักยภาพสำหรับอนาคตอันใกล้
    • ดัชนีคอมโพสิตรวมถึงคอลัมน์ใกล้เคียงมีประโยชน์น้อยที่สุด ต้องแน่ใจว่าคุณต้องบันทึก 1-2 IOs จริงๆ

    1: ในบางกรณีอาจมีค่ารวมถึงคอลัมน์ในดัชนีถ้านี่หมายความว่าคอลัมน์ทั้งหมดในแบบสอบถามของคุณอยู่ในดัชนี สิ่งนี้เปิดใช้งานการสแกนดัชนีเท่านั้นดังนั้นคุณไม่จำเป็นต้องเข้าถึงตาราง

    2: หากคุณได้รับใบอนุญาตสำหรับการวินิจฉัยและการปรับแต่งคุณสามารถบังคับให้มีการข้ามการสแกนด้วย SQL Plan Management

    ADDEDNDA

    PS - เอกสารที่คุณยกมามีตั้งแต่ 9i นั่นคืออายุ reeeeeeally ฉันจะยึดติดกับบางสิ่งที่ใหม่กว่า


    คำถามที่select count (distinct few_vals || ':' || many_vals || ':' || lots_vals )พบบ่อยจริงๆหรือไม่ Oracle ไม่อนุญาตให้ใช้ไวยากรณ์select count (distinct few_vals, many_vals, lots_vals )- ซึ่งไม่ได้ทำการต่อสตริงใด ๆ ไม่จำเป็นต้องใช้คอลัมน์เป็นประเภทข้อความและไม่พึ่งพาการไม่มี:อักขระใช่หรือไม่
    ypercubeᵀᴹ

    @ ypercubeᵀᴹคุณไม่สามารถทำได้count ( distinct x, y, z )ใน Oracle ดังนั้นคุณต้องทำแบบสอบถามย่อยที่แตกต่างกันและนับผลลัพธ์หรือการต่อข้อมูลตามที่กล่าวไว้ด้านบน ฉันเพิ่งทำมันที่นี่เพื่อบังคับให้เข้าถึงตาราง (แทนที่จะสแกนดัชนีเท่านั้น) และมีเพียงหนึ่งแถวในผลลัพธ์
    Chris Saxon

    1

    มีองค์ประกอบเพิ่มเติมของแบบสอบถามช่วยในการตัดสินใจขั้นสุดท้ายเกี่ยวกับสิ่งที่ดัชนีคอมโพสิตเริ่มต้นด้วยและ / หรือมีนอกเหนือจากการเลือกของคอลัมน์

    ตัวอย่างเช่น:

    1. ชนิดของตัวดำเนินการแบบสอบถามที่ใช้อยู่: หากแบบสอบถามมีตัวดำเนินการเช่น
      ">,> =, <, <="
    2. จำนวนแถวจริงที่คาดว่าจะเป็นผลมาจากแบบสอบถาม: ผลลัพธ์ของแบบสอบถามจะเป็นส่วนใหญ่ของแถวจากตารางหรือไม่
    3. มีการใช้ฟังก์ชั่นใด ๆ ในคอลัมน์ตารางในระหว่างที่ส่วนคำสั่ง: หากแบบสอบถามมีฟังก์ชันใด ๆ ที่สูงกว่า, ต่ำกว่า, ตัด, ย่อยใช้ในคอลัมน์ที่ใช้ในสภาพที่ใด

    ยังเพื่อให้การสนทนาที่เกี่ยวข้องคำตอบด้านล่างของฉันใช้กับสถานการณ์ต่อไปนี้:

    1. "90% ชนิดของการสืบค้นในตารางที่กำหนดมี WHERE Clause with operator ="
    2. "ที่เคียวรีส่วนใหญ่ส่งคืน 10% ของแถวทั้งหมดในตารางเป็นผลลัพธ์"
    3. "ไม่มีการใช้ฟังก์ชันใด ๆ ในคอลัมน์ตารางในส่วนคำสั่ง WHERE"
    4. "คอลัมน์เวลาส่วนใหญ่ใน WHERE Clause ที่ใช้ส่วนใหญ่จะเป็นชนิดตัวเลข,
      สตริง"

    จากประสบการณ์ของฉันมันเป็นทั้งที่ DBA ควรจะใส่ใจ

    ลองจินตนาการถึงการใช้กฎข้อเดียวเท่านั้น:

    1) ถ้าฉันสร้างดัชนีโดยมีคอลัมน์ที่เลือกมากที่สุดเป็นอันดับแรก แต่คอลัมน์นั้นไม่ได้ใช้จริงโดยแบบสอบถามส่วนใหญ่ในตารางนั้นกว่าจะไม่ใช้สำหรับโปรแกรม db

    2) ถ้าฉันสร้างดัชนีที่มีคอลัมน์ที่ใช้กันอย่างแพร่หลายมากที่สุดในแบบสอบถามที่อยู่ในดัชนีเป็นอันดับแรก แต่คอลัมน์มีการเลือกที่ต่ำกว่าประสิทธิภาพแบบสอบถามของฉันจะไม่ดี

    ฉันจะแสดงรายการคอลัมน์ที่ใช้เป็นส่วนใหญ่ใน 90% ของการสืบค้นตาราง จากนั้นให้ใส่เฉพาะลำดับความสำคัญของหัวใจให้มากที่สุดให้น้อยที่สุด

    เราใช้ดัชนีสำหรับการปรับปรุงประสิทธิภาพการสืบค้นแบบสอบถามและเวิร์กโฟลว์นั้น (ประเภทของแบบสอบถามแบบอ่าน) เท่านั้นควรผลักดันการสร้างดัชนี ในความเป็นจริงเมื่อข้อมูลเพิ่มขึ้น (หลายพันล้านแถว) ดัชนีที่บีบอัดอาจบันทึกที่เก็บข้อมูลได้


    1

    ในทางทฤษฎีแล้วคอลัมน์ที่เลือกมากที่สุดจะทำให้การค้นหาเร็วที่สุด แต่ในที่ทำงานฉันเพิ่งสะดุดกับสถานการณ์ที่เรามีดัชนีคอมโพสิต 3 ส่วนด้วยส่วนที่เลือกมากที่สุดก่อน (วันที่, ผู้แต่ง, บริษัท สำนักพิมพ์จะบอกว่าตามลำดับตารางจะตรวจสอบนิ้วโป้งบนโพสต์) และฉันมีแบบสอบถามที่ใช้ทั้ง 3 ส่วน ค่าเริ่มต้นของ Mysql คือการใช้ดัชนีผู้เขียนออนไลน์โดยข้ามดัชนีคอมโพสิตที่มี บริษัท และวันที่แม้ว่าจะมีอยู่ในแบบสอบถามของฉันก็ตาม ฉันใช้ดัชนีบังคับเพื่อใช้คอมโพสิตและแบบสอบถามจริงรันช้าลง ทำไมถึงเกิดขึ้น? ฉันจะบอกคุณ:

    ฉันเลือกช่วงในวันที่ดังนั้นแม้ว่าวันที่จะถูกเลือกอย่างมากความจริงที่ว่าเราใช้มันสำหรับการสแกนช่วง (แม้ว่าช่วงจะค่อนข้างสั้น 6 เดือนจาก 6 ปีของข้อมูล) ทำให้คอมโพสิตเป็นอันตรายสำหรับ MySQL ในการใช้คอมโพสิตในกรณีพิเศษนั้น mysql ต้องคว้าบทความทั้งหมดที่เขียนมาตั้งแต่ปีใหม่แล้วดำดิ่งลงไปว่าใครเป็นคนเขียนและระบุว่าผู้เขียนไม่ได้เขียนว่าบทความจำนวนมากเมื่อเทียบกับผู้เขียนคนอื่น ๆ .

    ในอีกกรณีหนึ่งแบบสอบถามวิ่งเร็วมากในคอมโพสิตกรณีคือเมื่อผู้เขียนได้รับความนิยมอย่างมหาศาลและเป็นเจ้าของบันทึกส่วนใหญ่เรียงลำดับตามวันที่เหมาะสม แต่ mysql ไม่ได้ตรวจจับกรณีนั้นโดยอัตโนมัติฉันต้องบังคับดัชนี ... คุณรู้ไหมว่ามันแตกต่างกันไป การสแกนแบบช่วงอาจทำให้คอลัมน์เลือกของคุณไร้ประโยชน์ การกระจายของข้อมูลอาจทำให้กรณีที่คอลัมน์มีการเลือกมากกว่าสำหรับระเบียนที่แตกต่างกัน ...

    สิ่งที่ฉันจะทำแตกต่างกันคือเปลี่ยนวันที่ (ซึ่งอีกครั้งในทางทฤษฎีคือการคัดเลือกมากที่สุด) ไปทางขวาเนื่องจากฉันรู้ว่าฉันจะทำการสแกนช่วงที่มันตอนนี้และที่ทำให้แตกต่าง


    1
    หากแบบสอบถามของคุณมีบางสิ่งบางอย่างเช่นWHERE (date BETWEEN @x AND @y) AND (author = @a) AND (publishing company = @p)นั้นดัชนี(author, publishing_company, date)หรือบน(publishing_company, author, date)จะดีกว่าและจะใช้ - โดยไม่บังคับ
    ypercubeᵀᴹ

    -2

    กรณีที่แตกต่างกันสำหรับสถานการณ์ที่แตกต่างกัน รู้เป้าหมายของคุณ จากนั้นสร้างดัชนีของคุณและเรียกใช้อธิบายแผนสำหรับแต่ละแผนและคุณจะได้คำตอบที่ดีที่สุดสำหรับสถานการณ์ของคุณ


    -2

    จากลำดับคอลัมน์ในดัชนีบน Ask Tom:

    ดังนั้นลำดับของคอลัมน์ในดัชนีของคุณขึ้นอยู่กับวิธีเขียนคำถามของคุณ คุณต้องการให้สามารถใช้ดัชนีสำหรับแบบสอบถามจำนวนมากเท่าที่จะทำได้ (เพื่อลดจำนวนดัชนีทั้งหมดที่คุณมี) - ซึ่งจะทำให้ลำดับของคอลัมน์ ไม่มีอะไรอื่น (การเลือกของ a หรือ b ไม่นับรวม)

    ยอมรับว่าเราต้องเรียงลำดับคอลัมน์ตามคำสั่ง แต่คำสั่ง "(การเลือกของ a หรือ b ไม่นับเลย)" ไม่ถูกต้อง) "คอลัมน์ที่เลือกมากที่สุดควรเป็นผู้นำถ้าพอใจกับบทบาทแรก ("ข้อใด")

    โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
    Licensed under cc by-sa 3.0 with attribution required.