MySQL-tweaking

(den här posten är någon slags form av terapi/anteckning för framtida bruk)

När servern för nyhetsportalen.se kraschade (igen) så låg sidan nere ett par dagar. För att komma igång fort igen har jag fått låna / hyra / whatever en core2duo-maskin som agerar server. Denna maskin är betydligt mindre bestyckad än den ursprungliga dual opteron-servern jag har kört på innan.

Detta innebär att jag för det första fick plocka bort en del funktioner helt från sidan för att den överhuvudtaget skulle fungera. I andra hand har jag fått försöka optimera. Detta har jag delvis lyckats med genom att installera Sphinx som får de rena sökningarna att gå flera magnituder snabbare (det har dock inneburit att en del sökresultat blir lite konstiga just nu men det kommer).

Det stora problem kvarstår dock (och detta är ett problem jag har haft hela tiden på nyhetsportalen), nämligen att vid nästan alla sökfrågor så måste jag göra en ORDER BY och oftast även en GROUP BY. Detta får effekten att alla index som MySQL möjligen kan använda flyger all världens väg (mysql kan bara använda ett index per tabell utan att börja skriva till tmp-tabellerna). Normalt sett är detta inget problem. För mig är det dock det. Relativt enkla queries för att sammanställa data, exempelvis för att lista de senaste 15 nyheterna en tidning har publicerat måste jag just nu välja data från tre tabeller. När sorteringen skall göras måste den då göras på alla artiklar som tidningen har gett ut (vilket kan vara upp mot 10000), datan måste hämtas från de andra tabellerna och skickas till klienten.

Detta blir då en läsning i varje tabells index, i datafilen samt en sortering som allt för ofta tar mer plats än vad som finns i ramminnet. Sorteringens “delar” hamnar då i tempfiler på disken. Det är här mitt problem uppstår. När en process börjar skriva till disken så hamnar ohjälpligt processorn i vänteläge på att hårddiskarnas IO skall bli klar innan de kan fortsätta. Detta gör att servern ofta står och bara väntar på skriv eller lästid mot diskarna.

När ett par frågor har byggts upp i kö är det nästan oundvikligt att varje enskild fråga börjar ta mer tid än vad som är bra och antalet connections till servern tar slut vilket resulterar i att användaren visas min inte så vackra felsida.

Vad har jag gjort åt detta? Delvis har jag försökt tillgodogöra mig så mycket information jag kan om ämnet genom att läsa mysqls optimeringskapitel, forumtrådar, blogginlägg, tricks och tricks etc.

Eftersom det inte finns något sätt att hindra MySQL från att kräkas ur sig tempfiler på disken om man sorterar på ett annat index än det som används i WHERE-clausen (och det är inget alternativ för mig).

The key used to fetch the rows is not the same as the one used in the ORDER BY SELECT * FROM t1 WHERE key2=constant ORDER BY key1;

Jag kan inte sortera på tidnings-id-nummret när jag gör en sökning efter de senaste 25 artiklarna en tidning har skrivit. Det är samma id-nummer på alla. Jag vill sortera på datum tex.

En del-lösning på detta som hjälper delvis är att göra sorteringen först i en subquery och sedan använda subqueryns resultat som sorterings-ordning. Detta gör att mindre data behöver skrivas till disken eftersom MySQL då inte skriver med all annan data som fanns i queryn.

Allmänna pointers

  • Använd inte text/blog i dina queries (LEFT() klipper effektivt ner text till varchars)
  • Använd inte mysqls egna fulltextsök utan titta på alternativen (sphinx, lucene), prestandamässigt kommer du INTE ångra dig
  • Skall du ändra tmp_table_size så ändra även max_heap_table_size
  • Om du har ett stort resultat-set (många kolumner) så försortera i en subquery:
    SELECT * FROM artikel WHERE id IN (SELECT id FROM artikel WHERE tidning=12 ORDER BY datum_tillagd) LIMIT 25 (mysql stödjer inte limit i subqueries)
  • Verktyg som lsof, vmstat, iostat och GkrellM hjälper dig identifiera bottlenecks

Så kära läsare, tips? :)


6 Responses to “MySQL-tweaking”

  1. Nikke Lindqvist Says:

    Bra tips. Tack för dem.

    Jag lider med dig, och känner igen mig från mer avancerade queries som jag har velat göra på Cocktailguiden.com, där drinkdatabasen är indexerad kors och tvärs och där vi vill kunna sortera och söka på allting.

    Skulle jag bygga om databasen idag så skulle jag göra om väldigt mycket…

  2. Hjalmar Says:

    Japp. Det kanske kommer fler tips om jag lyckas komma på något bra. Just nu är det bara att beta av seg fråga efter fråga och försöka optimera bort så mycket som möjligt.

    Jag har även gjort så att jag har lagt de aktuella nyheterna (jag har ju typ 10:1 i reads och writes) i en separat tabell som syncas mot den riktiga artikel-tabellen. Detta gjorde rätt mycket men jag kan ändå inte göra alla queries mot den lilla tabellen utan många (tex, alla tidningars bakåtloggar, arkivet) måste gå mot den maffiga artikel-tabellen; och då börjar mysql swappa.

    Dessutom har jag gjort som så att jag kör två temp-mappar på olika hårddiskar, eventuellt kan man överväga att lägga till ytterligare som är en ramdrive för att “lura” mysql. Det känns dock väldigt konstigt att behöva göra så..

  3. Jonas Says:

    Några tips: Kör aldrig SELECT på mer än vad du vill ha ut (dvs SELECT * kan vara ajaj). Kör LIMIT på så många frågor som möjligt. Är realtid alltid nödvändigt? Kanske smartare ibland att uppdatera en gång i timmen. Kör du innodb eller myisam? Kör ‘optimize table’ ibland via crontab om du kör myisam.. ja listan kan göras lång :) (tror tidigare jag skrev om explain före select för att kolla att du träffar index och ’show processlist’ samt log slow queries för att försöka hitta fler flaskhalsar).

  4. Hjalmar Says:

    Jonas, SELECT * undviker jag, däremot har jag ett gäng frågor där jag sorterar med massa kolumner valda. Jag skall försöka få bort de frågorna genom att köra två frågor och således minska antalet bytes som måste skrivas till disk. Det är konstigt att inte mysql är smartare där tycker jag.

    Cachning kör jag rätt hårt med, cachar i princip alla sidor i ett par minuter, särskilt rss och export-filer som ofta laddas flera hundra ggr i minuten :) .

    MyISAM för det mesta, optimize kan vara läge att kolla på. Problemet är att jag antagligen får ett 15 minuter hang när jag kör de kommandona på mina stora tabeller => servern blir helt nerswampad.

    En dellösning jag tänkte testa mig på nu är att köra min huvudserver för alla users och sedan replikera datat till den nuvarande-extraservern och låta mina premiumkunders exporter gå via den. Det kan förhoppningsvis ge lite bättre prestanda för alla samt bättre upplevelse för mina kunder.

  5. Jonas Says:

    Hum jag kanske skulle skriva en webbtjänst som ansluter via mysql och försöker optimera samt övervaka folks mysql datbaser.. oj nu gav jag bort en bra idé helt gratis :)

  6. Jonas Says:

    Har nu skrivit ett blogginlägg om de erfarenheter jag lärt mig då jag slitit som ett djur för att få mysql att lira lite snabbare. Kolla http://bloggz.se/blogg/2007/11/17/optimera-din-mysql-databas/

Leave a Comment

Subscribe without commenting