sql >> Databáze >  >> RDS >> Mysql

Velmi jednoduchý agregační dotaz AVG() na serveru MySQL trvá směšně dlouho

Aby bylo možné spočítat počet řádků s konkrétním datem, musí MySQL najít tuto hodnotu v indexu (což je docela rychlé, koneckonců k tomu jsou indexy určeny) a poté přečíst následující položky indexu dokud nenajde další datum. V závislosti na datovém typu esi , to bude sumarizovat načtení některých MB dat pro počítání vašich 700 000 řádků. Načtení některých MB nezabere mnoho času (a tato data mohou být dokonce již uložena do mezipaměti ve fondu vyrovnávacích pamětí, v závislosti na tom, jak často index používáte).

Pro výpočet průměru pro sloupec, který není zahrnut v indexu, MySQL opět použije index k nalezení všech řádků pro dané datum (stejně jako dříve). Navíc pro každý řádek, který najde, musí přečíst skutečná data tabulky pro tento řádek, což znamená použít primární klíč k nalezení řádku, přečíst několik bajtů a opakovat to 700 000 krát. Tento "náhodný přístup" je hodně pomalejší než sekvenční čtení v prvním případě. (To se ještě zhoršuje problémem, že „nějaké bajty“ je innodb_page_size (16 KB ve výchozím nastavení), takže možná budete muset přečíst až 700 kB * 16 KB =11 GB, ve srovnání s "nějakým MB" pro count(*); a v závislosti na konfiguraci vaší paměti nemusí být některá z těchto dat uložena do mezipaměti a je třeba je načíst z disku.)

Řešením je zahrnout všechny použité sloupce do indexu ("krycí index"), např. vytvořte index v date, 01 . MySQL pak nepotřebuje přistupovat k samotné tabulce a může pokračovat, podobně jako první metoda, pouhým čtením indexu. Velikost indexu se trochu zvětší, takže MySQL bude muset načíst "několik MB" (a provést avg -operace), ale stále by to mělo být otázkou sekund.

V komentářích jste zmínil, že je potřeba vypočítat průměr přes 24 sloupců. Pokud chcete vypočítat avg pro několik sloupců současně byste potřebovali krycí index na všech z nich, např. date, 01, 02, ..., 24 abyste zabránili přístupu ke stolu. Uvědomte si, že index, který obsahuje všechny sloupce, vyžaduje tolik úložného prostoru jako samotná tabulka (a vytvoření takového indexu bude trvat dlouho), takže může záviset na důležitosti tohoto dotazu, zda stojí za tyto prostředky.

Chcete-li se vyhnout limitu MySQL na 16 sloupců na index , můžete jej rozdělit na dva indexy (a dva dotazy). Vytvořte např. indexy date, 01, .., 12 a date, 13, .., 24 a poté použijte

select * from (select `date`, avg(`01`), ..., avg(`12`) 
               from mytable where `date` = ...) as part1
cross join    (select avg(`13`), ..., avg(`24`) 
               from mytable where `date` = ...) as part2;

Nezapomeňte to dobře zdokumentovat, protože neexistuje žádný zřejmý důvod psát dotaz tímto způsobem, ale mohlo by to stát za to.

Pokud průměrujete pouze jeden sloupec, můžete přidat 24 samostatných indexů (k date, 01 , date, 02 , ...), i když celkově budou vyžadovat ještě více místa, ale mohou být o něco rychlejší (protože jsou jednotlivě menší). Ale fond vyrovnávacích pamětí může stále upřednostňovat úplný index v závislosti na faktorech, jako jsou vzorce využití a konfigurace paměti, takže jej možná budete muset otestovat.

Od date je součástí vašeho primárního klíče, můžete také zvážit změnu primárního klíče na date, esi . Pokud data najdete podle primárního klíče, nebudete potřebovat další krok pro přístup k datům tabulky (protože k tabulce již přistupujete), takže chování bude podobné jako u krycího indexu. Jde však o významnou změnu vaší tabulky a může ovlivnit všechny ostatní dotazy (např. použití esi k vyhledání řádků), takže je třeba to pečlivě zvážit.

Jak jste zmínili, další možností by bylo vytvořit souhrnnou tabulku, kde byste ukládali předem vypočítané hodnoty, zejména pokud nepřidáváte ani neupravujete řádky pro minulá data (nebo je můžete udržovat aktuální pomocí spouštěče).



  1. nahrajte soubor do MySql DB s PHP

  2. Jak vytvořit a vložit objekt JSON pomocí dotazů MySQL?

  3. Skalární funkce ODBC pro datum a čas na serveru SQL (příklady T-SQL)

  4. Porovnání replikačních řešení od Oracle a MySQL