Windows - Védelem a blokkolásoktól

Adatbázis optimalizálás 10. rész

Sorozatunk jelenlegi részében a zárolással, tranzakció kezeléssel foglalkozunk. A cikkben mindent a blokkolás szempontjából vizsgálunk meg. Koncentráltan foglalkozunk az alkalmazásfejlesztés során lehetséges buktatókkal. Sorra vesszük, hogy milyen esetekben szokott blokkolás előállni.
Blokkolás akkor történik, amikor egy alkalmazás kapcsolata zárolást helyez el, és egy második kapcsolatnak ezzel ütköző zárolástípusra van igénye. Ez a helyzet arra készteti a második kapcsolatot, hogy várjon, azaz blokkolja őt az első kapcsolat. Egy kapcsolat blokkolhat más kapcsolatot tekintet nélkül arra, hogy ők ugyanabból vagy más alkalmazásból származnak-e, különböző kliens gépekről.
Néhány művelet, amely zárolást igényel, nem mindig magától értetődő, pl. zárolás a rendszertáblákon, indexeken.
A legtöbb blokkolási probléma abból adódik, hogy egy folyamat rendkívül hosszú ideig tart zárolva valamit, így blokkolási folyamatláncot idézve elő, valamennyi folyamat egy másikra vár a zárolással.
Általános blokkolási esetek a következők:
  • Lekérdezések túl hosszú futtatási idővel való indítása. Egy hosszan futó lekérdezés blokkolhat más lekérdezéseket. Például egy DELETE vagy UPDATE művelet, amely több sort is érint, több zárolást is elhelyezhet, zárolhat más lekérdezéseket. Erre az esetre gondolva ne akarjunk egyszerre használni hosszú futású lekérdezéseket és OLTP–t (online tranzakciós feldolgozású lekérdezéseket) ugyanabban az adatbázisban. A megoldás az, hogy keressünk a lekérdezések optimalizálására lehetőségeket, váltsunk indexeket, felosztva egy nagy, komplex lekérdezést kisebbekre, vagy a lekérdezéseket olyan időben futtatva, amikor nem zavarják egymás működését, vagy használjuk őket más számítógépen. Egy lehetséges eset az, ha a lekérdezések hosszú lefutásúak, és ezáltal blokkolást eredményeznek, ha nem megfelelően használjuk a kurzorokat. A kurzorok kényelmes megoldást nyújtanak az eredménytáblában történő mozgásra, de használatuk lassabb, mint az összeállításhoz igazodó lekérdezéseké.
  • Azon lekérdezések leállítása, amelyek nem érvényesítettek, vagy nem történt meg a visszagörgetésük. Ilyen akkor történhet, ha az alkalmazás leállít egy lekérdezést; pl.: az ODBC sqlcancel függvényének használatával, anélkül, hogy a szükséges számú ROLLBACK és COMMIT utasítást meghívnánk. A lekérdezés megszakítása automatikusan nem görgeti vissza, vagy érvényesíti a tranzakciót. Minden zárolás, amely a tranzakción belül jött létre, visszatartott lesz, miután a lekérdezés megszakításra kerül. Az alkalmazásoknak kell pontosan kezelni a tranzakció illeszkedési szintjeit érvényesítve, vagy visszagörgetve a leállított tranzakciókat.
  • Alkalmazások, amelyek nem dolgozzák fel teljesen a rendelkezésükre álló eredménytáblákat. Miután egy lekérdezést küldünk a kiszolgálónak, minden alkalmazásnak azonnal el kell hoznia a szerverről valamennyi eredménysorát. Ha egy alkalmazás ezt nem teszi meg, zárolás marad a táblán, blokkolva más felhasználókat. Ha programunk nyíltan T-SQL utasításokat küld a szervernek, akkor minden eredménysort el kell hoznia. Ha nem ezt teszi (és ha nem állítható be, hogy ezt tegye), akkor képtelenek leszünk megoldani a blokkolási problémát. Ahhoz, hogy elkerüljük ezt, korlátoznunk kell ezeket az alkalmazásokat egy értesítő, vagy döntés-támogató adatbázissal.
  • Elosztott kliens/szerver deadlock-ok. Egy eredeti deadlock-al szemben, egy elosztott deadlock-ot automatikusan nem képes észlelni az SQL szerver. Egy elosztott kliens/szerver deadlock akkor jön létre, ha az alkalmazás több kapcsolatot nyit az SQL szerverhez, és aszinkron küld el egy lekérdezést. Például egy egyedi kliens alkalmazási szálnak két nyitott kapcsolata van. Aszinkron módon indul egy tranzakció és jön egy lekérdezés az első kapcsolaton. Az alkalmazás ezután elindít egy másik tranzakciót, futtat egy lekérdezést a másik kapcsolaton, és várja az eredményt. Amikor az SQL szerver visszaadja az eredményt az első kapcsolaton, az alkalmazás elkezdi azt feldolgozni. Az alkalmazás addig folytatja a feldolgozást, amíg nincs újabb szabad eredmény, mivel a lekérdezés, amelyik az eredményt szolgáltatta, blokkolódik a másik kapcsolaton keresztül küldött lekérdezés által. Ezen a ponton az első kapcsolat blokkolt lesz, meghatározatlan ideig vár az újabb eredmények feldolgozására. A második kapcsolatot nem blokkolja le zárolás, de megpróbál eredményt visszaadni az alkalmazásnak. Mivel az alkalmazás blokkolt és eredményre vár az első kapcsolaton keresztül, a második kapcsolaton keresztül érkezett eredmény nem kerül feldolgozásra.
