Sistēmanalīze un ātrdarbība

maijs 19, 2011

Mana pamata nodarbošanās un oficiālais amats (vismaz šobrīd) ir sistēmanalīze un sistēmanalītiķis. Parasti starp sistēmanalītiķiem un programmētājiem (tāpat kā starp programmētājiem un testētājiem, piemēram, kā arī daudzām citām profesijām) mēdz būt tāda kā rīvēšanās, kā savstarpēja pienākumu nogrūšana, kas nelielos apmēros noteikti ir veselīga, jo rada nelielu stresiņu, neļauj iesūnot un vispār veicina attīstību 🙂

Lielos apmēros tas neapšaubāmi izvēršas par destruktīvu kašķi, kas vispārīgajam mērķim (radīt ātrāk, lētāk, labāku programmatūru) par labu nenāk. Reizēm mēdz būt tā, ka atklāta kašķa it kā nav, bet vienkārši viena no pusēm ir nospiesta uz ceļiem un burtiski nevar ne iepīkstēties, t.i. visas kļūdas noklusēti vienmēr ir vai nu programmētājiem, vai tieši otrādi analītiķiem.

Lūk tagad arī iemesls šādam vispārīgam filozofiskam ievadam – kur ir ātrdarbības problēmu cēloņi? Es gribētu teikt, ka lielā daļa gadījumu tā nav programmētāju “vaina”, bet bremzēm kājas aug jau sistēmanalīzē. Vai, pareizāk izsakoties, sistēmanalīzes neeksistēšanā, vai nekorektā un nekvalitatīvā tās izpildē. Par šo tēmu tad arī bija mana prezentācija pēdējā LVOUG konferencē. Prezentācija par tipiskām sistēmanalīzes kļūdām, kas noved pie ātrdarbības problēmām programmēšanas laikā, ir šeit.

Vēl par sistēmanalīzi gribu atzīmēt, ka nu jau kādu laiciņu Vita Karnīte ir izveidojusi emuāru par sistēmanalīzi. Pirmie interesantie ieraksti jau tur ir atrodami, atliek tikai novēlēt brīvu laiku un vēlmi rakstīt tālāk!

Papildinājums – aizmirsu pateikt, ka visas LVOUG trešās konferences prezentācijas ir atrodamas šeit.

Advertisements

Darbu secība

janvāris 19, 2011

Šis ir simtais ieraksts šai emuārā. Ievērojot šo apaļumu, gribējās rakstu mazliet īpašu un tad man ienāca prātā tradicionālā darbu secība, ko es itin bieži, piemēram, sevis lasītajos kursos mēdzu popularizēt. Atzīstos, ka neesmu šīs secības izgudrotājs, oriģināls ir pieejams šai grāmatā, es to tikai mazliet vispārināju un adaptēju 🙂 Tātad, izstrādājot programmatūru datubāzēm var izmantot šādu pieeju:

  • Labāk nedarīt neko;
  • Ja tomēr jādara, tad izmantojiet SQL;
  • Ja nav iespējams paveikt ar tīru SQL, tad izmantojiet iebūvēto programmēšanas valodu (PL/SQL –  Oraclē, Transact SQL – SQL Serverī utt.)
  • Ja to nav iespējams paveikt ar iebūvēto programmēšanas valodu, tad lietojiet kādu citu programmēšanas valodu – Java, C utt.
  • Ja to nav iespējams paveikt ar Java vai C (vai kādu citu izvēlētu programmēšanas valodu), tad stingri pārdomājiet, vai neķerties pie kāda cita darba.

Iespējams, ka daļai lasītāju šī pieeja šķiet mazliet provokatīva. Varbūt pat viņiem ir mazliet taisnība 😉 Tomēr tikai mazliet. Tagad daži komentāri par katru no punktiem. Lasīt pārējo šī ieraksta daļu »


MySQL lietotāja definētie mainīgie un paplašinātais izpildes plāns

Oktobris 29, 2010

