[php/mysql] result caching rendabel?

Pagina: 1
Acties:

  • Martijn02
  • Registratie: September 2000
  • Laatst online: 15-09 14:25

Martijn02

/* No Comment */

Topicstarter
Ik heb altijd het idee gehad dat een database een vrij trage applicatie was. Reuze handig, en als je hem goed gebruikt is er niks mis mee.

Maar nu ik aan het bouwen ben voor een vrij druk bezochte website heb ik het idee gevat om query-results te cachen.

Ik had al een database class, en daar heb ik het ingebouwd. Je kan bij een query opgeven of het cache gebruikt moet worden, of er cache gecreeerd moet worden, en wat de lifetime van het cache is.

Als je dus een bepaalde query maar een keer per dag wil uitvoeren, en het resultaat cachen roep je de class zo aan (er van uitgaande dat hij al geinitialiseerd is)
PHP:
1
2
3
<?
$DB->array_query($array, "SELECT * FROM Menu ORDER BY parentID, order", false, true, true, (24*60*60))
?>

het 3e argument (false) is even niet van toepassing hier.


Dit is de functie, dan zie je wat er gebeurd
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
<?
    function array_query_append( &amp;amp;amp;amp;$array, $SQL, $column = false, $useCache = false, $makeCache = false, $cacheTime = 0 )
    {
        $cacheFile = "";
        $buildCache = false;
        
        if ( $useCache )
        {
            
            $cacheFile = $this->cacheDir . md5 ( $SQL );
            if ( file_exists ( $cacheFile ) )
            {
                // How old is the cache file?
                $secondsOld = time() - filemtime( $cacheFile );
                // Can i use it?
                if ( $secondsOld < $cacheTime || $cacheTime == -1 )
                {
                    // use cache file instead of results
                    
                    $fp = fopen ($cacheFile, "r");
                    flock ( $fp, LOCK_SH );
                    $serialized = fgets ( $fp, 1048576 );
                    fclose ( $fp );
                    
                    $array = unserialize($serialized);
                    return true;
                }
            }
            $buildCache = true;
        }

        $result =&amp;amp;amp;amp; $this->query( $SQL );




// Grote knip, hier gebeurd een hoop wat niet ter zake doet, er komt een variabele $array uit die het resultaat bevat



        if ( $useCache &amp;amp;amp;amp;&amp;amp;amp;amp; $makeCache )
        {        
            if ( $buildCache )
            {
                $serialized = serialize( $array );
                $fp = fopen ($cacheFile, "w");
                flock ( $fp, LOCK_EX );
                fputs ( $fp, $serialized, strlen ( $serialized ) );
                fclose ( $fp );
            }
        }
        
        return true;
    }
?>

Deze functie roep ik om te benchmarken 1000 keer aan.

<Edit: KNIP Per ongeluk verkeerde results gepost... zucht (zie post 4)>

Vaag he? inlezen van het fs en unserializen is veeel trager dan een query doen aan de database...


Of doe ik het nou zo fout?

  • Martijn02
  • Registratie: September 2000
  • Laatst online: 15-09 14:25

Martijn02

/* No Comment */

Topicstarter
Heb even verder zitten kijken en geprobeerd te kijken wat er nou zo lang duurt...

De md5 duurt 2.2172251939774
De file_exists => 0.11673533916473
De filemtime => 0.1621071100235
Het openen, locken, inlezen en sluiten duurt 8.6878294944763
en het Unserializen => 11.760777115822

Dat wil dus zeggen dat het unserializen van een string naar een array even lang duurt (in mijn geval dan) als de hele database query bij elkaar... Bizar toch ?

(tijden zijn in seconden, onderdelen zijn 1000 keer uigevoerd en tijden zijn bij elkaar opgeteld)

  • Orphix
  • Registratie: Februari 2000
  • Niet online
Op woensdag 06 februari 2002 00:50 schreef Martijn02 het volgende:
Heb even verder zitten kijken en geprobeerd te kijken wat er nou zo lang duurt...

De md5 duurt 2.2172251939774
De file_exists => 0.11673533916473
De filemtime => 0.1621071100235
Het openen, locken, inlezen en sluiten duurt 8.6878294944763
en het Unserializen => 11.760777115822

Dat wil dus zeggen dat het unserializen van een string naar een array even lang duurt (in mijn geval dan) als de hele database query bij elkaar... Bizar toch ?

