Testování rychlosti dotazů v MySQL 5.1
V příspěvku jsou popsány základní možnosti, jak zjišťovat rychlost a efektivitu různých SQL dotazů, za účelem výběru nejoptimálnějšího řešení.
Nejdříve něco málo o testování rychlosti dotazů v MySQL. Rychlost dotazů testujeme nejčastěji u sestavování složitějších dotazů, často doprovázené spojováním více tabulek. Pomocí PhpMyAdminu nebo konzole můžeme vytvářet libovolné dotazy a rychlost jejich zpracování je po provedení uvedena.
Musím hned zpočátku upozornit, abyste si předem velmi rozmysleli, jaký dotaz provádíte, pokud to není na vašem stroji, můžete nešikovným dotazem SQL server pěkně potrápit. V případě pochybností nebo i preventivně doporučuji použít na dotaz omezení pomocí LIMIT. To většinou zabrzdí vykonávání dotazu včas.
Při testování rychlosti dotazů často narážíme na první problém - využívání cache. Pokud podobný dotaz provádíme vícekrát nebo jsou data v paměti SQL serveru, výsledky jsou k dispozici rychleji a o složitosti dotazu se mnoho nedozvíme. Zakázat využití cache paměti lze doplněním klíčového slova SQL_NO_CACHE ihned za první SELECT. Tím bychom měli přemluvit server, aby nepoužíval cache.
SELECT SQL_NO_CACHE *
FROM `produkty`
WHERE price > 1000
ORDER BY `name` ASC
limit 10;
Pro pochopení, jak zpracovává složitější dotaz SQL server, lze využívat příkaz EXPLAIN, který se jednoduše přidá před zkoumaný dotaz.
EXPLAIN SELECT *
FROM `produkty`
WHERE price > 1000
ORDER BY `name` ASC
limit 10;
Pokud ale hledáme ideální řešení a vybíráme mezi více dotazy, které se liší strukturou, ale vracejí stejná data, příkaz EXPLAIN nám toho moc neřekne, místo něho je lepší použít proměnou last_query_cost. Optimalizátor tuto hodnotu nastaví vždy při vykonání nějakého dotazu v daném sezení a obsahuje nějaké číslo. Čím větší číslo, tím složitější dotaz.
SELECT *
FROM `produkty`
WHERE price > 1000
ORDER BY `name` ASC
limit 10;
SHOW SESSION STATUS WHERE Variable_name LIKE 'last_query_cost';
Samozřejmě mi nedá abych nepřipomenul, že velkou práci databázovému serveru ušetříte správným používáním indexů. Nakonec přikládám malý prográmek v Pythonu 3, který generuje náhodné hodnoty do tabulek produkty a atributy. Definice tabulek je následující:
CREATE TABLE IF NOT EXISTS `atributy` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`produkt` int(11) NOT NULL,
`type` int(11) NOT NULL,
`value_number` int(11) DEFAULT NULL,
`value_text` text COLLATE utf8_czech_ci,
`value_datetime` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `produkt` (`produkt`,`type`)
) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `produkty` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(200) COLLATE utf8_czech_ci NOT NULL,
`price` decimal(8,2) NOT NULL,
`active` int(11) NOT NULL,
`text` text COLLATE utf8_czech_ci NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
A slíbený program je zde:
import random
alphabet = 'abcdefghijklmnopqrstuvwxz '
def getString(mn, mx):
"""
Generate random string with the length between mn and mx.
"""
s = ''
for i in range(random.randint(mn, mx)):
s += alphabet[random.randint(0, len(alphabet) -1)]
return s
# generates data for table produkty
for i in range(1000):
print('INSERT INTO `produkty` (`id`, `name`, `price`, `active`, `text`) '
+ 'VALUES (NULL, \'%s\', \'%s\', %s, \'%s\');'
% (getString(3, 20), str(random.randint(10, 1000)),
str(random.randint(0, 1)), getString(50, 500)))
# generates data for table atributy
for i in range(1000):
for j in range(5):
print('INSERT INTO `atributy` '
+ '(`id`, `produkt`, `type`, `value_number`, `value_text`, `value_datetime`) '
+ 'VALUES (NULL, \'%s\', \'%s\', %s, \'NULL\', \'NULL\');'
% (str(i), '2', str(random.randint(10, 1000))))
print('INSERT INTO `atributy` '
+ '(`id`, `produkt`, `type`, `value_number`, `value_text`, `value_datetime`) '
+ 'VALUES (NULL, \'%s\', \'%s\', \'NULL\', \'%s\', \'NULL\');'
% (str(i), '3', getString(10, 1000)))
print('INSERT INTO `atributy` '
+ '(`id`, `produkt`, `type`, `value_number`, `value_text`, `value_datetime`) '
+ 'VALUES (NULL, \'%s\', \'%s\', \'NULL\', \'NULL\', \'%s\');'
% (str(i), '4', ('2009-' + str(random.randint(1, 12)) + '-'
+ str(random.randint(1, 28)) + ' 19:08:23')))
Instalace mod_python 3.3.1 (21.08.2008)