Raksts būs par divām lietām, kuras vieno faktiski tikai tas, ka tās ir saistītas ar MySQL. Uzdūros tām atbildot uz jautājumiem Latvijas programmētāju forumos, cik nu mums vispār te tādu ir.

Un tātad pirmais stāsts ir par MySQL lietotāja definētajiem mainīgajiem SQLā. Daudzām DBVS ir savi procedurālie paplašinājumi SQL, piemēram, Oracle tas ir PL/SQL, Microsoft SQL Server tas ir Transact-SQL jeb saīsinot T-SQL, MySQL arī 5.0 versijā pirmo reizi parādījās saglabātās procedūras un funkcijas. Tomēr jau no 3.23.6 versijas ir kaut kādi iedīgļi – lietotāja definētie mainīgie. Viens no populāriem to pielietojumiem, piemēram, ir ģenerēt numurus pēc kārtas, kas itin bieži ir nepieciešams, bet citā veidā MySQL nav iespējams. Piemērs, kurā ir tabula ar primārās atslēgas lauku, kurš ir unikāls, bet nenodrošina numurus pēc kārtas (kas tam, protams, NAV ARĪ JĀDARA!): Lasīt pārējo šī ieraksta daļu »


Slinkums jeb predikātu neizpilde

janvāris 21, 2010

Šoreiz nebūs runa par cilvēcisko slinkumu, bet par datubāžu slinkumu. Parasti viens no galvenajiem mērķiem datubāzes darbībā ir, lai tā visus SQL teikumus apstrādātu pēc iespējas ātrāk. Tiecoties pēc šī mērķa, tiek veikti daudzi un dažādi uzlabojumi, tai skaitā arī daži vienkāršākie un vēsturiski senākie – datubāzes beidz SQL teikuma kritēriju (predikātu) pārbaudi tiklīdz rezultāts ir skaidrs atlikušos nemaz nerēķinot.
Paši vienkāršākie piemēri ir šādi:

  • ja vairāki predikāti ir apvienoti ar loģisko UN (AND), tad tiklīdz neizpildās (ir aplams) viens no tiem, nākošo vērtības vairs nav svarīgas, jo viss izteikums ir aplams;
  • ja vairāki predikāti ir apvienoti ar loģisko VAI (OR), tad tiklīdz izpildās kaut viens no tiem (ir patiess), nākošo vērtības vairs nav svarīgas, jo viss izteikums ir patiess. Lasīt pārējo šī ieraksta daļu »

Paradigmas maiņa – SQL ātrdarbības atslēga

aprīlis 3, 2009

Strādājot ar lielākiem datu apjomiem, datubāzēs parasti rodas problēmas ar ātrdarbību. Viens no izplatītiem iemesliem (par ko es rakstīju arī savās 7 lietās, ko nevajag darīt) ir datu uztveršana un darbības ar tiem pa ierakstiem un nevis kopām. Protams, ne es pirmais, ne pēdējais, kas izrunā šo maģisko vārdu savienojumu “apstrādāt datus datubāzē KĀ KOPAS“, tikai, skatoties uz komentāru pie šī paša raksta, acīmredzot ir problēmas saprast, ko tas īsti nozīmē. Tad nu mēģināšu paskaidrot sīkāk, ko nozīmē datus uztvert kā kopas un ar ko tas atšķiras no tradicionālās programmēšanas pieejas. Tā lūk ir paradigmas maiņa no viena veida datu uztveres uz citu.

Tradicionālā programmēšanas pieeja

Tradicionālajā programmēšanā darbība vienlaicīgi notiek ar vienu ierakstu. Tas var būt vienkāršs ar vienu vienkāršu lauku, piemēram, skaitlis 17, tas var būt sarežģītāks, piemēram, adreses objekts, kurš sastāv no 10 sīkākiem laukiem, bet objekts parasti ir viens. Un tas viens arī tiek vienlaicīgi apstrādāts. Dabiski, ka programmētājam, sākot darboties ar datubāzēm, šāda pieeja tiek pārnesta arī tur. Programmētājs, rakstot pieprasījumus datubāzei (gan datu atlases, gan mainīšanas), vienlaicīgi “redz” tikai vienu ierakstu. Tas var būt sarežģītāks, mazāk sarežģīts, bet tikai vienu. Šāda pieeja nerada nekādas vai rada relatīvi mazas problēmas tradicionālu ievades/rediģēšanas formu gadījumā, jo tajās lietotāji arī parasti strādā ar vienu vai dažiem ierakstiem un parasta programmētāja pieeja “redzot” tikai vienu ierakstu neko daudz neskādē, vai pat bieži nekāda cita pieeja vispār nav izmantojama. Taču problēmas rodas lielāku datu kopu apstrādē. Un tad ir jāveic izmaiņas domāšanā un jāpāriet uz..