(tijden zijn in seconden, onderdelen zijn 1000 keer uigevoerd en tijden zijn bij elkaar opgeteld)
Wees blij dat je DB zo snel is :)
Maar zoals je al zei, goede indexen en een vrije simpele query kunnen heel erg snel uitgevoerd worden. Daarbij heeft je database het voordeel dat de db waarschijnlijk al grotendeels in het geheugen staat, en dat het programma hiervoor geoptimaliseerd is (in een snellere taal dan PHP).
Ik weet niet hoeveel records je hebt en hoe je query eruit ziet, maar ik denk dat je vooral veel baat hebt bij complexe queries.

  • Grum
  • Registratie: Juni 2001
  • Niet online
volges mij is de enige manier om query-results goed te cachen als jij een soort proxy maakt die de results voor X tijd bewaard.

Als je dit doet met pconnects is het enige wat nog echt tijd eet weg .. nl het daadwerkelijke ophalen/zoeken van de resultaten.

  • Martijn02
  • Registratie: September 2000
  • Laatst online: 15-09 14:25

Martijn02

/* No Comment */

Topicstarter
Oeps.. ik heb voor zonder caching per ongeluk de verkeerde resultaten genomen... (stom |:( het was al laat gisteravond) Nu is het verschil niet te zien namelijk, er staan 2 resultaten met caching... hieronder zal ik ze nog eens zetten.. maar nu goed:

Benchmark results
code:
1
2
3
4
5
6
7
8
9
Amount of queries: 3
SELECT * FROM Menu ORDER BY 'order', 'name'
Time: 11.54732298851 ( 1000 x 0.01154732298851)

SELECT * FROM Template WHERE title like 'include.features.1.%'
Time: 1.0529080629349 ( 1000 x 0.0010529080629349)

SELECT * FROM PageWindow, Window Where PageWindow.windowID = Window.ID AND PageWindow.pageID = '27' ORDER BY PageWindow.order ASC;
Time: 0.85794496536255 ( 1000 x 0.00085794496536255)

Benchmark results met caching
code:
1
2
3
4
5
6
7
8
9
Amount of queries: 3
SELECT * FROM Menu ORDER BY 'order', 'name'
Time: 26.049967050552 ( 1000 x 0.026049967050552)

SELECT * FROM Template WHERE title like 'include.features.1.%'
Time: 9.3022490739822 ( 1000 x 0.0093022490739822)

SELECT * FROM PageWindow, Window Where PageWindow.windowID = Window.ID AND PageWindow.pageID = '27' ORDER BY PageWindow.order ASC;
Time: 9.2708070278168 ( 1000 x 0.0092708070278168)

  • ACM
  • Registratie: Januari 2000
  • Niet online

ACM

Software Architect

Werkt hier

Op woensdag 06 februari 2002 00:50 schreef Martijn02 het volgende:
Dat wil dus zeggen dat het unserializen van een string naar een array even lang duurt (in mijn geval dan) als de hele database query bij elkaar... Bizar toch ?
Je zou, als je jezelf vertrouwd ;), ook ipv serialized arrays gewoon "php" arrays weg kunnen schrijven en die gewoon via "include" binnenhalen.

  • Martijn02
  • Registratie: September 2000
  • Laatst online: 15-09 14:25

Martijn02

/* No Comment */

Topicstarter
Ja heb ik ook over nagedacht... maar een include is volgens mij nog trager, dan moet de file ook ingelezen worden en vervolgens geparsed en uitgevoerd worden...

Ik weet niet, als dit niks oplevert ben ik zeker van plan om dat te gaan proberen...


Een collega van mij roept dat het aan de test zou kunnen liggen. Mischien dat het resultaat pas bij meerdere users tegelijk echt voordeel op gaat leveren. 1000 keer achter elkaar is natuurlijk wel iets anders als 1000 keer tegelijk. En files kunnen wel tegelijk gelezen worden. Maar volgens mij de database ook, die maakt gewoon een extra procesje aan.

  • ACM
  • Registratie: Januari 2000
  • Niet online

ACM

Software Architect

Werkt hier

Op woensdag 06 februari 2002 09:54 schreef Martijn02 het volgende:
En files kunnen wel tegelijk gelezen worden. Maar volgens mij de database ook, die maakt gewoon een extra procesje aan.
Klopt... Misschien dat je pas resultaat merkt bij grote queries of whatever.

Maar ik gok dat parsen niet perse slomer is dan unserializen ;)

  • Martijn02
  • Registratie: September 2000
  • Laatst online: 15-09 14:25