Ez utóbbi kezeléséhez használjunk:
  • Egy lekérdezési időkorlátot (timeout) minden query-hez.
  • Egy zárolási időkorlátot minden query-hez.
  • Egy összefűzött kapcsolatot.
Az SQL szerver lényegében a kliensalkalmazás bábja. A kliensalkalmazásnak majdnem teljes kontrollja (és felelőssége) van az elhelyezett zárolások felett a szerveren. Habár az SQL szerver zároláskezelője automatikusan használja a zárolásokat a tranzakciók védelmében, erre őt a kliens alkalmazás által küldött lekérdezés-típus és az eredmény feldolgozásának módja sarkallja. Ekképpen a legtöbb blokkoló problémáról szóló döntéssel együtt jár a kliensalkalmazás megtekintése.
Egy blokkolási problémához gyakran egyaránt szükséges az alkalmazás által elküldött SQL utasítás megtekintése, valamint az alkalmazás viselkedésének vizsgálata a kapcsolatkezelés szempontjából, valamennyi eredménysor feldolgozása, stb. Ha a fejlesztőeszköz nem teszi lehetővé a nyílt kontrollt a kapcsolatkezelés felett, akkor a lekérdezés-időkorlát, eredmény-feldolgozás, blokkolási problémák nem lesznek megoldhatók.
Az irányelvek a blokkolás-mentes alkalmazástervezéshez az alábbiak:
  • Ne használjunk, vagy tervezzünk olyan programot, amely engedi a felhasználóknak, hogy kitöltsenek olyan editbox-okat, amelyek hosszú lefutású lekérdezéseket generálnak. Például ilyen lehet, ha a felhasználót bevitelre ösztönözzük, de számos üres beviteli mezőt hagyunk üresen, vagy egy helyettesítőt adunk meg. Ez okozhatja azt, hogy az alkalmazás extrém hosszú lefutási idejű lekérdezést indít, ezáltal blokkolást okozva.
  • Ne használjunk, vagy tervezzünk olyan alkalmazást, amelyben tranzakció kezeléssel ellátott felhasználói bevitel van.
  • Tegyük lehetővé a lekérdezés megszakítását.
  • Használjunk lekérdezés-, vagy zárolás-időkorlátot, hogy megállítsuk az elszabadult lekérdezéseket és megelőzzük az elosztott deadlock-okat.
  • Azonnal hozzunk el minden eredménysort, amint azok elkészültek.
  • A tranzakciók legyenek minél rövidebbek, amennyire csak lehetséges.
  • Legyen határozott felügyeletünk a kapcsolatkezelés felett.
  • Végezzünk stressz-tesztet az alkalmazáson teljesre tervezett felhasználói terheléssel.

Adatbázis optimalizálás cikksorozat