Database
 sql >> Teknologi Basis Data >  >> RDS >> Database

Estimasi Kardinalitas:Menggabungkan Statistik Kepadatan

Artikel ini menunjukkan bagaimana SQL Server menggabungkan informasi kepadatan dari beberapa statistik kolom tunggal, untuk menghasilkan perkiraan kardinalitas untuk agregasi pada beberapa kolom. Detailnya mudah-mudahan menarik dalam diri mereka. Mereka juga memberikan wawasan tentang beberapa pendekatan umum dan algoritme yang digunakan oleh penduga kardinalitas.

Pertimbangkan contoh kueri database AdventureWorks berikut, yang mencantumkan jumlah item inventaris produk di setiap nampan di setiap rak di gudang:

SELECT 
    INV.Shelf, 
    INV.Bin, 
    COUNT_BIG(*)
FROM Production.ProductInventory AS INV
GROUP BY 
    INV.Shelf,
    INV.Bin
ORDER BY 
    INV.Shelf,
    INV.Bin;

Perkiraan rencana eksekusi menunjukkan 1.069 baris sedang dibaca dari tabel, diurutkan ke dalam Shelf dan Bin pesanan, lalu digabungkan menggunakan operator Agregat Aliran:

Perkiraan Rencana Eksekusi

Pertanyaannya adalah, bagaimana pengoptimal kueri SQL Server sampai pada perkiraan akhir 744 baris?

Statistik yang Tersedia

Saat mengkompilasi kueri di atas, pengoptimal kueri akan membuat statistik satu kolom di Shelf dan Bin kolom, jika statistik yang sesuai belum ada. Antara lain, statistik ini memberikan informasi tentang jumlah nilai kolom yang berbeda (dalam vektor kepadatan):

DBCC SHOW_STATISTICS 
(
    [Production.ProductInventory], 
    [Shelf]
)
WITH DENSITY_VECTOR;
 
DBCC SHOW_STATISTICS 
(
    [Production.ProductInventory], 
    [Bin]
)
WITH DENSITY_VECTOR;

Hasilnya dirangkum dalam tabel di bawah ini (kolom ketiga dihitung dari kepadatan):

Kolom Kepadatan 1 / Kepadatan
Rak 0,04761905 21
Sampah 0.01612903 62

Informasi vektor kepadatan Rak dan Bak

Sebagai catatan dokumentasi, kebalikan dari kepadatan adalah jumlah nilai yang berbeda dalam kolom. Dari informasi statistik yang ditunjukkan di atas, SQL Server mengetahui bahwa ada 21 Shelf distinct yang berbeda nilai dan 62 Bin distinct yang berbeda nilai dalam tabel, saat statistik dikumpulkan.

Tugas memperkirakan jumlah baris yang dihasilkan oleh GROUP BY klausa sepele ketika hanya satu kolom yang terlibat (dengan asumsi tidak ada predikat lain). Misalnya, mudah untuk melihat bahwa GROUP BY Shelf akan menghasilkan 21 baris; GROUP BY Bin akan menghasilkan 62.

Namun, tidak segera jelas bagaimana SQL Server dapat memperkirakan jumlah (Shelf, Bin) yang berbeda kombinasi untuk GROUP BY Shelf, Bin kami pertanyaan. Untuk mengajukan pertanyaan dengan cara yang sedikit berbeda:Diberikan 21 rak dan 62 tempat sampah, berapa banyak kombinasi rak dan tempat sampah unik yang akan ada? Mengesampingkan aspek fisik dan pengetahuan manusia lainnya tentang domain masalah, jawabannya bisa di mana saja dari maks (21, 62) =62 hingga (21 * 62) =1,302. Tanpa informasi lebih lanjut, tidak ada cara yang jelas untuk mengetahui di mana harus memberikan perkiraan dalam kisaran tersebut.

Namun, untuk kueri contoh kami, SQL Server memperkirakan 744.312 baris (dibulatkan menjadi 744 dalam tampilan Plan Explorer) tetapi atas dasar apa?

