8 байта за клеймо за време или 6 байта за клеймо за време в COMB GUID в SQLServer

Благодарение на прекрасната статия Цената на GUID като първични ключове , ние имаме COMB GUID. Въз основа на текущото изпълнение има 2 подхода:

  1. използвайте последните 6 байта за клеймо за време: GUID като бързи първични ключове под множество бази данни
  2. използвайте последните 8 байта за клеймо за време, като използвате Windows tick: стратегия GUID COMB в EF4.1 (CodeFirst)

Всички знаем, че за 6 байта клеймо за време в GUID ще има повече байтове за произволни байтове, за да се намали сблъсъкът на GUID. Въпреки това ще бъдат създадени повече GUID със същото времево клеймо и те изобщо не са последователни. С това би било за предпочитане 8 байта клеймо за време.

Така че изглежда труден избор. Въз основа на статия по-горе GUID като бърз първичен ключове под множество бази данни, казва:

Преди да продължим, кратка бележка под линия за този подход: използването на клеймо за време с разделителна способност от 1 милисекунда означава, че GUID, генерирани много близо един до друг, може да имат една и съща стойност на клеймо за време и следователно няма да бъдат последователни. Това може да е често срещано явление за някои приложения и всъщност експериментирах с някои алтернативни подходи, като например използване на таймер с по-висока разделителна способност, като System.Diagnostics.Stopwatch, или комбиниране на клеймото за време с „брояч“, който би гарантирал последователността продължи до актуализиране на клеймото за време. По време на тестването обаче открих, че това изобщо не прави забележима разлика, дори когато десетки или дори стотици GUID се генерират в рамките на един и същ прозорец от една милисекунда. Това е в съответствие с това, което Джими Нилсон срещна по време на тестването си с COMBs

Просто се чудя дали някой, който познава вътрешната база данни, може да сподели някои светлини относно горното наблюдение. Дали защото този сървър на база данни просто съхранява данните в паметта и записва на диск само когато достигне определен праг? По този начин пренареждането на вмъкнати данни с GUID без последователност с едно и също времево клеймо би се случило като цяло в паметта и по този начин минимално наказание за производителност.

Актуализация: Въз основа на нашето тестване, COMB GUID не може да намали фрагментацията на таблицата, както се твърди в интернет, в сравнение с произволния GUID. Изглежда единственият начин в момента е да се използва SQL Server за генериране на последователния GUID.


person windfly2006    schedule 06.03.2014    source източник
comment
Мисля, че всички изброени статии объркват първичен ключ с клъстериран индексен ключ. GUIDS работят добре като първични ключове, особено в ситуации с множество главни, но не работят добре като клъстерирани индексни ключове (въпреки че не работят добре, зависи от това какви други колони има в таблицата).   -  person Greenstone Walker    schedule 11.03.2014
comment
да, прав си. Нашата основна грижа се дължи на факта, че това би създало голяма фрагментация поради произволността на GUID, който е групиран PK на нашата маса. Някакви мисли относно моите въпроси по-горе за изпълнение на произволен GUID в рамките на същия времеви печат?   -  person windfly2006    schedule 12.03.2014
comment
Ако няма друга колона, която да се използва като клъстериран индексен ключ, тогава бих избрал newsequentialid() (от @ErikE по-долу).   -  person Greenstone Walker    schedule 14.03.2014


Отговори (1)


Статията, която споменахте, е от 2002 г. и е много стара. Просто използвайте newsequentialid (налично в SQL Server 2005 и по-нова версия). Това гарантира, че всеки нов идентификатор, който генерирате, е по-голям от предишния, решавайки проблема с фрагментирането на индекса/разделянето на страници.

Друг аспект, който обаче бих искал да спомена, който авторът на тази статия премълча, е, че използването на 16 байта, когато имате нужда само от 4, не е добра идея. Да приемем, че имате таблица с 500 000 реда, средно 150 байта, без клъстерираната колона, и таблицата има 3 неклъстерирани индекса (които повтарят клъстерната колона във всеки ред), всеки на свой ред с редове, средно 4 байта, 25 байта и 50 байтове, без да се брои клъстерираната колона.

Тогава изискванията за съхранение при перфектен коефициент на запълване от 100% са (всички числа в мегабайти, с изключение на %):

