Autor Zpráva
Foi
Profil
Mám dotaz:
SELECT * FROM statuses AS `s` WHERE (s.account_id IN(SELECT `f`.`follows_id` AS `f_follows_id` FROM followers AS `f` WHERE f.account_id = '75') OR s.account_id = '75') ORDER BY s.created DESC LIMIT 0, 10

A indexy u created, zkoušel jsem i dát (created, account_id), ale výsledek je stále 15 ms. Tabulka statuses má jen 2 500 položek a tabulka followers má jen 5 500. Zdá se mi to přehnaný čas na to kolik to má záznamů.


Explain dotazu tady:

Další jednoduchý update trvá 10ms UPDATE accounts SET last_visit = '2019-08-05 14:02:36' WHERE id = '75' je to normální?
Tomášeek
Profil
Foi:
A co takhle pracovat s číslem jako s číslem?

Tedy místo '75' používat 75. Nepomůže?
Foi
Profil
Nene, výsledky časů jsou stejné :/
ttttt
Profil *
Foi:
Další jednoduchý update trvá 10ms. Je to normální?
Jo, myslím, že je. Zkusil jsem UPDATE nad tabulkou s pěti záznamy na PC s HDD (ne SSD) a dostávám časy okolo 40 - 50 ms. Postgres na serveru 1,5 ms. Tady má někdo podobný dotaz na 55 000 záznamech a čas 120 ms. 10 ms není čas, u kterého bych si řekl, že musí být nutně něco špatně, záleží s jakým nastavením a na čem to spouštíš.
Tomášeek
Profil
ttttt:
No, ono 10ms na tak triviálním dotazu je docela dost.

Vezmi si, že máš e-shop, kde se produkty nedají natáhnout jedním dotazem (parametry, varianty, ...), máš vícero jazykových mutací (dotaz do slovníku), samozřejmě kategorie, hodnocení produktů, a milion dalších věcí, které se běžně načítají... Pak 10ms na takto jednoduchý dotaz je skutečně dost.

Ale je fakt, že nevíme, na čem to Foi pouští a může to být jen pomalým strojem.
Keeehi
Profil
On je nějaký minimální čas, který zabere režije okolo spuštění jakehokoli dotazu. Zrovna v tomto případě to může být těch 10ms a pod to se na té dané konfiguraci nedá dostat. Na druhou stranu těch 10 - 15 ms to může držet klidně třeba od toho tisíce do milionu řádků.
blaaablaaa
Profil
Tomášeek:
Tak všechna tahle data snad na větším projektu netaháš vždy z db, ale z cache.
Kajman
Profil
Mysql mívala dříve problémy s dotazy typu sloupec in (select), kdy plánovač občas dělal vnitřní select pro každý řádek, i když tam nebyla použita korelace. Pak bylo vhodnější to přespat na join. Ale podle explainu to vypadá, že tady se tak neděje.

Pro porovnání rychlosti si můžete změřit obyčejné dotazy.

SELECT SQL_NO_CACHE `f`.`follows_id` FROM followers AS `f` WHERE f.account_id = 75

SELECT SQL_NO_CACHE * FROM statuses AS `s` WHERE s.account_id = 75
Foi
Profil
Kajman časy jsou takové:

1) 0.0024s
2) 0.0022s

Původní dotaz 0.025

Nemá to smysl řešit a při nejhorším uvažovat o lepším stroji. Myslel jsem, že mám chybu někde v kódu/klíčích, ale asi ne.
Kajman
Profil
Tipnul bych, že nejpomalejší na tom bude to řazení, protože index se použite na where, ale řazení se musí udělat bez pomoci indexu. Je divné, že to v explainu není poznat.

Podle názvů indexů se moc nedá poznat, co tam je za sloupce a jakého typu indexy jsou. Pomohly by výsledky
show create table statuses;
show create table followers;

Jak jsou rychlé tyto dotazy a kolik vrací řádků bez limitu?
SELECT SQL_NO_CACHE s.* FROM statuses AS s WHERE s.account_id IN(SELECT `f`.`follows_id` AS `f_follows_id` FROM followers AS `f` WHERE f.account_id = 75) LIMIT 0, 10;

SELECT SQL_NO_CACHE s.* FROM statuses AS s JOIN followers AS f ON s.account_id=f.follows_id WHERE f.account_id = 75 LIMIT 0, 10;

SELECT SQL_NO_CACHE s.* FROM statuses AS s JOIN followers AS f ON s.account_id=f.follows_id WHERE f.account_id = 75 ORDER BY s.created DESC LIMIT 0, 10;
Foi
Profil
1) Table statuses (názvy sloupců zkrácené, indexu není moc, protože optimalizuji, až když je problém s časem), používám doctrine

