21. září bude sraz! Od 18.00 v restauraci Tradice v Praze u Anděla
Autor Zpráva
mimochodec
Profil
Kdysi jsem dostal radu tam, kde si říkám o jeden řádek tabulky, používat LIMIT 1, protože to prý je rychlejší. Nemám teď možnost to prověřit nad takovým množstvím dat, abych dosáhl nějakých smysluplných výsledků. Je na tom něco pravdy nebo ne?

Tedy:
SELECT a, b, c FROM tbl WHERE ID=234 LIMIT 1

Moderátor Petr ZZZ: Titulek „LIMIT 1“ nevystihoval podstatu dotazu. Příště zkus prosím vymyslet lepší.
Jan Tvrdík
Profil
mimochodec:
Pokud se hledá podle primárního nebo unikátního klíče, tak je to nesmysl.
jefitto44
Profil
Tak... pokiaľ ti DB vráti jediný row, je úplne jedno, či tam ten limit je, alebo nie...
Otestované v debugbare Laravelu
mimochodec
Profil
Změřil jsem to takhle. Je to dost objektivní? Prvním dotazem vytáhnu náhodné ID, druhým měřeným si řeknu o jeden řádek podle toho ID.

$avg = 0;

for ($i = 1; $i <= 100; $i++) {

  $q = "SELECT comment_ID FROM wp_comments_zaloha ORDER BY RAND() LIMIT 1";
  $res = mysql_query($q);
  $num = mysql_num_rows($res);

  if ($num > 0 ) {
    while ($ret=MySQL_Fetch_Array($res)):
      $id= $ret["comment_ID"];
    endwhile;
  }

  $time_start = microtime_float();

  $q = "SELECT * FROM wp_comments_zaloha WHERE comment_ID=".$id." LIMIT 1"; // tady LIMIT  ano/ne
  $res = mysql_query($q);
  $num = mysql_num_rows($res);

  if ($num > 0 ) {
    while ($ret=MySQL_Fetch_Array($res)):
      $id= $ret["comment_ID"];
    endwhile;
  }

  $time_end = microtime_float();
  $time = $time_end - $time_start;
  $avg = $avg + $time;
}

echo $avg;

s limitem
0.39239645004272
0.26396441459656
0.2030992031097
0.14027094841003
0.16573667526245

bez limitu
0.26832056045532
0.13402724266052
0.14877676963806
0.085238456726074
0.19190049171448
mimochodec
Profil
Říkám si, jestli ten první dotaz neovlivní ten druhý tím, že by ten řádek nějak zůstal v nějaké interní paměti. Možná by bylo lepší tisícovku těch ID vytáhnout předem, v nějakém poli v php s tím zamíchat a pak vybírat z toho pole.


Omlouvám se za samomluvu, ale myslím, že když už jsem se do toho pustil, mohlo by se to hodit ještě někdy příště někomu. Takže jako méně zkreslené mi připadá toto. Závěr: LIMIT 1 nemá na rychlost vliv.
$ids = array();

$q = "SELECT comment_ID FROM wp_comments_zaloha ORDER BY RAND() LIMIT 100";
$res = mysql_query($q);
$num = mysql_num_rows($res);

if ($num > 0 ) {
  while ($ret=MySQL_Fetch_Array($res)):
    $ids[]= $ret["comment_ID"];
  endwhile;
}

shuffle($ids);

foreach ($ids as $id) {
  $time_start = microtime_float();

  $q = "SELECT * FROM wp_comments_zaloha WHERE comment_ID=".$id." LIMIT 1";  //tady
  $res = mysql_query($q);
  $num = mysql_num_rows($res);

  if ($num > 0 ) {
    while ($ret=MySQL_Fetch_Array($res)):
      $id= $ret["comment_ID"];
    endwhile;
  }

  $time_end = microtime_float();
  $time = $time_end - $time_start;
  $avg = $avg + $time;
}

echo $avg;