Item  Clust  50     25     4      Total
----  -----  -----  -----  -----  ------
GUID  79.1   31.5   19.6    9.5   139.7
 int  73.4   25.7   13.8    3.8   116.7
%imp   7.2%  18.4%  29.6%  60.0%   16.5%

В неклъстърирания индекс, който има само една int колона от 4 байта (често срещан сценарий), превключването на клъстерирания индекс към int го прави с 60% по-малък! Това се превръща директно в 60% подобрение на производителността за всяко сканиране на таблицата – и това е консервативно, защото с по-малки редове разделянето на страници ще се случва по-рядко и фрагментацията ще остане по-добра за по-дълго време.

Дори в самия клъстериран индекс все още има 7,2% подобрение на производителността, което изобщо не е нищо.

Какво ще стане, ако използвате GUIDs в цялата си база данни, която има таблици с подобен профил като този, където преминаването към int би довело до 16,5% намаление на размера, а самата база данни е с размер 1,397 терабайта? Цялата ви база данни ще бъде с 230 Gb по-голяма (вижте колоната Общо, 139,7 - 116,7). Това се превръща в реални пари в реалния свят за съхранение с висока наличност. Той премества графика ви за закупуване на дискове по-рано във времето, което е вредно за крайния резултат на вашата компания.

Никога не използвайте по-големи типове данни от необходимото. Това е като да добавите тегло към колата си без причина: вие ще платите за това (ако не заради скоростта, то заради разхода на гориво).

АКТУАЛИЗАЦИЯ

Сега, след като знам, че създавате GUID във вашия клиентски код, мога да видя по-ясно естеството на вашия проблем. Ако можете да отложите създаването на GUID до времето за вмъкване на ред, ето един начин да го постигнете.

Първо задайте стойност по подразбиране за вашата колона CustomerID:

ALTER TABLE dbo.Customer ADD CONSTRAINT DF_Customer_CustomerID
   DEFAULT (newsequentialid()) FOR Customer;

Сега не е нужно да указвате каква стойност да вмъкнете за CustomerID във всеки INSERT и вашата заявка може да изглежда така:

DECLARE @Name varchar(100) = 'Acme Spy Devices';
INSERT dbo.Customer (Name)
OUTPUT inserted.CustomerID -- a GUID
VALUES (@Name);

В този много прост пример сте вмъкнали нов ред в таблицата Customer и сте върнали набор от редове на клиента, съдържащ току-що създадената стойност, всичко това в една заявка.

Ако искате изрично да вмъкнете VALUES (newsequentialid(), @Name), това също ще работи.

person ErikE    schedule 07.03.2014
comment
Много благодаря. Ние използваме Entity Framework и използваме кода за генериране на GUID за PK, така че търсим някаква минимална промяна на кода, за да генерираме последователните GUID в кода. На този етап няма да можем да се върнем към INT като PK поради твърде много промяна на кода. Ако използваме newsequentialid, ще има допълнително двупосочно пътуване на базата данни за генериране на този GUID, нали? - person windfly2006; 08.03.2014
comment
Правилно, newsequentialid() ще трябва да се извика от базата данни. Понякога кодът може да бъде пренаписан, за да получи идентификатора след вмъкване на целия запис, вместо да се налага да получава идентификатора преди това - ако е така, няма допълнително пътуване до базата данни. - person ErikE; 08.03.2014
comment
Благодаря отново. Просто се чудя дали бихте могли да споделите някаква връзка или примерен код за случая, когато не е необходимо допълнително пътуване до базата данни. - person windfly2006; 10.03.2014
comment
Вашето изчисление на дисковото пространство не отчита свободното пространство. - person Greenstone Walker; 11.03.2014
comment
Да, @GreenstoneWalker, беше умишлено да не се даде точно отчитане на действителното използване на пространството (имайте предвид, че казах перфектен коефициент на запълване от 100%), беше просто да се покаже разумно сравнение между изискванията за пространство на двете колони. Как бихте очаквали, че отчитането на свободното пространство ще повлияе на нещата? Смятате ли, че това ще повлияе съществено на моето твърдение, че използването на 16 байта, където 4 ще свърши работа, е лош избор на дизайн? - person ErikE; 11.03.2014