CREATE TABLE `statuses` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `created` datetime NOT NULL,
 `content` longtext COLLATE utf8_unicode_ci NOT NULL,
 `account_id` int(11) DEFAULT NULL,
 `category_id` int(11) DEFAULT NULL,
 `status_id` int(11) DEFAULT NULL,
 `link_metadata_id` int(11) DEFAULT NULL,
 PRIMARY KEY (`id`),
 KEY `IDX_4BF01E119B6B5FBA` (`account_id`),
 KEY `IDX_4BF01E116BF700BD` (`status_id`),
 KEY `IDX_4BF01E1112469DE2` (`category_id`),
 KEY `IDX_4BF01E11111E650A` (`link_metadata_id`),
 KEY `created_idx` (`created`),
 CONSTRAINT `FK_4BF01E11111E650A` FOREIGN KEY (`link_metadata_id`) REFERENCES `link_metadata` (`id`) ON DELETE SET NULL,
 CONSTRAINT `FK_4BF01E1112469DE2` FOREIGN KEY (`category_id`) REFERENCES `news_categories` (`id`) ON DELETE SET NULL,
 CONSTRAINT `FK_4BF01E116BF700BD` FOREIGN KEY (`status_id`) REFERENCES `statuses` (`id`) ON DELETE CASCADE,
 CONSTRAINT `FK_4BF01E119B6B5FBA` FOREIGN KEY (`account_id`) REFERENCES `accounts` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=18298 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci

2) followers
CREATE TABLE `followers` (
 `account_id` int(11) NOT NULL,
 `follows_id` int(11) NOT NULL,
 PRIMARY KEY (`account_id`,`follows_id`),
 KEY `IDX_8408FDA79B6B5FBA` (`account_id`),
 KEY `IDX_8408FDA725215351` (`follows_id`),
 CONSTRAINT `FK_8408FDA725215351` FOREIGN KEY (`follows_id`) REFERENCES `accounts` (`id`) ON DELETE CASCADE,
 CONSTRAINT `FK_8408FDA79B6B5FBA` FOREIGN KEY (`account_id`) REFERENCES `accounts` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci

1) 0.0021-0.0029, vrací 0 řádků
2) 0.0019-0.0037, vrací 0 řádků
3) 0.0023-0.0040, vrací 0 řádků

Když to provedu na několika tisících záznamech, tak jen 3) vzroste na 0.0310

Jakmile dodám u 1) to OR s.account_id = 75, tak najednou stoupne o 10 násobek na 0.0256 - 0.0350, i když je 0 záznamů

Takže problémem asi bude OR s.account_id a ORDER BY s.created
Kajman
Profil
Testovat rychlost dotazů s řazením, co vrací nula řádků, nedává moc smysl.

Ten or tam může být problematický, často se to vnitřně rozloží na union dotazy, tak by se to dalo rozložit ručně a využít toho limitu, aby se union dělal až na těch 10 řádků. Ale pochybuji, že to bude rychlejší. Plánovač by to měl zvládnou sám.
SELECT *
FROM   ((SELECT s.*
         FROM   statuses AS s
         WHERE  s.account_id IN(SELECT f.follows_id
                                FROM   followers AS f
                                WHERE  f.account_id = 75)
         ORDER  BY s.created DESC
         LIMIT  10) -- pri strankovani menit limit 10 20 30 40...
        UNION
        (SELECT s.*
         FROM   statuses AS s
         WHERE  s.account_id = 75
         ORDER  BY s.created DESC
         LIMIT  10) -- pri strankovani menit limit 10 20 30 40...
         ) t
ORDER  BY t.created DESC
LIMIT  0, 10 -- pri strankovani menit offset 0 10 20 30...

Také můžete zkusit udělat u tabulky statuses dvousloupcový index (`account_id`, `created`) - on pomůže přímo jen v druhém pododtaze, ale v prvním možná bude stačit číst pro řazení hodnotu created z indexu a až těch 10 řádků by se četlo z tabulky.

Nebo si můžete určit, že v tabulce followers bude mít každý uživatel sám sebe a pokud tam tedy bude řádek (75,75), nemusíte OR psát :-)
Foi
Profil
Kajman Děkuji za všechno. Dotaz je stejně výkonný jako OR. Dvousloupcový index jsem zkoušel viz první post bez výsledku. Vypadá to tak, že to nechám a třeba v budoucnu to vyřeší uvnitř mysql
Kajman
Profil
Foi:
Dvousloupcový index jsem zkoušel viz první post bez výsledku.

Ale v obráceném pořadí sloupců. Není to stejné.
Foi
Profil
Kajman:
Z 24 ms na 19ms změna. Musím si nastudovat mnoho informací o databázích

Vaše odpověď

Mohlo by se hodit


Prosím používejte diakritiku a interpunkci.

Ochrana proti spamu. Napište prosím číslo dvě-sta čtyřicet-sedm:

0