LIMIT 1 ano
0.06256
0.06308
0.07500
0.07627
0.04009

LIMIT 1 ne
0.08467
0.07941
0.08589
0.06042
0.04577
Joker
Profil
mimochodec:
Tak logickou úvahou se ani nedá očekávat, že by přidání LIMIT 1 k dotazu, který i bez toho vybere vždy jen jeden záznam, vykonávání mohlo nějak urychlit. Resp. nenapadá mě žádný mechanismus, jakým by to mohlo zlepšit výkon.
Spíš se dá očekávat, že by to dotaz mohlo nepatrně zpomalit.

Naopak dotaz, který by jinak vybral hodně záznamů, to asi urychlit může.
Alphard
Profil
[#5] mimochodec
[#6] Joker
Já těm vašim experimentálním závěrům a úvahám moc nevěřím. Pokud lze použít index, tak limit nemá vliv, to je jasné; složitost hledání bude cca O(log(n)), z principu se prochází celá tabulka (resp. index), když se najde požadovaný záznam, už ani není kam jít dál (klasický B-tree).
Ale když index není k dispozici, musí se procházet sekvenčně (lineárně) se složitostí O(n). Pokud má server štěstí a nalezne vyhovující záznam na prvním místě, ukončí procházení zbytku a vrátit jediný záznam*. Jestliže není použit limit, musí zkontrolovat i všechny ostatní záznamy, což by mělo být pomalejší.

*Tohle je klíčové, takže podkládám dokumentací:
As soon as MySQL has sent the required number of rows to the client, it aborts the query(...)
http://dev.mysql.com/doc/refman/5.6/en/limit-optimization.html (5. bod)
Amunak
Profil
Přikláním se k názoru Alpharda a ještě bych si troufal tvrdit že dnešní databázové systémy budou dost chytré na to aby věděly že pokud se ptáme na sloupec s indexem/unikátní hodnotou, nějaký limit vůbec nebudou brát v potaz a "vyoptimalizují" ho ven.
juriad
Profil
Ještě tam je alespoň několik prvků, které můžou hrát roli.

Bufferování výsledků. Přestože klient v aplikaci (PHP) provede jediný fetch, je databáze požádána o několik prvních záznamů. Musí tak projít při table-full-scanu větší množství dat. Čím více dat musí databáze projít, tím větší nároky na výkon, ve výsledku zvládne menší množství dotazů za jednotku času. Table-full-scan při větším množství dat vyžaduje přistup na disk.

Uzavření dotazu. Pokud je dotaz uložen v nějaké globálnější proměnné v PHP, zůstává viset až do konce skriptu, protože se kdykoli později může objevit další fetch. Tomu to bufferování naopak může pomoci, protože dotaz bude již vyčerpaný a na straně databáze ukončený. Takové visící dotazy omezují propustnost databáze kvůli transakční izolaci, kdy dotaz stále musí vracet záznamy z doby, kdy byl spuštěn, přestože od toho okamžiku mohlo proběhnout mnoho insertů/updatů/deletů. Databáze zvládne méně dotazů současně a má větší paměťové nároky (hádám).

Databáze jsou, myslím, dost chytré na to, aby v případě existence indexu uměli dokázat, kolik výsledků existuje bez nutnosti přistupu k samotným datům. Pokud index není, klauzule LIMIT 1 databázi pomůže.

Pak je otázkou, proč aplikace pokládá dotazy, které nejsou uspokojitelné indexy. Buď se dotaz neprovádí často, pak nás ani ten ušetřený čas za LIMIT 1 nezajímá. Nebo je dotaz příliš komplikovaný a má nepředvídatelné podmínky (uživatelské filtrování) a indexů by bylo potřeba příliš, což by omezilo čas na insert/update/delete. Pak je možnost takový dotaz přepsat, aby byl částečně pokrytý indexy a zbytek se provedl množinovými operacemi nad už předfiltrovanou sadou záznamů.

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