Soci (Soczó Zsolt) szakmai blogja

2008.03.30.

SQL Server 2008 újdonságok 27. – művelettel egybekötött értékadó operátorok

Filed under: Adatbázisok,SQL Server 2008,Szakmai élet — Soczó Zsolt @ 21:53

C nyelveket kedvelők figyelem! Hiányzott már a += és társai a TSQL-ből? Hát itt van!

declare @a int = 5
set @a = @a + 2 --régi iskola
print @a -- 7
set @a += 3     --új módszer
print @a -- 10

Persze az össze matematikai és bitenkénti operátorra megy. Nem esünk hasra tőle, de örülünk, hogy eljutottunk ide. Csak hogy a csudába nem tudták még megcsinálni, hogy egy változó típusaként meg lehessen adni egy tábla oszlopát, így csökkentve a redundanciát a kódban? Ez olyan nagy dolog?
Meg aztán a @i++ is beleférhetett volna. Én már csak ilyen C agyú lény vagyok.

2008.03.28.

Mikor és hogyan működik a Minimal Logging (bulk műveletek) az SQL Serverben?

Az közismert, hogy a BULK INSERT parancs és a bcp.exe vagy az SqlBulkCopy osztály gyorsabban tud adatokat betölteni egy táblába, mint a sima insert. Méréseim szerint pár 10x gyorsabban. Azért gyorsabb, mert nem minden sort logolnak, csak nagyobb allokációs egységeket. Eddig ez ok. Ami nem, hogy ez messze nem minden helyzetben megy, sokszor átcsap teljesen logolt működésbe. Nem arra a triviára gondolok, hogy Full Recovery modell esetén így működik, az ok. Hanem az indexek is befolyásolják, hogy mikor lesz simán logolt egy művelet. Pl. ha van Clustered index a táblán és van benne adat, akkor nem lesz logolt a művelet, punktum!
Tessék megnézni a cikk végén a táblázatot.

Update: SQL Server 2008-ban ez is változik, majd írok róla később. Legyen elég annyi, hogy az insert-select is minimálisan logolt lesz. :)

SQL Server 2008 újdonságok 26. – FORCESEEK table hint

Filed under: Adatbázisok,SQL Server 2008,Szakmai élet — Soczó Zsolt @ 10:38

Mint minden optimizer hint előtt figyelmeztetés: ne használd, csak ha érted mit csinál, és biztosan tudod, hogy az adott helyzetben segít, és hogy az adatok eloszlása nem változik meg jelentősen az idők folyamán, ami miatt már ront, és nem javít a hint.

A szerver lekérdezés optimalizálója általában ügyesen kiválasztja a szükséges indexet egy szűrés hatékony végrehajtásához, azonban vannak esetek, amikor
1. nem elég bevállalós
2. felhasználja a cache-elt tervet, de ezzel rosszul járunk.

Ezekben az esetekben hasznos lehet a forceseek. Némi háttér a megértéshez.

A nonclustered (NC) index egy B* fa, amelyben csak az indexhez megadott oszlop(ok) adata szerepel rendezetten, a tényleges adatok az indexfától elkülönült adatterületen helyezkednek el. Emiatt, ha mondjuk egy where feltételhez szeretné használni a szerver az indexet, először végigszalad az NC index fán, ami nagyon gyors művelet, hisz rendezett a fa. A levélszinten talált egy vagy több sort pedig úgy szedi fel, hogy átnyúl az indexben található pointer mentén az adatlapokhoz, és kiolvassa az ott található sorokat. Ezt Bookmark Lookup operátorként láthattuk az SQL 2000-ben, 2005-ben JOIN-ként jelenik meg, most 2008-ban pedig Key Lookupként látom (ez a legtalálóbb eddig).
Sok sor esetén ez jó nagy ugrabugrával jár, hisz pl. 100 sor esetén extrém esetben 100 lapon vannak szétszórva az adatok, így 100 lapot be kell tölteni a memóriába, 1-1 nyomorult sor miatt. Ezért a szerver nem lelkesedik alapban az NC indexért, igen konzervatívan él csak vele. Sokszor inkább végigolvassa a teljes táblát, read ahead módon, néhány nagy harapással, semmint hogy apró 8k-s laponként zötyögtesse a vinyók fejét. A visszafogottsága sokszor megalapozott, de néha nem.
Az elején felvetett 1. esetben például lehet, hogy egy 10e soros táblából 200 sort leválogatva már nem index seek-el (azaz az előbb leírt módon használja az indexet), hanem table scan-t csinál. Ennek lehet oka egy rossz statisztika is persze, ami miatt azt hiszi, hogy sokkal több sor jön vissza, ezért mindig KELL frissíteni a statisztikákat, mielőtt hintelnénk (sp_updatestats és UPDATE STATISTICS). Viszont lehet, hogy csak egyszerűen nem merte bevállalni az index seeket, helyette scan-elt.
A 2. eset nem egyenletes eloszlású adatok vagy nagyon változó paraméterű szűrések esetén okozhat problémát. Tegyük fel, hogy egy táblában megrendelések vannak és ezek dátumára nagyon gyakran szűrnek, de mindig úgy, hogy csak egy adott nap megrendeléseire kíváncsiak. Mivel ez kis részét hozza vissza a táblának index seeket használ a szerver, ha van a megrendelés dátuma oszlopon egy akármilyen index. Cél, hogy ezek a napi lekérdezések nagyon gyorsak legyenek, a nagyobb tartományra lekérdezés sebessége első körben nem fontos (értsd, nem baj, ha rosszabb lesz a bűvészkedésünk hatására, mint volt).
Ha azonban egy kolléga szerver újraindulás vagy cache ürülés után valamiért mondjuk egy évet válogat le, akkor teljes táblaolvasást fog használni a szerver. Ez rendben is van, jó döntés, de a végrehajtási terv letárolódhat a cache-ben, így ezek után lehet, hogy az 1 napi szűrések is scan-elni fognak, azaz piszok lassúk lesznek. Erre persze sokféle megoldás adható, a forcessek csak az egyik ezek közül.