Martijn02

/* No Comment */

Topicstarter
de eerste (trage) query haalt ongeveer 25350 bytes aan data op. (althans, zo groot is de file als hij serialized is)
Best een hoop resultaat.

Maar je bedoeld denk ik een zware query, met veel joins enzo. Ook dat zal ik zo eens even proberen...

Wie weet er een stuk code waarmee je een array kan omzetten naar php code? ik heb het ooit ergens gezien, maar kan het niet meer vinden

  • ACM
  • Registratie: Januari 2000
  • Niet online

ACM

Software Architect

Werkt hier

Op woensdag 06 februari 2002 09:59 schreef Martijn02 het volgende:
Wie weet er een stuk code waarmee je een array kan omzetten naar php code? ik heb het ooit ergens gezien, maar kan het niet meer vinden
Zolang ie niet multidimensionaal is:
PHP:
1
2
3
4
5
6
7
8
<?
echo "\$val = array(";
foreach($array as $arval)
{
   echo "'$arval', ";
}
echo ");"
?>

En dat nog even met filewrites doen ipv echo's :)
Maar je bedoeld denk ik een zware query, met veel joins enzo. Ook dat zal ik zo eens even proberen...
Dan zeker :)

  • Martijn02
  • Registratie: September 2000
  • Laatst online: 15-09 14:25

Martijn02

/* No Comment */

Topicstarter
Op woensdag 06 februari 2002 10:02 schreef ACM het volgende:

[..]

Zolang ie niet multidimensionaal is:
PHP:
1
2
3
4
5
6
7
8
<?
echo "\$val = array(";
foreach($array as $arval)
{
   echo "'$arval', ";
}
echo ");"
?>

En dat nog even met filewrites doen ipv echo's :)
Neee... die is te simpel ACM :)

Het gaat hier om ietsje complexere array's, die ook multidimensionaal zijn. en ook de key's moeten opgeslagen worden, maar goed het is een start...

Ja stom, ik zie er een beetje tegenop. Heb er namelijk ooit een stuk php code voor gezien. en dat heb ik toen niet in mijn KB opgeslagen (stom stom stom) Ben nu op zoek. Heb geen zin om het wiel opnieuw uit te vinden

(begint op een script-request te lijken... (schaam))

  • ACM
  • Registratie: Januari 2000
  • Niet online

ACM

Software Architect

Werkt hier

Dan krijg je geneste foreaches ala:
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?
function write_array($array, $level)
{
    if(is_array($array))
    {
        foreach($array as $key => $val)
        {
            // Hier wat array() dingen nog omheen
            echo "$key => ";
            write_array($val, $level+1)
        }
    }
    else
    {
        echo "'$array', ";
    }
}
?>

Zal nog niet helemaal perfect zijn, maarja :)

  • Martijn02
  • Registratie: September 2000
  • Laatst online: 15-09 14:25

Martijn02

/* No Comment */

Topicstarter
Benchmark results met caching dmv include
code:
1
2
3
Amount of queries: 1
SELECT * FROM Menu ORDER BY 'order', 'name'
Time: 2.3566380739212 ( 100 x 0.023566380739212)

Ik heb alleen de eerste een array van gemaakt, het lukt me nog niet om de inhoud van de file echt te gebruiken omdat als je iets in een include declareerd het buiten de scope ligt. Moet dus iets met GLOBALS[] gaan doen, en zo het resultaat doorgeven.

Als ik het ding 1000 keer test loopt hij uit zijn max_execution_time van 30 seconden. Dit is 100 keer getest. De totale tijd moet dus even met 10 vermenigvuldigd worden

Benchmark results met caching dmv include (keer 10!!!)
code:
1
2
3
Amount of queries: 1
SELECT * FROM Menu ORDER BY 'order', 'name'
Time: 23.56638073921 ( 1000 x 0.023566380739212)

Dit scheelt dus niet echt veel met de manier van cachen met serialize


HELP? Wat doe ik fout? Ik geloof mijn eigen resultaten niet meer :) kan toch niet kloppen dit?

/edit: nulletje te veel

  • Martijn02
  • Registratie: September 2000
  • Laatst online: 15-09 14:25

Martijn02

/* No Comment */

