Skip to main content

29 Tekniikkaa

29Tekniikka logo
Moni ajattelee ensimmäisenä Java-koodia, kun järjestelmä alkaa tuntua hitaalta. Se on ymmärrettävä reaktio. Käyttäjät valittavat, että sivut latautuvat hitaasti, API-vastaukset viipyvät tai taustaprosessit kestävät aiempaa kauemmin. Jossain vaiheessa joku toteaa palaverissa, että “Java ei enää jaksa”.
Todellisuudessa syy löytyy yllättävän usein aivan muualta.
Olen huomannut, että suorituskykyongelmia tutkittaessa ensimmäinen oletus menee usein väärin. Epäillään palvelinta, JVM:ää tai jotain yksittäistä metodia, vaikka ongelma liittyykin tietokantaan, kasvaneeseen datamäärään tai ulkoiseen rajapintaan.
Siksi hyvä suorituskykytyö alkaa yleensä yhdestä yksinkertaisesta kysymyksestä:

Mikä oikeasti hidastui?

Se kuulostaa itsestään selvältä, mutta käytännössä moni tiimi siirtyy korjaamaan asioita ennen kuin ongelman sijainti on tiedossa.

Sama koodi, eri määrä dataa

➠ Yksi tavallisimmista tilanteista näkyy järjestelmissä, jotka ovat olleet käytössä pitkään.
➠ Alussa kaikki toimii nopeasti. Testiympäristössä asiakkaita on muutama sata ja tapahtumia muutama tuhat. Hakutoiminnot vastaavat lähes välittömästi.
➠ Vuoden tai kahden kuluttua tilanne on toinen.
➠ Tietokannassa on miljoonia rivejä. Käyttäjien määrä on kasvanut. Rajapintoja käytetään enemmän kuin alun perin arvioitiin.
➠ Koodia ei välttämättä ole muutettu juuri siinä kohdassa, joka nyt hidastaa järjestelmää.
➠ Sovellus tekee edelleen saman asian kuin ennen. Se tekee sitä vain paljon suuremmalla datamäärällä.
➠ Tästä syystä suorituskykyä kannattaa tarkastella myös liiketoiminnan näkökulmasta. Jos palvelu menestyy, sen kuorma muuttuu. Se, mikä toimi hyvin ensimmäisenä vuonna, ei välttämättä toimi enää kolmantena.

Tietokanta on usein ensimmäinen epäilty

Kun vasteajat kasvavat, katse kannattaa suunnata tietokantaan ennen kuin lähdetään muuttamaan Java-koodia.
Yllättävän usein ongelma löytyy kyselyistä.
Eräässä projektissa käyttöliittymässä näkyi lista asiakkaista. Sivu oli aiemmin latautunut alle sekunnissa, mutta myöhemmin käyttäjät joutuivat odottamaan useita sekunteja.
Ensimmäinen epäilys kohdistui palvelinresursseihin.
Todellinen syy oli paljon arkisempi.
Jokaisen asiakkaan yhteydessä haettiin erillinen lisätieto tietokannasta. Kun asiakkaita oli vähän, tätä ei huomannut kukaan. Kun määrä kasvoi tuhansiin, pieni suunnitteluratkaisu muuttui suorituskykyongelmaksi.
Tällaiset tilanteet ovat Java-maailmassa tuttuja erityisesti ORM-ratkaisujen kanssa. Koodi näyttää siistiltä ja helposti ylläpidettävältä, mutta taustalla syntyy enemmän kyselyitä kuin kehittäjä olettaa.
Siksi SQL-lokit kertovat usein enemmän kuin pelkkä lähdekoodi.

Hitaus ei aina näy omassa järjestelmässä

Monessa yrityksessä Java-palvelu ei toimi yksin.
Se keskustelee maksupalveluiden, tunnistautumisratkaisujen, ERP-järjestelmien, CRM-järjestelmien ja muiden rajapintojen kanssa.
Kun käyttäjä odottaa vastausta, koko ketju vaikuttaa lopputulokseen.
Jos ulkoinen palvelu vastaa kahdessa sekunnissa, oma Java-sovellus ei voi olla merkittävästi nopeampi.
Tämä unohtuu helposti, koska ongelma näkyy omassa käyttöliittymässä. Silloin syntyy vaikutelma, että oma järjestelmä on hidas.
Todellisuudessa se saattaa vain odottaa jotakin muuta järjestelmää.
Tästä syystä lokit ja mittarit ovat paljon arvokkaampia kuin arvailu.
Kun nähdään tarkasti, kuinka paljon aikaa kuluu tietokantaan, kuinka paljon ulkoisiin kutsuihin ja kuinka paljon varsinaiseen sovelluslogiikkaan, keskustelu muuttuu huomattavasti selkeämmäksi.

