Проблема оптимізації: складені кластеризовані ключі, умови прапора та об'єднання індексів


11

Три таблиці:

product: зі стовпцями: ( a, g, ...a_lot_more... )

a: PK, clustered
g: bit-column

main: зі стовпцями: ( c, f, a, b, ...a_lot_more... )

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

lookup зі стовпцями: ( a, b, c, i )

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

Я не можу знайти хороші індекси для приєднання:

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

Я спробував індекс покриття, product (g, a, ...)і він використовується, але не з вражаючими результатами.

Деякі комбінації індексів lookupтаблиці створюють плани виконання з об'єднанням індексів з невеликим підвищенням ефективності порівняно з попереднім планом.

Чи є якась очевидна комбінація, яку мені не вистачає?

Чи може допомогти переконструкція структури?

СУБД є MySQL 5.5, і всі таблиці використовують InnoDB.


Розміри столу:

product: 67K   ,  g applied:    64K 

main:   420K   ,  f applied:   190K

lookup:  12M   ,  b,i applied:  67K 

Спробуйте перемістити предикати фільтрів до об'єднань і подивіться, чи оптимізатор робить щось розумне з цим. Раніше я бачив, що оптимізатор SQL-сервера вийшов з ладу.
ЗанепокоєнийOfTunbridgeWells

Виглядає як декартовий продукт, тому що я не бачу нічого приєднання до таблиці продуктів. Або я щось пропустив ???
RolandoMySQLDBA

@RolandoMySQLDBA: Ви праві. Я виправлю запит.
ypercubeᵀᴹ

Відповіді:


3

Це мене болить ...

Мені раніше довелося використовувати темп таблиці з InnoDB. Завантажте їх фільтрами, створіть індекс, приєднайтеся до цих темп-таблиць.

Я вважаю, що проблема InnoDB має лише алгоритм Nested Join: дорослі оптимізатори запитів RDBMS мають більше використовувати. Це засновано на спробі запустити навантаження типу сховища даних на InnoDB.

Тимчасові таблиці перетягують загальну складність на рівень оптимізатора запитів MySQL ...


Thnx, я спробую це. Кількість або рядки (після застосування критеріїв не такі великі, 64K, 67K, 190K відповідно). Можливо, я повинен спробувати позбутися однієї з трьох таблиць ( main), денормалізуючи дані lookup?
ypercubeᵀᴹ

1
@ypercube: денормалізація зробить рядки ширшими, нижча щільність сторінки = інші проблеми
gbn

3

Це схоже на декартовий продукт. Повторити критерії ПРИЄДНАННЯ

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

АЛЬТЕРНАТИВНА ПРОГРАМА

Це може здатися неортодоксальним і, ймовірно, пахне SQL Anitpattern, але ось це іде ...

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

Я не перемістив product.g = 1і main.f = 1в підзапити, тому що вони є бітовими полями і просто проведуть сканування таблиці в точці. Навіть якби бітові поля були індексами, Оптимізатор запитів просто ігнорував би такий індекс.

Звичайно, ви могли б змінити , SELECT * FROM lookupщоб , SELECT a FROM lookupякщо ваш ВИБРАТИ не потрібно нічого відlookup

Можливо, включіть a, b у ПРИЄДНАЙТЕ між пошуком і main, якщо це має сенс

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

або поставити назад c і приєднатись до трьох стовпців (покажчик на три стовпці в mainі 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

Thnx. Інший план ПОЯСНЕННЯ, але схожа ефективність.
ypercubeᵀᴹ

Яка кардинальність main.fта product.g??? Якщо потужність main.fі product.gдля значення 1 становить менше 5% рядків таблиці, індекс на main.fі product.gможе бути виправданим.
RolandoMySQLDBA

Неважливо, вони вже індексовані. Якщо кардинальність main.fі product.gдорівнює 2, ви можете скинути ці індекси.
RolandoMySQLDBA

Відредагував питання з розмірами таблиці та використаними рядками (після застосування умов).
ypercubeᵀᴹ

Я оновив своє запитання, пропозиція ПРИЄДНАЙТЕСЬ на a, b замість c. Подивіться, чи це робить інший план
ПОЯСНЕННЯ
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.