Iránymutatás gyors elsődleges kulcsként a különböző adatbázisokhoz

A GUID problémája

Történelmileg az adatbázis kialakításának legelterjedtebb módja egy adott sor azonosítására egy egész sorrend. Általában egy ilyen sorrendet generálnak a kiszolgálói oldalon, amikor új sor kerül beillesztésre. Ez az egyszerű és világos megközelítés számos alkalmazáshoz alkalmas.

Mindazonáltal vannak olyan helyzetek, amikor ez a megközelítés nem lesz jó. Az ORM keretrendszerek széles körű használatával a legtöbb felhasználó megpróbálja elkerülni az adatbázis oldalán felesleges bonyolultságokat, és az adatbázis összetevőjének kialakulása az ilyen összetettséghez tulajdonítható. Az adatbázisok többszöröse is nehézkes lesz, ha csak egy házon belüli szolgáltatásra támaszkodik a kulcsok generálásához. Külső forrásra van szükség ahhoz, hogy a kulcsok generálásával kapcsolatos függőségeket minimálisra csökkentsük.

A GUID legfontosabb előnye, hogy képes a kliens oldalán létrehozni, anélkül, hogy ellenőrizni kellene az adatbázis egyediségét. Első pillantásra ez ideális megoldás az egyedi kulcsok problémájára.

Mi a probléma?

A probléma a teljesítmény. A legjobb teljesítmény érdekében a legtöbb adatbázis tárolja a fürtöket fürtözött indexekben, pl. A táblázatok sorai a lemezen bizonyos sorrendben tárolódnak. Ez megkönnyíti a keresett karakterlánc keresését, ugyanúgy, mint az indexkeresés, de ugyanaz a mechanizmus miatt új érték beillesztése nagyon lassú, ha az új érték nem esik a lista végére. Például vegye figyelembe ezt a példát:

A 7. és 8. sorokat le kell tolni, hogy új rekordot teremtsenek. Nem probléma ebben az esetben, de amikor beszélünk erről a műveletről, amikor több millió sor van a táblában, akkor ez igazi probléma. És ha másodpercenként több száz betétet szeretne készíteni, akkor nagyon nehéz lehet.

Hogy ez a probléma GUID: ők lehet, hogy nem teljesen random, de a legtöbbjük úgy tűnik véletlen, abban az értelemben, hogy ezek rendszerint nem fontossági sorrendben. Ezért a GUID elsődleges kulcsként való használata a nagyméretű kulcsadatokban egy mauveton. Az új értékek beszúrása nagyon lassú lehet, és intenzív lemezes működéshez vezethet.

Következő GUID-ek

Tehát, milyen megoldást lehet alkalmazni? A fő probléma az, hogy a GUID-adatokban nagyfokú eltérés tapasztalható. Ebben az esetben megpróbálhatjuk a GUID-t következetesebbé és kiszámíthatóbbá tenni. A COMB megközelítés (a COMBined GUID \ időbélyegző összetétele) a GUID egy részét olyan értékkel helyettesítheti, amely garantáltan megnő, vagy legalábbis nem csökken minden új értékkel. Amint azt a SOMV definíciójából kitalálhatod, e célból az aktuális dátumtól és időponttól kapott érték érvényes.

Ennek szemléltetéséhez képzeljünk el egy sor szabványos GUID-t:

fda437b5-6edd-42dc-9bbd-c09d10460ad0
2cb56c59-ef3d-4d24-90e7-835ed5968cdc
6bce82f3-5bd2-4efc-8832-986227592f26
42af7078-4b9c-4664-ba01-0d492ba3bd83

Vegyük észre, hogy az értékek véletlenszerű sorrendben vannak, és valójában véletlenszerűek. Elegendő millió sor beszúrása nagyon hosszú lehet.

Most képzeljük el a speciális GUID-ek hipotetikus listáját:

00000001-a411-491d-969a-77bf40f55175
00000002-d97d-4bb9-a493-cad277999363
00000003-916c-4986-a363-0a9b9c95ca52
00000004-f827-452b-a3be-b77a3a4c95aa

A számjegyek első blokkját a számjegyek növekvő sorrendje váltotta fel - mondjuk az ezredmásodpercek számát a program kezdete óta. A formátumban egy millió sor beillesztése nem lesz olyan lehangoló, mivel minden egyes sor a lista végére illeszkedik, és nem igényli a meglévő adatok újrarendezését.