Lássuk az alapproblémát:

create nonclustered index idx_orderdate on Purchasing.PurchaseOrderHeader(OrderDate)
go

create proc GetOrdersByOrderDate
  @start datetime,
  @end datetime
as
SELECT * FROM Purchasing.PurchaseOrderHeader
where OrderDate >= @start and OrderDate < @end
go
&#91;/source&#93;

&#91;source='sql'&#93;
exec GetOrdersByOrderDate '20030317', '20040318'
&#91;/source&#93;
Ez egy évi adat, a tábla majdnem fele lejön, persze, hogy table scan lesz a vége, 44 IO művelettel, ennyi lapból áll a tábla:

<a href='http://soci.hu/blog/wp-content/uploads/2008/03/scan1.png' title='Index scan sok adat miatt'><img src='http://soci.hu/blog/wp-content/uploads/2008/03/scan1.png' alt='Index scan sok adat miatt' /></a>

A baj az, hogy a következő lekérdezés is scan-el, nem seekel, mert felhasználja az előbbi tervet, pedig csak 8 sort hoz vissza:

exec GetOrdersByOrderDate '20030317', '20030318'

Mit lehet tenni? A legegyszerűbb, ha kikényszerítjük az újrafordítást minden egyes futtatáskor vagy az sp létrehozásakor (régi fícsör):

exec GetOrdersByOrderDate '20030317', '20030318' with recompile

Seek a recompile kikényszerítése miatt

Viszont sokszor pont azért hoztunk létre sp-t, hogy ne legyen recompile. SQL Server 2005-től használható az optimize for hint:

alter proc GetOrdersByOrderDate
@start datetime,
@end datetime
as
SELECT * FROM Purchasing.PurchaseOrderHeader
where OrderDate >= @start and OrderDate < @end option(optimize for (@start = '20030317', @end = '20030318')) [/source] Azt mondtuk a szervernek, tök mindegy milyen paraméterrel hívják meg az sp-t, a két paraméter értékét tekintsd annak, amit odaírtam, és ennek megfelelően optimalizálj. Direkt egy napnyi, pár sornyi adatot adtam meg, ami miatt index seek-et használ a szerver, ami stabilan gyorsan tartja a napi lekérdezéseket, de nagyon lelassítja a nagy tartományt, mert arra is seek lesz, ami miatt az előbbi 44 helyett 3458 IO műveletet kapunk. Az előbbi megoldás tehát akkor jó, ha nem nagyon vannak nagy tartománybeli lekérdezések. Ha abból is sok van, akkor vagy recompile kell mindig, vagy kell két sp, más paraméterekre optimalizálva, és a kliens okosan a megfelelőt hívja meg. Másképp is ki lehet kényszeríteni a seeket: [source='sql'] alter proc GetOrdersByOrderDate @start datetime, @end datetime as SELECT * FROM Purchasing.PurchaseOrderHeader with(index(idx_orderdate)) where OrderDate >= @start and OrderDate < @end [/source] Azaz rákényszerítjük, hogy használja az NC indexünket. Az optimize for megoldás hátránya, hogy ha az adatok változnak a táblában, lehet, hogy érvénytelen lesz a mintaként megadott érték, így rosszul dönt a szerver az adatelérés módjáról. A with index hint pedig belevarrja az index nevét a lekérdezésbe, így ha az megváltozik vagy az index törlése kerül, a lekérdezés hibázni fog. No, lássuk most már, mire is jó a FORCESEEK: [source='sql'] alter proc GetOrdersByOrderDate @start datetime, @end datetime as SELECT * FROM Purchasing.PurchaseOrderHeader with(forceseek) where OrderDate >= @start and OrderDate < @end [/source] Ez ugyanúgy ráveszi a szervert a korábbi indexünk használatára, seek-elve, de nem mondja meg, hogy milyen indexet használjon. Történetesen adtunk neki olyat, amit használhat, él is vele. Azaz ezzel egyfajta noszogatást adtunk neki, hogy nézz szét jól, van ott olyan index, ami jó neked seek-elve is. Ha gonosz módon ledobjuk az indexet, nagyot néz a szerver, és ezt mondja: Query processor could not produce a query plan because of the hints defined in this query. Resubmit the query without specifying any hints and without using SET FORCEPLAN. No, igen, ennek is vannak kötöttségei, ha egyszer nincs megfelelő index, akkor mi a francot erőltetjük, nem? Összegezve, az FORCESEEK egy újabb eszköz az optimalizálási repertoárunkban, semmiképpen se csodafegyver, illetve látható, hogy a hatását másképp is el lehet érni. Egy újra trükk, amit bevetünk, HA INDOKOLT. De csak akkor.