Datu kopu pieeja

Šī pieeja ir svarīga un vislielākos ieguvumus dod, ja tiek apstrādāts relatīvi liels ierakstu skaits. Daži piemēri:

  • Atskaišu izveide;
  • Vienreizēja datu migrācija no vecās sistēmas uz jauno;
  • Regulāra datu pārnešana/sinhronizācija no vienas sistēmas uz otru;
  • Regulāra specifiska lielu apjoma datu apstrādes (dienas rezultātu apstrāde, noteiktā laika periodā ievadīto datu kvalitātes pārbaude) utt.

Izmantojot tradicionālo viena ieraksta pieeju, rezultāta ieguve ir aptuveni šāda:

Ciklā pa nepieciešamajiem ierakstiem atskaitei vai apstrādei
  Veicam katram ierakstam nepieciešamās darbības
    (kolonu aprēķini, kvalitātes pārbaudes utml)
  Papildinam gala summu vai saglabājam rezultātu datubāzē

Ko tas nozīmē no ātrdarbības viedokļa? Rezultāta ieguve ir tieši proporcionāla apstrādājamo datu apjomam. Ja katra ieraksta apstrāde mums prasa nieka 0,1 sekundi, tad pie ierakstu skaita 1 miljons (kas nav nekas īpašs) rezultāta ieguve prasīs 1 000 000 * 0,1 sekundes ≈ 1667 minūtes ≈ 28 stundas. Tātad, ja reizi diennaktī Jūs saņemat šādu datu apjomu, tad diemžēl ir liela problēma, jo datu apstrāde aizņem vairāk nekā diennakti. Pat, ja šāds datu apjoms ir reizi nedēļā, patērētais laiks nav īpaši iepriecinošs. Un ko darīt, ja datu apjoms var tikai pieaugt? Tādā gadījumā ir jāveic principiālas izmaiņas un jāpāriet no domas ierakstu pa ierakstiem uz domu par visiem ierakstiem reizē, un tātad:

  • Kā visiem ierakstiem reizē iegūt atskaites kolonu vai vēl labāk vairākas, vai vislabāk visas kolonas?
  • Kā veikt noteiktu kvalitātes pārbaudi nevis vienam ierakstam, bet kā to paveikt visiem ierakstiem reizē? Vai sliktākajā gadījumā ierakstu daļai?
  • Kā ierakstīt tabulā nevis viena ieraksta datus, bet kā ierakstīt visus uzreiz.

Tātad domāt kopās nozīmē nevis

  • paņemt kārtējo ierakstu un izpildīt tam 17 pārbaudes un tad to izmētāt pa 10 tabulām,

bet

  • visiem ierakstiem reizē izpildīt pārbaudi 1, 2, … , 17 un tad visus ierakstus reizē ielikt tabulā 1, 2, …, 10.

Tātad domāt kopās nozīmē nevis

  • paņemt kārtējo ierakstu un izrēķināt tam visas atskaites kolonas, pieskaitīt tās starprezultātam un beigās izdot gala rezultātu,

bet

  • izrēķināt kolonu A visiem ierakstiem, izrēķināt kolonu B visiem ierakstiem, optimālā gadījumā šos rēķinus apvienojot vienā vaicājumā.

Protams, nevajadzētu šo pasākumu kaut kādā veidā nomaskēt, piemēram, ar lietotāja definētu funkciju, kas tiek izsaukta katram pārbaudāmajam ierakstam un kurā, savukārt, atkal iekšā ir ntie vaicājumi. Šādā veidā ieguvums vai nu nebūs nekāds, vai relatīvi niecīgs.