Peristiwa Perpanjangan Estimasi Kardinalitas

Cara terdokumentasi untuk melihat proses estimasi kardinalitas adalah dengan menggunakan Peristiwa Diperpanjang query_optimizer_estimate_cardinality (meskipun berada di saluran "debug"). Saat sesi yang mengumpulkan acara ini berjalan, operator rencana eksekusi mendapatkan properti tambahan StatsCollectionId yang menghubungkan perkiraan operator individu dengan perhitungan yang menghasilkannya. Untuk contoh kueri kami, pengumpulan statistik id 2 ditautkan ke perkiraan kardinalitas untuk grup menurut operator agregat.

Output yang relevan dari Extended Event untuk kueri pengujian kami adalah:

<data name="calculator">
    <type name="xml" package="package0"></type>
    <value>
    <CalculatorList>
        <DistinctCountCalculator CalculatorName="CDVCPlanLeaf" SingleColumnStat="Shelf,Bin" />
    </CalculatorList>
    </value>
</data>
<data name="stats_collection">
    <type name="xml" package="package0"></type>
    <value>
    <StatsCollection Name="CStCollGroupBy" Id="2" Card="744.31">
        <LoadedStats>
        <StatsInfo DbId="6" ObjectId="258099960" StatsId="3" />
        <StatsInfo DbId="6" ObjectId="258099960" StatsId="4" />
        </LoadedStats>
    </StatsCollection>
    </value>
</data>

Pasti ada beberapa informasi berguna di sana.

Kita dapat melihat bahwa plan meninggalkan kelas kalkulator nilai yang berbeda (CDVCPlanLeaf ) digunakan, menggunakan statistik kolom tunggal pada Shelf dan Bin sebagai masukan. Elemen kumpulan statistik mencocokkan fragmen ini dengan id (2) yang ditampilkan dalam rencana eksekusi, yang menunjukkan perkiraan kardinalitas 744,31 , dan informasi lebih lanjut tentang id objek statistik yang digunakan juga.

Sayangnya, tidak ada dalam output acara yang mengatakan dengan tepat bagaimana kalkulator sampai pada angka akhir, yang merupakan hal yang sangat kami minati.

Menggabungkan jumlah yang berbeda

Pergi ke rute yang kurang terdokumentasi, kami dapat meminta perkiraan rencana untuk kueri dengan tanda jejak 2363 dan 3604 diaktifkan:

SELECT 
    INV.Shelf, 
    INV.Bin, 
    COUNT_BIG(*)
FROM Production.ProductInventory AS INV
GROUP BY 
    INV.Shelf,
    INV.Bin
ORDER BY 
    INV.Shelf,
    INV.Bin
OPTION (QUERYTRACEON 3604, QUERYTRACEON 2363);

Ini mengembalikan informasi debug ke tab Pesan di SQL Server Management Studio. Bagian yang menarik direproduksi di bawah ini:

Begin distinct values computation
Input tree:
  LogOp_GbAgg OUT(QCOL: [INV].Shelf,QCOL: [INV].Bin,COL: Expr1001 ,) BY(QCOL: [INV].Shelf,QCOL: [INV].Bin,)
      CStCollBaseTable(ID=1, CARD=1069 TBL: Production.ProductInventory AS TBL: INV)
      AncOp_PrjList 
          AncOp_PrjEl COL: Expr1001 
              ScaOp_AggFunc stopCountBig
                  ScaOp_Const TI(int,ML=4) XVAR(int,Not Owned,Value=0)

Plan for computation:
  CDVCPlanLeaf
      0 Multi-Column Stats, 2 Single-Column Stats, 0 Guesses

Loaded histogram for column QCOL: [INV].Shelf from stats with id 3
Loaded histogram for column QCOL: [INV].Bin from stats with id 4

Using ambient cardinality 1069 to combine distinct counts:
  21
  62

Combined distinct count: 744.312
Result of computation: 744.312

Stats collection generated: 
  CStCollGroupBy(ID=2, CARD=744.312)
      CStCollBaseTable(ID=1, CARD=1069 TBL: Production.ProductInventory AS TBL: INV)

End distinct values computation

Ini menunjukkan banyak informasi yang sama seperti yang dilakukan Acara yang Diperpanjang dalam format yang (bisa dibilang) lebih mudah digunakan:

  • Operator relasional input untuk menghitung perkiraan kardinalitas untuk (LogOp_GbAgg – kelompok logis berdasarkan agregat)
  • Kalkulator yang digunakan (CDVCPlanLeaf ) dan memasukkan statistik
  • Detail pengumpulan statistik yang dihasilkan

Informasi baru yang menarik adalah bagian tentang menggunakan kardinalitas ambient untuk menggabungkan hitungan yang berbeda .
Ini dengan jelas menunjukkan bahwa nilai 21, 62, dan 1069 digunakan, tetapi (dengan frustrasi) masih belum tepat perhitungan mana yang dilakukan untuk sampai pada 744.312 hasil.

Untuk Sang Debugger!

Melampirkan debugger dan menggunakan simbol publik memungkinkan kita untuk menjelajahi secara detail jalur kode yang diikuti saat mengompilasi kueri contoh.
Snapshot di bawah ini menunjukkan bagian atas tumpukan panggilan pada titik representatif dalam proses:

MSVCR120!log
sqllang!OdblNHlogN
sqllang!CCardUtilSQL12::ProbSampleWithoutReplacement
sqllang!CCardUtilSQL12::CardDistinctMunged
sqllang!CCardUtilSQL12::CardDistinctCombined
sqllang!CStCollAbstractLeaf::CardDistinctImpl
sqllang!IStatsCollection::CardDistinct
sqllang!CCardUtilSQL12::CardGroupByHelperCore
sqllang!CCardUtilSQL12::PstcollGroupByHelper
sqllang!CLogOp_GbAgg::PstcollDeriveCardinality
sqllang!CCardFrameworkSQL12::DeriveCardinalityProperties

Ada beberapa detail menarik di sini. Bekerja dari bawah ke atas, kita melihat bahwa kardinalitas diturunkan menggunakan CE yang diperbarui (CCardFrameworkSQL12 ) tersedia di SQL Server 2014 dan yang lebih baru (CE asli adalah CCardFrameworkSQL7 ), untuk grup menurut operator logika agregat (CLogOp_GbAgg ).

Menghitung kardinalitas yang berbeda melibatkan penggabungan (munging) beberapa input, menggunakan pengambilan sampel tanpa penggantian.

Referensi ke H dan logaritma (alami) pada metode kedua dari atas menunjukkan penggunaan Shannon Entropy dalam perhitungan:

Entropi Shannon

Entropi dapat digunakan untuk memperkirakan korelasi informasional (informasi timbal balik) antara dua statistik:

Informasi Bersama

Menyatukan semua ini, kita dapat membuat skrip perhitungan T-SQL yang cocok dengan cara SQL Server menggunakan pengambilan sampel tanpa penggantian, Shannon Entropy, dan informasi bersama untuk menghasilkan perkiraan kardinalitas akhir.

Kita mulai dengan angka input (kardinalitas ambient dan jumlah nilai yang berbeda di setiap kolom):

DECLARE 
    @Card float = 1069,
    @Distinct1 float = 21,
    @Distinct2 float = 62;

Frekuensi dari setiap kolom adalah jumlah rata-rata baris per nilai yang berbeda:

DECLARE
    @Frequency1 float = @Card / @Distinct1,
    @Frequency2 float = @Card / @Distinct2;

Pengambilan sampel tanpa penggantian (SWR) adalah masalah sederhana dengan mengurangi jumlah rata-rata baris per nilai (frekuensi) yang berbeda dari jumlah total baris:

DECLARE
    @SWR1 float = @Card - @Frequency1,
    @SWR2 float = @Card - @Frequency2,
    @SWR3 float = @Card - @Frequency1 - @Frequency2;

Hitung entropi (N log N) dan informasi timbal balik:

DECLARE
    @E1 float = (@SWR1 + 0.5) * LOG(@SWR1),
    @E2 float = (@SWR2 + 0.5) * LOG(@SWR2),
    @E3 float = (@SWR3 + 0.5) * LOG(@SWR3),
    @E4 float = (@Card + 0.5) * LOG(@Card);
 
-- Using logarithms allows us to express
-- multiplication as addition and division as subtraction
DECLARE
    @MI float = EXP(@E1 + @E2 - @E3 - @E4);

Sekarang kita telah memperkirakan seberapa berkorelasi dua set statistik, kita dapat menghitung perkiraan akhir:

SELECT (1e0 - @MI) * @Distinct1 * @Distinct2;

Hasil perhitungannya adalah 744.311823994677, yaitu 744.312 dibulatkan menjadi tiga tempat desimal.

Untuk kenyamanan, berikut adalah seluruh kode dalam satu blok:

DECLARE 
    @Card float = 1069,
    @Distinct1 float = 21,
    @Distinct2 float = 62;
 
DECLARE
    @Frequency1 float = @Card / @Distinct1,
    @Frequency2 float = @Card / @Distinct2;
 
-- Sample without replacement
DECLARE
    @SWR1 float = @Card - @Frequency1,
    @SWR2 float = @Card - @Frequency2,
    @SWR3 float = @Card - @Frequency1 - @Frequency2;
 
-- Entropy
DECLARE
    @E1 float = (@SWR1 + 0.5) * LOG(@SWR1),
    @E2 float = (@SWR2 + 0.5) * LOG(@SWR2),
    @E3 float = (@SWR3 + 0.5) * LOG(@SWR3),
    @E4 float = (@Card + 0.5) * LOG(@Card);
 
-- Mutual information
DECLARE
    @MI float = EXP(@E1 + @E2 - @E3 - @E4);
 
-- Final estimate
SELECT (1e0 - @MI) * @Distinct1 * @Distinct2;

Pemikiran Terakhir

Perkiraan akhir tidak sempurna dalam kasus ini – kueri contoh sebenarnya mengembalikan 441 baris.

Untuk mendapatkan perkiraan yang lebih baik, kami dapat memberikan informasi yang lebih baik kepada pengoptimal tentang kepadatan Bin dan Shelf kolom menggunakan statistik multikolom. Misalnya:

CREATE STATISTICS stat_Shelf_Bin 
ON Production.ProductInventory (Shelf, Bin);

Dengan statistik itu (baik seperti yang diberikan, atau sebagai efek samping dari penambahan indeks multikolom yang serupa), perkiraan kardinalitas untuk kueri contoh tepat. Jarang sekali menghitung agregasi sederhana seperti itu. Dengan predikat tambahan, statistik multicolumn mungkin kurang efektif. Namun demikian, penting untuk diingat bahwa informasi kepadatan tambahan yang disediakan oleh statistik multikolom dapat berguna untuk agregasi (serta perbandingan kesetaraan).

Tanpa statistik multikolom, kueri agregat dengan predikat tambahan mungkin masih dapat menggunakan logika penting yang ditampilkan dalam artikel ini. Misalnya, alih-alih menerapkan rumus ke kardinalitas tabel, rumus ini dapat diterapkan ke histogram masukan langkah demi langkah.

Konten terkait:Estimasi Kardinalitas untuk Predikat pada COUNT Ekspresi


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Pemantauan Kinerja untuk TimescaleDB

  2. Tugas Asinkron Dengan Django dan Seledri

  3. Mengapa Statistik Tunggu Saja Tidak Cukup

  4. APPEND_ONLY_STORAGE_INSERT_POINT Kait

  5. Memigrasikan Cluster Cassandra Anda