Most, hogy van egy alapvető koncepciójuk, nézzük meg, hogyan kaphatunk hasonló GUID-t, és hogyan működnek különböző adatbázisokban.

A 128 bites GUID négy fő blokkból áll: Data1, Data2, Data3, Data4 - amelyek a példán láthatóak:

A legtöbb ma használt algoritmus, és különösen a használt .Net keretrendszer, eredendően véletlenszám-generátorok. Ez jó hír számunkra, mivel ez azt jelenti, hogy a GUID különböző részeivel végzett kísérletek nem vezethetnek az egyediség integritásának megsértéséhez.

Sajnos az adatbázisok eltérően működnek a GUID-mel. Néhányuknak (MS SQL, PostgreSQL) beépített típusuk van a GUID használatához. A beépített támogatás nélküli DB-k a GUID-t használják, a 36 karakter hosszúságú szöveges mezővel együtt. Az Oracle általában egy nyers oszlopot használ egy nyers oszlopban (16).

További komplikáció az, hogy az MS SQL szervezi a GUID-t az utolsó 6 jelentős bájtban (az utolsó 6 bájt a Data4 blokkban). beleértve ha szekvenciális GUID-t akarunk létrehozni az SQL Serverben való használathoz, akkor be kell illeszteni az egymást követő részt a végén. Számos más rendszer elvárja, hogy az elején egy sorozatos részt vegyen fel.

Figyelembe véve azt a tényt, hogy az adatbázisok eltérően működnek a GUID-mel, nem létezhet egyetlen algoritmus sem, amely megfelel az összes igénynek. Szükséges lesz a létrehozási módszer kezelése, attól függően, hogy az adatbázis hogyan működik a GUID-mel. Néhány kísérlet elvégzése után három fő megközelítést azonosítottam, amelyeknek minden esetre kiterjednie kellene:

  • Szekvenciális GUID létrehozása karakterláncként
  • Szekvenciális GUID létrehozása bináris adatok formájában
  • Szekvenciális GUID létrehozása, a szekvenciális rész a MS SQL végén

(Miért nem lehet ugyanaz a GUID, mint egy húr, és egy sor bájt? Mert így Net kezeli GUID eltérhet a húr képviselete a little-endian rendszerek, és a legtöbb használó autók .Net little-endian. Részletek alább.)

A stratégia megválasztása a következőképpen jeleníthető meg:

a forrásnyomtatás megtekintése?

De mennyire fogunk megteremteni egy következetes GUID-t? Melyik részt hagyjuk "véletlenszerűen", és mi lesz az időbélyeggel? A COMB eredeti MS SQL specifikációja az utolsó 6 bájtot az időértékkel helyettesíti. Ez részben a kényelem felett van, mivel a 6 bájtot rendelésre használják, de az idő elegendő 6 bájtra. A fennmaradó 10 bájt elegendő lesz a véletlen komponens számára.

Tehát, amint említettük, elkezdünk 10 véletlen bájtot felvenni:

a forrásnyomtatás megtekintése?

var rng = új System.Security.Cryptography.RNGCryptoServiceProvider ();

byte [] randomBytes = új byte [10];

Véletlenszerű GUID összetevő létrehozásához használja az RNGCryptoServiceProvider osztályt. mivel a System.Random olyan funkciókat tartalmaz, amelyek alkalmatlanná teszik feladatunkat. Az általuk generált értékek bizonyos definíciójú mintáknak felelnek meg, és nem kezdődnek több mint 2 32 iteráció. Mivel a véletlenszerűségre támaszkodunk, igyekszünk minél pontosabb véletlen számot szerezni, és az RNGCryptoServiceProvider osztály ad ilyen lehetőséget.

Először is, a kullancsok visszatér a 64-bites egész, és már csak 48 bites, és ha megszabadulunk a két bájt, a fennmaradó 48-bit alapján 100 nanosekndnogo intervallummal legalább egy évvel korábban értékei elkezdenek ismételni. Ez elpusztítja azt a sorrendet, amelyet megpróbálunk létrehozni és megölni az általunk így reményben lévő adatbázis teljesítményét. Mivel a legtöbb alkalmazást egy évnél hosszabb ideig használják, érdemes más időméretet használni.