Muistiin liittyvät ongelmat eivät näytä suorituskykyongelmilta

➠ Toinen yleinen tilanne liittyy muistinkäyttöön.
➠ Palvelu toimii normaalisti aamulla, mutta iltapäivällä vasteajat kasvavat.
➠ Palvelin ei ole kaatunut. Virheilmoituksia ei juuri näy. Kaikki näyttää päällisin puolin normaalilta.
➠ Silti järjestelmä tuntuu hitaalta.
➠ Usein taustalla on kasvava muistinkäyttö.
➠ Kun JVM joutuu käyttämään enemmän aikaa roskienkeruuseen, käyttäjät kokevat sen hitaampina vasteaikoina.
➠ Moni kehittäjä on joskus keskittynyt tutkimaan tietokantaa tai sovelluslogiikkaa, vaikka ongelma näkyi lopulta muistinkäytön mittareissa.
➠ Tästä syystä suorituskykyä ei kannata tarkastella pelkästään vasteaikoina. Myös muistinkäytön kehitys kertoo paljon siitä, mitä järjestelmässä tapahtuu.

Liian suuri määrä dataa yhdessä vastauksessa

Joskus ongelma löytyy paikasta, jota ei ensimmäisenä ajatella.
Palvelu toimii teknisesti oikein. Vastaukset palautuvat ilman virheitä.
Silti käyttö tuntuu hitaalta.
Syy voi olla yksinkertaisesti siinä, että sovellus palauttaa enemmän dataa kuin kukaan tarvitsee.
Tämä näkyy erityisesti vanhemmissa järjestelmissä.
API palauttaa satoja tai tuhansia tietueita, vaikka käyttöliittymä näyttää niistä vain pienen osan.
Verkossa liikkuu tarpeettoman paljon dataa.
Selain käsittelee enemmän sisältöä kuin tarvitsisi.
Palvelin tekee ylimääräistä työtä.
Kun vastaukset rajataan todellisen tarpeen mukaan, vaikutus voi olla suurempi kuin monimutkaisella optimointiprojektilla.

Säikeet eivät korjaa kaikkea

Kun järjestelmä hidastuu, joku ehdottaa usein lisää säikeitä.
Ajatus kuulostaa loogiselta.
Jos työ valmistuu hitaasti, tehdään sitä useammassa säikeessä.
Käytännössä asia ei ole näin yksinkertainen.
Jos ongelma on tietokannassa, lisäsäikeet voivat jopa pahentaa tilannetta. Tietokanta saa enemmän pyyntöjä ja kuormittuu entistä enemmän.
Jos taas ongelma liittyy ulkoiseen rajapintaan, useammat säikeet saattavat vain kasvattaa odottavien pyyntöjen määrää.
Säikeiden määrä kannattaa mitoittaa todellisen kuorman perusteella, ei oletusten.
Tästä syystä mittaaminen nousee suorituskykykeskusteluissa jatkuvasti esiin. Ilman mittaustietoa optimointi muuttuu helposti arvailuksi.

Kaikkea ei kannata optimoida

➠ Tämä on ehkä vaikein asia hyväksyä.
➠ Kaikkia hitaita kohtia ei tarvitse korjata.
➠ Jos toiminto suoritetaan kerran yössä eikä se vaikuta käyttäjiin, sen optimointi ei välttämättä tuo liiketoiminnalle mitään hyötyä.
➠ Toisaalta pieni viive kirjautumisessa tai maksutapahtumassa voi näkyä tuhansille käyttäjille päivittäin.
➠ Siksi suorituskykytyössä pitäisi aina ymmärtää myös vaikutus liiketoimintaan.
➠ Teknisesti kiinnostavin ongelma ei välttämättä ole tärkein ongelma.

Miten kokeneet tiimit yleensä etenevät?

Hyvin toimivissa kehitystiimeissä suorituskykyongelmaa ei ratkaista ensimmäisen idean perusteella.
Ensin kerätään tietoa.
Sen jälkeen tunnistetaan pullonkaula.
Vasta tämän jälkeen päätetään, mitä muutetaan.
Joskus ratkaisu löytyy indeksistä.
Joskus rajapintakutsusta.
Joskus välimuistista.
Ja joskus Java-koodista.
Mutta vasta tutkimisen jälkeen tiedetään varmasti.
Juuri tämä erottaa järjestelmällisen suorituskykytyön satunnaisesta optimoinnista.
Nopein tapa käyttää aikaa hukkaan on korjata asiaa, joka ei ollut ongelma alun perinkään.