Iespējams arī, ka Jūs nevarat VISUS ierakstus apstrādāt vienlaicīgi, t.i., atkarībā no ieraksta veida un datiem veicamās darbības ir ļoti atšķirīgas. Tādā gadījumā Jūs noteikti varat ierakstus sadalīt loģiskās daļās, kur visiem vienas daļas ierakstiem veicamā darbība ir vienāda.

Kāds ir ieguvums?

Galvenais ieguvums ir tāds, ka izveidotais process vairs nav tieši proporcionāli atkarīgs no ierakstu skaita. Kāpēc? Tam ir vairāki iemesli:

  • Protams, ka palielinoties ierakstu skaitam, izveidotajiem SQL teikumiem būs jāapstrādā lielāks datu apjoms, taču parasti šis process nav tieši proporcionāls datu apjomam, tas ir – ir iespēja datus apstrādāt ātrāk nekā tieši proporcionāli.
  • Nav nepieciešams atkal un atkal lasīt vienus un tos pašus datus (klasifikatorus vai citus references datus), kas neglābjami būs, ja notiks apstrāde ierakstu pa ierakstam.
  • Nav nepieciešams atkal un atkal izpildīt vienus un tos pašus mazos SQL teikumiņus, kas katrs prasa maz laika, bet kopsummā tomēr laiks salasās.
  • Nav nepieciešams mētāties no procedurālās programmēšanas vides uz SQL vidi. Oraclē tas ir PL/SQL un SQL, citur tas var būt kaut kas cits, bet ideja nemainās un laiks tiek patērēts.

Laika izteiksmē ieguvums, protams, ir atkarīgs no datu daudzuma, izejas stāvokļa un gala stāvokļa, cik viens un otrs ir bijis optimāls. Bet ir redzēti gadījumi, gan kad ieguvums ir bezgalīgs, jo nevienam nebija pacietības sagaidīt rezultātu no ierakstu pa ierakstam pieejas 😉 , un arī tādas reizes, kad ieguvums ir bijis vismaz par kārtu.

Tālākā lasāmviela


MySQL profiler

februāris 23, 2009

Es ik pa laikam iedomājos vai MySQLā arī var lietot līdzīgas iespējas, kā piemēram Oracle autotrace, lai varētu mērīt izpildes laiku, paveiktos soļus un kaut kādu statistiku vaicājumiem, un lai salīdzinātu, kas tad izpildījās ātrāk un kāpēc. Tad nu izrādās, ka jau vismaz kopš 2007. gada pavasara eksistē tāda lieta, kā MySQL profiler, kas lielā mērā to nodrošina. Jau pašā sākumā jābrīdina, ka MySQL ir visai dīvaina DBVS un visai dīvainas izstrādes metodes vismaz no mana skatupunkta, jo pirmkārt MySQL Profiler nav pieejams MySQL Enterprise Server un notiek arī tāda mistiska dīvainība, ka vecākās versijās ir, bet jaunākās nē, piemēram, 5.0.37 ir, bet 6.0.4 nav (uz to arī es uzrāvos un brīnījos kā gan tas nākas), savukārt pēdējā novelkamajā 6.0.9. alpha atkal ir. Tā kā iespējams ne visiem lietotājiem būs tā iespēja šo rīku arī izmantot. Vēl tāds interesants fakts, ka šajā brīdī meklējot Googlē MySQL profiler latviskās lapās (tas ir pirms šī raksta, ko pašlaik lasat, publicēšanas) tiek atrasti 17 rezultāti, no kuriem tieši 1 ir kādā sakarā ar meklēto frāzi.

Kā jālieto?

Profiler sākšana un beigšana ir ļoti vienkārša. Lai sāktu, rakstam:

mysql> set profiling=1;
Query OK, 0 rows affected (0.00 sec)

Lai beigtu, rakstam:

mysql> set profiling=0;
Query OK, 0 rows affected (0.00 sec)