Topicstarter
|:(|:(|:(|:(|:(|:( SHIT SHIT SHIT SHIT |:(|:(|:(|:(|:(|:(

Nou de aap is uit de mouw hoor... bij nadere inspectie blijkt dat we MySQL 4.0.1-alpha draaien... een een van de features is Query caching...

Zucht,
* Martijn02 schaamt zich wezenloos en heeft zich belachelijk gemaakt

Ga morgen eens testen hoe het zit met een oudere mysql server... is mijn werk tenmniste niet voor niks geweest...

  • Rense Klinkenberg
  • Registratie: November 2000
  • Laatst online: 23:45
Hehe, ik was de topic aan het doorlezen en wou je er net op gaan wijzen dat de nieuwste MySQL zelf al de queries cached, maar dat hoeft dus niet meer ;)

  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 12-09 21:31

Janoz

Moderator Devschuur®

!litemod

Op woensdag 06 februari 2002 16:45 schreef Martijn02 het volgende:

* Martijn02 schaamt zich wezenloos en heeft zich belachelijk gemaakt
Als je dit topic vergelijkt met het gemiddelde php/mysql topic zie ik absoluut niet waarom jij je zou moeten schamen ;)..

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'


  • Martijn02
  • Registratie: September 2000
  • Laatst online: 15-09 14:25

Martijn02

/* No Comment */

Topicstarter
Op woensdag 06 februari 2002 19:17 schreef Janoz het volgende:

[..]

Als je dit topic vergelijkt met het gemiddelde php/mysql topic zie ik absoluut niet waarom jij je zou moeten schamen ;)..
Bedankt... Ik heb nog even verder zitten kijken... Query caching zit er wel in, maar staat uit... Dus zijn de resultaten toch weer wel goed... Sorry voor de verwarring...

Ben aan het proberen om het aan te krijgen, maar ik kan de my.cnf (daar schijnt de setting in te staan) nergens vinden. Kan het zijn dat hij niet bestaat?

Ik ga later testen met dik gevulde databases, en complexe joins. De wat zwaardere query's zeg maar. Omdat bij mij de news page een selectie van een aantal items bevat, welke gejoined is met Texten, Users, en Categorien. En in die Items wordt onder andere de "amountOfViews" opgeslagen. Dus iedere keer als er een nieuwsItem bekeken wordt verdwijnt de MySQL query cache weer. Desalniettemin (wat is dit voor woord?) Is het denk ik toch handig om het query cache aan te laten staan. Kan nooit kwaad volgens mij.

  • ACM
  • Registratie: Januari 2000
  • Niet online

ACM

Software Architect

Werkt hier

Ik denk dat je cache het vrijwel nooit zal winnen van simpele selects.

Echter wel als je pagina's uit een hele rij queries bestaan, bijvoorbeeld 10 queries voor allerlei delen van het nieuws, etc etc :)

En dan zeker als je queries ineens 10-15seconden gaan duren ;)

  • Martijn02
  • Registratie: September 2000
  • Laatst online: 15-09 14:25

Martijn02

/* No Comment */

Topicstarter
MySQL query cache wordt in shared-memory opgeslagen volgens mij. Dat is dus reuze snel. Je kan een grootte opgeven van het geheugen wat daarvoor gebruikt wordt.
Searches after one row in a one row table is 238% faster. This can be regarded as close to the minimum speedup to be expected for a query that is cached.
bron:
http://www.mysql.com/doc/Q/u/Query_Cache.html

  • Martijn02
  • Registratie: September 2000
  • Laatst online: 15-09 14:25

Martijn02

/* No Comment */

Topicstarter
Ok... MySQL query caching aan weten te zetten... Nieuwe benchmark results dus

Benchmark results
code:
1
2
3
4
5
6
7
8
9
10
11
Amount of queries: 3

SELECT * FROM Menu ORDER BY 'order', 'name'
Time: 0.74867296218872 ( 1000 x 0.00074867296218872)

SELECT * FROM Template WHERE title like 'include.features.1.%'
Time: 0.24046003818512 ( 1000 x 0.00024046003818512)

SELECT * FROM PageWindow, Window Where PageWindow.windowID = Window.ID 
     AND PageWindow.pageID = '27' ORDER BY PageWindow.order ASC;
Time: 0.2155179977417 ( 1000 x 0.0002155179977417)

Conclusie: MySQL met query caching is TERING SNEL :):):)

Ik kan er in ieder geval niet tegen op cachen...

Het enige voordeel van zelf cachen is mischien dat je de database-server minder zwaar belast... Bij erg drukke systemen scheelt het waarschijnlijk load. Als het even kan laat je MySQL zelf cachen. Werkt perfect, en vreselijk snel
Pagina: 1