A jó hír az, hogy a két hibája értelemben kioltja egymást: korlátozott felbontás azt jelenti, hogy nem tudja használni a kullancsok a különbség, de nem tudjuk használni közvetve. Oszd meg a mező értékét 10000 értékkel, hogy megkapjuk a 48 bites összerendelés értékét. Megyek, hogy egy ezredmásodperc, mivel ez lehetővé teszi, hogy használja ezeket az értékeket, hogy 5800 évvel ezelőtt az érték fog menni a második fordulóban, azt hiszem, ez elég lesz számos modern alkalmazásokat.

Egy kis jegyzet, mielőtt beköltöznék. Az 1 milliszekundumos felbontás elegendő számos feladat elvégzéséhez. Kísérleteztek további számlálókkal és alacsonyabb felbontással, de nem sok értelme volt, mivel a különbségek minimálisak voltak. A bázisok tökéletesen megbirkóznak a GUID-ek százai által egy időbélyeggel, így ez nem jelent problémát.

a forrásnyomtatás megtekintése?

hosszú időbélyegző = DateTime.Now.Ticks / 10000L;

byte [] timestampBytes = BitConverter.GetBytes (időbélyeg);

Most van időbélyegzőnk. Mivel a BitConverter használatával bájtsorozatot kaptunk, ellenőrizni kell a rendszert egy sornyi bájtra.

a forrásnyomtatás megtekintése?

Általánosságban elmondható, hogy mindent elég jó, de érdemes figyelembe venni a .Net sajátosságait a GUID képviseletében. A keretrendszer számára ez nem egyszerű bájtsor. A GUID a 32 bites egész számot, 2 16 bites egészet és 8 egyedi bájtot tartalmazó struktúrát jelenti.

Mit csináljunk ezzel? A fő probléma ismét a byte sorrendje. Ismét meg kell rendezned a sorrendet, de csak a kis-endian rendszerekhez való karakterlánc ábrázolásához.

a forrásnyomtatás megtekintése?