2008.03.27.

SQL Server 2008 újdonságok 25. – Large user-defined types and aggregates

Filed under: Adatbázisok,SQL Server 2008,Szakmai élet — Soczó Zsolt @ 14:08

Saját típusok és aggregáló függvények esetén maximum 8 kbyte lehetett az adat tárolásához szükséges adatmennyiség mérete. Egy string összefűző UDA emiatt pl. hamar meghalt már pár sornyi adattól is.
Vagy, ha mondjuk szeretnénk egy UTF-8 kódolású string típust írni, akkor is kellemetlen volt a 8k limit (az egyik példa egyébként erről szól).
Mivel jelezzük, hogy sok adatunk lesz?

“SqlUserDefinedTypeAttribute.MaxByteSize field is set to -1, the serialized UDT can be as large as the large object (LOB) size limit (currently 2 GB).”

Csak saját serialization esetén oldották fel a limitet, azaz, ha implementáljuk a System.Data.IBinarySerialize interfészt, és mi pumpáljuk ki és be az adatokat. Natív serialization esetén továbbra is 8k a limit. Persze, ha valaki ilyen nagy típus készít, elég valószínű, hogy lesz benne string is, ahhoz meg úgyse jó a natív serializálás.

Annak ellenére, hogy laza lett a limit, nem érdemes memóriába berántani 120 egyenként 2 gigás sort. Inkább arra jó ez, hogy ha időnként túl kell lépni a 8k-s határt, akkor is működni fog a típusunk.

2008.03.05.

Windows 2008 termékbejelentés lement

Filed under: Élet,Személyes — Soczó Zsolt @ 22:59

Úgy vettem észre tetszett a közönségnek az IIS7, és egyáltalán, az egész Windows 2008 termék. Én még Magyarországon nem láttam az MStől ennyire színvonalas rendezvényt, gratulálok a szervezőknek, amolyan mini-teched hangulata volt.

Páran tudták, legtöbben nem, de életem legnehezebb előadása volt ez. Tegnap este fél hétkor autóbalesetben meghalt az egyik családtagunk, amikor tőlünk ment hazafelé. :((
Nagyon nehéz volt összekaparni magam az előadásra, ennyi sikerült, amit láttatok.

Köszönöm a résztvevők figyelmét.

2008.03.04.

Internet Information Services (IIS) 7.0 Manager

Filed under: IIS,IIS7,Szakmai élet — Soczó Zsolt @ 19:24

Na kérem, ezzel lehet már XP-ről is távolról, HTTPS-en keresztül reszelni egy Windows 2008 IIS7-et.

Holnap Windows 2008 launch

Filed under: Felhívás,Szakmai élet — Soczó Zsolt @ 19:07

Aki jelentkezett, jöjjön, jó lesz. 3 gépen fogok IIS7-et demózni, ezúttal nem kockáztattam laptopokkal, hanem virtuális gépeket használok. Én vagyok az utolsó, ígérem nem lógok bele a partyba. :)
Aztán, ha ezen túl vagyok, újra jönnek az SQL-es cikkek.

2008.03.03.

A magyar támogatás sikere

Filed under: Élet — Soczó Zsolt @ 15:16

Osztrák lesz a magyar találmány

“Nem kapott megfelelő támogatást a kizárólag elektromos meghajtással működő autók sorozatgyártására a Brixxon Elektromos Autó Kft. Magyarországon, ezért a társaság kitelepül Ausztriába, ahol tárt karokkal fogadták – mondta az InfoRádiónak a cég tulajdonos-ügyvezetője.”

Akinek nem tetszik, el lehet innen menni. Köszönjük, úgy látszik van, aki eképpen cselekszik. Szomorú ez, nem kicsit, nagyon.

Powered by WordPress