Masalah optimisasi: kunci berkerumun majemuk, kondisi bendera, dan gabungan indeks

11

Tiga tabel:

product: dengan kolom: ( a, g, ...a_lot_more... )

a: PK, clustered
g: bit-column

main: dengan kolom: ( c, f, a, b, ...a_lot_more... )

c: PK, clustered
f: bit-column
(a, b): UQ 

lookup dengan kolom: ( a, b, c, i )

(a, b): PK, clustered
a: FK to product(a)
c: UQ, FK to main(c)
i: bit-column

Saya tidak dapat menemukan indeks yang baik untuk bergabung:

FROM  
    product
  JOIN 
    lookup
      ON  lookup.a = product.a  
  JOIN
    main
      ON  main.c = lookup.c 
WHERE 
      product.g = 1
  AND
      main.f = 1
  AND 
      lookup.i = 1
  AND lookup.b = 17

Saya mencoba indeks penutup product (g, a, ...)dan itu digunakan tetapi tidak dengan hasil yang spektakuler.

Beberapa kombinasi indeks di atas lookupmeja menghasilkan rencana eksekusi dengan gabungan indeks, dengan sedikit peningkatan efisiensi dibandingkan rencana sebelumnya.

Apakah ada kombinasi jelas yang saya lewatkan?

Bisakah desain ulang struktur membantu?

DBMS adalah MySQL 5.5 dan semua tabel menggunakan InnoDB.


Ukuran meja:

product: 67K   ,  g applied:    64K 

main:   420K   ,  f applied:   190K

lookup:  12M   ,  b,i applied:  67K 
ypercubeᵀᴹ
sumber
Coba pindahkan predikat filter ke gabungan dan lihat apakah pengoptimal melakukan sesuatu yang masuk akal dengan itu. Saya telah melihat pengoptimal SQL Server gagal pada itu sebelumnya.
ConcernedOfTunbridgeWells
Sepertinya produk Cartesian karena saya tidak melihat apa-apa BERGABUNG dari tabel produk. Atau apakah saya melewatkan sesuatu ???
RolandoMySQLDBA
@RolandoMySQLDBA: Anda benar. Saya akan memperbaiki kueri.
ypercubeᵀᴹ

Jawaban:

3

Ini menyakitkan saya ...

Saya harus menggunakan tabel temp dengan InnoDB sebelumnya. Muat dengan filter, buat indeks, gabungkan tabel temp ini.

Masalahnya menurut saya adalah jika InnoDB itu hanya memiliki algoritma Nested Join: pengoptimal permintaan RDBMS dewasa memiliki lebih banyak untuk digunakan. Ini didasarkan pada upaya untuk menjalankan beban tipe Gudang Data di InnoDB.

Tabel temp menyeret keseluruhan kompleksitas ke tingkat pengoptimal permintaan MySQL ...

gbn
sumber
Thnx, saya akan coba itu. Jumlah atau baris (setelah kriteria diterapkan tidak terlalu besar, masing-masing 64K, 67K, 190K). Mungkin saya harus mencoba untuk menyingkirkan salah satu dari tiga tabel ( main) dengan mendenormalisasi data lookup?
ypercubeᵀᴹ
1
@ ypercube: denormalising akan membuat baris lebih lebar, kepadatan halaman lebih rendah = masalah lain
gbn
3

Itu terlihat seperti produk Cartesian. Ulangi Kriteria GABUNG

FROM  
    product
  JOIN 
    lookup
      ON  product.a = lookup.a  
  JOIN
    main
      ON  main.c = lookup.c 
WHERE 
      product.g = 1
  AND
      main.f = 1
  AND 
      lookup.i = 1
  AND lookup.b = 17

ALTERNASI SARAN

Ini mungkin tampak tidak lazim dan mungkin berbau seperti SQL Anitpattern, tapi ini dia ...

FROM  
    product
JOIN 
    (
        SELECT * FROM lookup
        WHERE i=1 AND b=17
    ) lookup ON product.a = lookup.a  
JOIN
   main ON main.c = lookup.c 
WHERE 
    product.g = 1 AND main.f = 1

Saya tidak memindahkan product.g = 1dan main.f = 1ke subqueries karena mereka adalah bidang bit dan hanya akan melakukan pemindaian tabel pada saat itu. Bahkan jika bidang bit adalah indeks, Pengoptimal Permintaan akan mengabaikan indeks seperti itu.

Tentu saja, Anda bisa berubah SELECT * FROM lookupmenjadi SELECT a FROM lookupjika SELECT Anda tidak membutuhkan apa pun darinyalookup

Mungkin melibatkan a, b dalam GABUNGAN antara pencarian dan main jika ini masuk akal

FROM  
    product
  JOIN 
    lookup
      ON  product.a = lookup.a  
  JOIN
    main
      ON  main.a = lookup.a AND main.b = lookup.b
WHERE 
      product.g = 1
  AND
      main.f = 1
  AND 
      lookup.i = 1
  AND lookup.b = 17

atau masukkan kembali c dan gabung di tiga kolom (Indeks pada tiga kolom di maindan lookup)

FROM  
    product
  JOIN 
    lookup
      ON  product.a = lookup.a  
  JOIN
    main
      ON main.a = lookup.a
      AND main.b = lookup.b
      AND main.c = lookup.c
WHERE 
      product.g = 1
  AND
      main.f = 1
  AND 
      lookup.i = 1
  AND lookup.b = 17
RolandoMySQLDBA
sumber
Thnx. Paket EXPLAIN berbeda, tetapi kinerjanya serupa.
ypercubeᵀᴹ
Apa kardinalitas main.fdan product.g??? Jika kardinalitas main.fdan product.guntuk nilainya adalah 1 kurang dari 5% dari baris tabel, indeks aktif main.fdan product.gdapat dibenarkan.
RolandoMySQLDBA
Sudahlah, mereka sudah diindeks. Jika kardinalitas dari main.fdan product.g2, Anda bisa membuang indeks tersebut.
RolandoMySQLDBA
Mengedit pertanyaan dengan ukuran dan baris tabel yang digunakan (setelah ketentuan diterapkan).
ypercubeᵀᴹ
Saya memperbarui pertanyaan saya, saran BERGABUNG di a, b bukannya c. Lihat apakah itu membuat rencana EXPLAIN yang berbeda
RolandoMySQLDBA