ha (guidType == SequentialGuidType.SequentialAsString

Array.Reverse (guidBytes, 0, 4);

Array.Reverse (guidBytes, 4, 2);

A legegyszerűbb, ha minden számítás eredményét visszaadja:

a forrásnyomtatás megtekintése?

új iránymutatás (guidBytes);

használata

A módszer használatához meg kell határoznod, hogy milyen típusú adatbázist használsz, és milyen típusú GUID-t találsz legjobban. Íme néhány tipp a használatához:

Az SQLite adatbázis nem tartalmaz natív GUID-t, de vannak olyan bővítmények, amelyek emulálják a támogatást. Az egyik vagy másik módon a GUID-t akár egy 16 bájtos tömb, akár egy 36 karakteres karakterlánc képviselheti.

Íme néhány példa az új NewSequentialGuid módszerrel (SequentialGuidType.SequentialAsString) nyert GUID-ekről:

39babcb4-e446-4ed5-4012-2e27653a9d13
39babcb4-e447-ae68-4a32-19eb8d91765d
39babcb4-e44a-6c41-0fb4-21edd4697f43
39babcb4-e44d-51d2-c4b0-7d8489691c70

És egy másik példa a NewSequentialGuid (SequentialGuidType.SequentialAsBinary):

b4bcba39-58eb-47ce-8890-71e7867d67a5
b4bcba39-5aeb-42a0-0b11-db83dd3c635b
b4bcba39-6aeb-4129-a9a5-a500aac0c5cd
b4bcba39-6ceb-494d-A978-c29cef95d37f

Ha megnézzük ezeket az adatokat a ToSting () segítségével. akkor valami különöset lát. Az első két blokkot fordított sorrendben kell írni. Ez csak a nagy \ little-endian rendszerek által tárgyalt problémának tudható be. Ha ez az adat stringként van írva, akkor teljesítményproblémák lesznek. A megoldás lehet a Guid.ToByteArray () használata:

39babcb4eb5847ce889071e7867d67a5
39babcb4eb5a42a00b11db83dd3c635b
39babcb4eb6a4129a9a5a500aac0c5cd
39babcb4eb6c494da978c29cef95d37f

A teszteket az egyes adatbázisokkal ellátott konzolalkalmazások segítségével futtatták. 2 millió sorba helyezett egy GUID-t egy elsődleges kulcs formájában és 100 karakteres szöveges értékkel, a cikkben leírt összes módszert alkalmazták. Az ellenőrzéshez a Guid.NewGuid (), valamint az egész típusú elsődleges kulcsot használtuk. Az időt mértük másodpercek alatt, miután minden millió beillesztésre került. Íme, mi történt:

Iránymutatás gyors elsődleges kulcsként a különböző adatbázisokhoz

Az MS SQL esetében az eredmények egészében várták, mert a SequentialAtEnd csak erre az adatbázisra készült. A különbségek mindössze 8,4% egész számmal rendelkeznek.

Iránymutatás gyors elsődleges kulcsként a különböző adatbázisokhoz

Iránymutatás gyors elsődleges kulcsként a különböző adatbázisokhoz

Nehéz volt kezelni az Oracle-szel. Megtartása GUID oszlop a nyers (16) lehet számítani, hogy SequentialAsBinary módszer a leggyorsabb, és ez, de még véletlenszerű GUID nem volt túl lassú, nem olyan lassú, mint képest egy egész szám gombot. Még több, egymást követő GUID-ok gyorsabban voltak, mint az egész kulcsok, amit nehéz megjósolni és elfogadni! Biztosan azt gyanítom, hogy az én alkalmatlanságom szerepet játszott az Oracle lekérdezésében, és ha valaki vitatja az adatokat, tudassa velem.

Iránymutatás gyors elsődleges kulcsként a különböző adatbázisokhoz

Az Oracle-hez hasonlóan az önkényes GUID-teljesítmény sem volt lehangoló. A várakozások szerint a SequentialAsString módszer a leggyorsabb, majdnem kétszer olyan gyors, mint egy tetszőleges GUID.

További megfontolások

Van néhány dolog, amit érdemes figyelembe venni. Ebben a cikkben már adott egy csomó időt időt betét értékeket az adatbázisba, de figyelmen kívül megalakult a GUID, míg Guid.NewGuid (). Természetesen a képzési idő hosszabb. Millió tetszőleges GUID-t hozhatok létre 140 ms-ra, de 2800 ms-ot hoznak létre egymás után, ami 20-szor lassabb.

A gyors tesztek azt mutatták, hogy a lassulás oroszlánrésze az RNGCryptoServiceProvider szolgáltatás használata önkényes adatok előállítására. Váltás System.Randomre csökkentette a végrehajtási időt 400 ms-ra. Még mindig nem ajánlom ezt a módszert a felsorolt ​​veszélyek miatt.

Ez a lassulás probléma? Személy szerint én úgy döntöttem, hogy nem. Amíg az alkalmazás nem használja a részletes adatokat behelyezése (és akkor érdemes figyelembe venni a nagyon célszerűségét a GUID), a termelés költségére összhangban a működési idő az adatbázis maga és a további gyors munka.

Egy másik lehetséges probléma: elegendő lesz 10 bájt az egyediség garantálására? Az időbélyegzőt tekintve ez azt jelenti, hogy a több, mint néhány milliszekundum alatt létrehozott két GUID-t különböző módon garantálják. De mi a helyzet azokkal a GUID-ekkel, amelyeket gyorsan létrehozunk egy idő alatt? Ebben az esetben a 10 byte nekünk az értéke 2 vagy 80 1,208,925,819,614,629,174,706,176 lehetséges kombináció. Ie a valószínűsége ugyanaz lesz, mint az a tény, hogy ebben a pillanatban az adatbázisod és az összes mentés egyidejűleg támad és megsemmisül egy vaddisznó horda.

Az utolsó probléma az, hogy lehet, hogy érdekel, hogy kapunk GUID technikailag nem felel meg az RFC szabvány 4122. Őszintén, nem hiszem, hogy ez egy nagy probléma, nem tudom, egy egységes adatbázis, amely ténylegesen ellenőrzi a GUID belső berendezés és a mulasztás változat GUID további bájtokat ad nekünk, hogy növeljük a kulcs egyediségét.

Végső kód

a forrásnyomtatás megtekintése?

Kapcsolódó cikkek