Lai noskaidrotu, kādā stāvoklī esam:

mysql> select @@profiling;
+-------------+
| @@profiling |
+-------------+
|           0 |
+-------------+
1 row in set (0.00 sec)

Ko ar to var redzēt?

Lasīt pārējo šī ieraksta daļu »


7 lietas, ko nevajag darīt

janvāris 23, 2009

Arī mani ir sasniegusi sērga par 7 lietām, kas neesot par mani zināmas. Bet nu nekā nebija, es šeit netaisos pagaidām apspriest savas privātās lietas vai kaut ko tādu, kas pārāk tālu attālinās no datubāzēm un ar to saistīto. Ja vien mani nenovedīs līdz tam briestošo priekšvēlēšanu laikā 😉 Bet tagad nē. Tagad Jums būs 7 lietas saistītas ar datubāzes ātrdarbību (faktiski lēndarbību), ar kurām visām  es pašlaik cīnos vai man ir nācies cīnīties un par kurām varu izteikties tikai [cenzēts], [cenzēts], [cenzēts].

  1. Nelietojiet SQL vaicājumos lietotāja definētas (user-defined) funkcijas. Nelietojiet tās lieliem datu apjomiem. Vēl vairāk nelietojiet tās, ja tajās atkal kaut kas tiek lasīts no datubāzes. Un tas attiecas tikpat labi uz Oracle, kā uz SQL serveri. Izbaudu to uz savas ādas gandrīz katru reizi, kad nākas cīnīties ar bremzīgiem Select vaicājumiem. Kāpēc? Oracle tas ir tāpēc, ka vaicājums tiek nosacīti izpildīts zem SQL dziņa (SQL engine), savukārt jebkurš lietotāja definēts funkcijas izsaukums prasa PL/SQL dzini (PL/SQL engine) un mētāšanās no viena uz otru ir relatīvi dārgs process. To pašu esmu novērojis SQL Server, nezinu precīzu tehnisku skaidrojumu, bet tā vien izskatās, ka tur ir kaut kas ļoti līdzīgs.
  2. Nelasiet no datubāzes liekus datus. Nerakstiet SELECT * no 50 tabulām (jā 50!), rakstiet tikai tās kolonas no tām tabulām, ko Jums vajag. Tas ir pilnīgs ārprāts, ja Jūs atlasat 80 kolonas, jā 80! un pēc tam attēlojat 10. Un tas viss tikai tāpēc, ka šis vaicājums tagad derēs visās 17 vietās, kur Jums kaut ko no tā vajag attēlot. Autors par laimi (viņam) bija jau atlaists, citādi tas noteikti beigtos ar asinsizliešanu 😉
  3. Nekārtojiet liekus datus. Ja nu ir jākārto (ORDER BY) kādi dati, tad vienmēr raugieties, lai kārtojamo kolonu skaits nebūtu vairāk nekā nepieciešams. Nerakstiet SELECT * ORDER BY <1, 2, 3 kolonas>, ja izrādās, ka * nozīmē 20 kolonas no kurām tālāk Jūs lietojat tikai 10. Katra lieka kolona ir lieki dati, kas datubāzes serverim jātur atmiņā un jāvazā līdz kārtošanas laikā. Tā ir papildus atmiņa, papildus apstrādes laiks un bezjēdzīgi iztērēti resursi.
  4. Neatlasiet unikālās kolonas (DISTINCT jeb UNIQUE) tikai tāpat vien, drošības pēc. DBVS nezin, ka tas ir tikai tāpat, aiz nekā darīt. Ja tas patiesi ir pārāk bieži nepieciešams, tad diezgan droši tā ir kļūda datubāzes modelī. Kāpēc to nevajag? Tāpēc, ka katrs DISTINCT prasa unikālo ierakstu atlasi un jo vairāk kolonas tiks atlasītas, jo vairāk atmiņas un resursu tam būs nepieciešami. Ja tas ir izdarīts uz ierakstu kopu, kas jau tāpat ir ar unikāliem elementiem, tad tie ir burtiski zemē nomesti CPU cikli, atmiņas operācijas un iespējams pat diska apgriezieni.
  5. Nerakstiet dinamisko SQLu ar iešūtām mainīgo vērtībām. Datu bāzu vadības sistēmās (Oracle, MS SQL Server), kuru izstrādātāji ir ilgi pūlējušies, lai izveidotu kopīgu atmiņas apgabalu visiem SQL teikumiem (attiecīgi shared pool un procedure cache), maz kas var būt paradoksālāk kā nezinoši vai piedodiet par izteicienu stulbi izstrādātāji, kas katru savu SQL teikumu uzģenerē ar iešūtām mainīgo vērtībām, līdz ar to visus sākotnējos pūliņus izslaukot miskastē. Iešūtas mainīgo vērtības nozīmē praktiski nulles varbūtību, ka izveidotais SQL teikuma izpildes plāns būs lietojams arī nākošajam lietotājam, jo viņa SQL teikums būs gandrīz tāds pats, bet tikai gandrīz. Tajā atšķirsies iešūtie identifikatori vai arī kādi citi mainīgie un hopsā – tas vairs nav tas pats SQL teikums, kuram izpildes plāns jau bija zināms. Tā ir viena no tūkstoš hidras galvām, kam atkal jāpārbauda tiesības uz objektiem, jāģenerē savienojumu iespējamās kombinācijas un viss SQL teikuma izpildes plāns. Sīkums vienam lietotājam, kas reizi pusstundā palaiž SQL teikumu uz 15 minūtēm, bet nežēlīga sāpe kaut vai 10 lietotājiem, kuri katrs izpildītu desmitiem vai pat simtiem SQL teikumu sekundē, ja vien katram SQL teikumam nebūtu analīzes (parse) fāze, kas konstanti aizņem sekundi…
  6. Nelietojiet procedurālo valodu ciklus, lai apstrādātu ierakstus pa vienam, jo sevišķi, ja iterāciju skaits ir proporcionāls datu apjomam. Visizplatītākā lieta – kursori. Mans mazais skaistais kursoriņš, kurš izpildās zibenīgi uz dažiem ierakstiem, pārvēršas par milzīgu nekustīgu monstru, ja tas tikpat akli dodas caur reālajiem produkcijas miljons ierakstiem. Cikli, kuru iterāciju skaits ir tieši proporcionāls datu daudzumam nav savienojami ar vārdu ātrdarbība. Tie var būt tikai un vienīgi atbilstoši vārdam lēndarbība.
  7. Testējiet uz reālu datu apjomu, kādu Jūsu sistēma sasniegs pēc gada, diviem. Tas, ka Jūsu vaicājums izstrādes vidē uz 10 ierakstiem atgriež funkcionāli pareizu rezultātu zibenīgi ir nekas. Burtiski nekas. Sliktākajā gadījumā saģenerējiet datus pats, ja esošus reālus daudz nevar dabūt. Jo tad, kad Jūsu vaicājums sastapsies ar miljons ierakstiem, kam nāksies katram izsaukt lietotāja definētu funkciju, nolasīt visu ierakstu 50 kolonas, visu miljonu sakārtot dēļ liekā Distinct, un to darīs vismaz 10 lietotāji vienlaicīgi palaižot jūsu dinamiski ģenerēto SQL teikumu, lūk tad iestāsies brīdis, kad klients pacels cepuri un dosies pie Jūsu konkurentiem. Jo diemžēl lielāks dzelzis nederēs. Neviens dzelzis pie kaut cik ievērojama datu apjoma nespēj pārciest šeit uzskaitīto, tā lai lietotāji nesāktu dusmās vārīties un klusiņām lādēt izstrādātāju.

Man principā diez ko nepatīk visādas ķēdes un piramīdas, galu galā Medofs arī slikti beidza 🙂 , taču gribu pievērst lasītāju uzmanību vienam visnotaļ interesantam un (cerams, ka arī turpmāk) daudzsološam blogam, kurš raksta arī par lietām, kas saistās ar tīmekli un ātrdarbību.

Lasīt arī: