Найкраще рішення для виправлення дизайну бази даних за допомогою GUID як основного ключа


18

Я після деякого підтвердження цієї ідеї виправити погано працюючу базу даних або кращі пропозиції, якщо хтось має. Завжди відкриті для кращих пропозицій.

У мене дуже велика база даних (20+ мільйонів записів зростає приблизно на 1/2 мільйони на день), які використовують GUID як ПК.

З мого боку нагляд, але ПК є кластеризованим на SQL сервері і викликає проблеми з продуктивністю.

Причина настанови - ця база даних частково синхронізована з 150 іншими базами даних, тому ПК повинен бути унікальним. Синхронізацією не керує SQL Server, скоріше існує вбудований процес, який підтримує синхронізацію даних з вимогами системи - все на основі цього GUID.

Кожна з 150 віддалених баз даних не зберігає повні дані, як вони зберігаються в центральній базі даних SQL. вони зберігають лише підмножину даних, які вони насправді потребують, а дані, які вимагають, не є унікальними для них (10 із 150 баз даних можуть мати деякі ті самі записи, наприклад, із баз даних інших сайтів - вони діляться). Крім того - дані фактично генеруються на віддалених сайтах - а не в центральній точці - отже, необхідність у GUID.

Центральна база даних використовується не лише для синхронізації всього, але запити від 3000+ користувачів виконуватимуться проти цієї дуже великої фрагментованої бази даних. Вже це велика проблема в початковому тестуванні.

На щастя, ми ще не живемо - тому я можу вносити зміни та приймати речі в режимі офлайн, якщо потрібно, що хоча б щось.

Продуктивність віддалених баз даних не є проблемою - підмножини даних досить малі, і база даних зазвичай ніколи не перевищує розмір понад 1 ГБ. Записи подаються в основну систему досить регулярно і видаляються з менших BD, коли це більше не потрібно.

Продуктивність центральної БД, яка зберігає всі записи, є жахливою - завдяки кластеризованому GUID як первинному ключу для багатьох записів. Фрагментація індексу виключається з діаграм.

Отже - мої думки вирішити проблему продуктивності - створити новий стовпець - Без підпису BIGINT IDENTITY (1,1), а потім змінити Clustered PK таблиці BIGINT.

Я б створив унікальний індекс без кластера в полі GUID, який був первинним ключем.

Менші віддалені 150 баз даних не повинні знати про новий ПК в базі даних Центрального сервера SQL - його суто буде використовуватися для організації даних у базі даних та зупинки поганої продуктивності та фрагментації.

Чи вдасться це покращити продуктивність центральної бази даних SQL і запобігти подальшому фрагментації індексу (певною мірою)? чи я пропустив щось дуже важливе тут, яке збирається стрибнути і кусати мене і викликати ще більше горя?


2
@mattytommo Я згоден.
Пол Флемінг

2
Ви дефрагментацію індексу проводите принаймні раз на тиждень?
Андомар

1
Чи є у вас щось, що має сенс кластеризувати? Тобто, який запит повинен бути швидким? Це точно не буде скануванням діапазону на орієнтирі, тому замість того, щоб просто вибирати автоматичне збільшення, врахуйте, чи є якась оптимальна кластеризація часу запиту, яку ви можете вибрати. Якщо ні, то продовжуйте і скористайтеся

2
@Borik Не чудова ідея, виходячи з того, що він має, і темпи його зростання, він би виснажився intза 4255 днів (11,5 років). Якщо він це зробив, він звинуватив би вас лише через 11,5 років;)
mattytommo

1
Протилежний погляд: чому ви вважаєте, що тип даних GUID є проблемою? Це 128-бітове ціле число. Чому, на вашу думку, заміщення його на 64-бітне ціле (bigint) або 32-бітове ціле (int) збирається помітно змінити швидкість? Я думаю, вам слід обов'язково змінити ключ кластеризації на щось інше, щоб уникнути розщеплення сторінок, що призводить до фрагментації, але я не думаю, що вам слід змінювати тип даних, якщо ви не впевнені, що тип даних є проблемою.
Грінстоун Уокер

Відповіді:


8

Вам, звичайно, НЕ потрібно кластерувати на GUID. Якщо у вас є щось, що дозволило б вам однозначно ідентифікувати записи, відмінні від цього GUID, я б запропонував вам поглянути на створення унікального індексу на іншому полі та зробити цей індекс кластеризованим. Якщо ні, ви можете кластеризувати інші поля, навіть використовуючи невідповідні індекси. Підхід до кластеру, однак, найкраще полегшує розподіл даних та запитів, тож якщо у вас є поле "регіон" чи щось таке, це може бути кандидатом у вашій схемі кластеризації.

Проблемою із зміною на "А" BIGINTє доповнення до даних з інших баз даних та інтеграція їх бази в центральний сховище. Якщо це не розгляд - і він ніколи не буде розглядом - тоді, так, це BIGINTвирішило б проблему відновлення балансування індексу.

Якщо ви не вказуєте кластерний індекс, SQL Server робить те саме: він створює поле ідентифікатора рядка і відображає всі інші індекси до цього. Отже, роблячи це самостійно, ви вирішуєте це так само, як вирішив би його SQL.


Єдине справді унікальне поле в таблиці - це GUD - інші стовпці не є унікальними, і є комбінації стовпців разом, які можуть бути унікальними для початку - але з часом є невеликий шанс, що вони генерують повторюваний запис. Дуже віддалений, але це можливо з огляду на характер даних. Я читав, що всі інші некластеризовані індекси посилаються на кластерний індекс для підвищення ефективності пошуку тощо. Чи не було б кластерного ПК, оскільки GUID не спричинив би ефективність? Я знаю про простір і хоча турбуюся - продуктивність є найважливішою.
Родлз

Якщо ви не вкажете кластерний індекс, то показник продуктивності полягає в тому, що SQL створить один поза кадром для вас і зіставить усі інші індекси в цей. Отже, у вашому випадку ви отримаєте поліпшення продуктивності , дозволяючи SQL робити це, тому що зараз ви постійно переміщуєте всі свої дані на диску, щоб зберегти порядок сортування, коли порядок сортування не важливий. Вам знадобиться більше місця для зберігання, але ви побачите значне поліпшення пам’яті та мінімальний / не вплине на пошук.
Девід Т. Макнет

Тож питання, я думаю, якщо я не буду BIGINT Clustered PK, а просто зміну PK на GUID, що не кластеризується, які наслідки для продуктивності? На столі є інші некластеризовані індекси, які будуть часто шукати. Чи вплине це на ефективність цих пошуків?
Родлз

+1 Я б також запропонував залишитися з GUID. Дуже важко їх замінити в розподілених системах. Ваш кластерний індекс на великій таблиці повинен бути очевидним на основі того, як ви запитуєте дані.
Рем Русану

1
Привіт, хлопці - просто оновлення - я вніс зміни і зробив ПК некластеризованим на GUID, і SQL Server зайнятий введенням 2+ мільйонів записів у базу даних. У той же час, коли дані вставлялися, я зміг запитувати в базі даних інформацію та запити, які часом до зміни змінилися за 10 хвилин, завершені за лічені секунди. Отже, якщо зробити ПК не кластеризованим і не турбуватися про BIGINT, схоже, добре працювали. Дуже дякую за внесок та допомогу кожного.
Родлз

1

Це високе замовлення.

Дозвольте запропонувати підхід середньої людини.

У мене виникли проблеми із System.Guid.NewGuid (), що генерує випадкові настанови. (Я дозволяв клієнтові створити свій власний навід, замість того, щоб спиратися на базу даних для створення послідовності).

Як тільки я перейшов до UuidCreateSequences на стороні клієнта, моя продуктивність стала НАБАГО кращою, особливо на INSERT.

Ось код клієнта DotNet вуду. Я впевнений, що звідкись заклав:

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;


namespace MyCompany.MyTechnology
{
  public static class Guid
  {


    [DllImport("rpcrt4.dll", SetLastError = true)]
    static extern int UuidCreateSequential(out System.Guid guid);


    public static System.Guid NewGuid()
    {
      return CreateSequentialUUID();
    }


    public static System.Guid CreateSequentialUUID()
    {
      const int RPC_S_OK = 0;
      System.Guid g;
      int hr = UuidCreateSequential(out g);
      if (hr != RPC_S_OK)
        throw new ApplicationException("UuidCreateSequential failed: " + hr);
      return g;
    }


  }
}














    /*

Original Reference for Code:
http://www.pinvoke.net/default.aspx/rpcrt4/UuidCreateSequential.html


*/

/*



Text From URL above:

UuidCreateSequential (rpcrt4)

Type a page name and press Enter. You'll jump to the page if it exists, or you can create it if it doesn't.
To create a page in a module other than rpcrt4, prefix the name with the module name and a period.
. Summary
Creates a new UUID 
C# Signature:
[DllImport("rpcrt4.dll", SetLastError=true)]
static extern int UuidCreateSequential(out Guid guid);


VB Signature:
Declare Function UuidCreateSequential Lib "rpcrt4.dll" (ByRef id As Guid) As Integer


User-Defined Types:
None.

Notes:
Microsoft changed the UuidCreate function so it no longer uses the machine's MAC address as part of the UUID. Since CoCreateGuid calls UuidCreate to get its GUID, its output also changed. If you still like the GUIDs to be generated in sequential order (helpful for keeping a related group of GUIDs together in the system registry), you can use the UuidCreateSequential function.

CoCreateGuid generates random-looking GUIDs like these:

92E60A8A-2A99-4F53-9A71-AC69BD7E4D75
BB88FD63-DAC2-4B15-8ADF-1D502E64B92F
28F8800C-C804-4F0F-B6F1-24BFC4D4EE80
EBD133A6-6CF3-4ADA-B723-A8177B70D268
B10A35C0-F012-4EC1-9D24-3CC91D2B7122



UuidCreateSequential generates sequential GUIDs like these:

19F287B4-8830-11D9-8BFC-000CF1ADC5B7
19F287B5-8830-11D9-8BFC-000CF1ADC5B7
19F287B6-8830-11D9-8BFC-000CF1ADC5B7
19F287B7-8830-11D9-8BFC-000CF1ADC5B7
19F287B8-8830-11D9-8BFC-000CF1ADC5B7



Here is a summary of the differences in the output of UuidCreateSequential:

The last six bytes reveal your MAC address 
Several GUIDs generated in a row are sequential 
Tips & Tricks:
Please add some!

Sample Code in C#:
static Guid UuidCreateSequential()
{
   const int RPC_S_OK = 0;
   Guid g;
   int hr = UuidCreateSequential(out g);
   if (hr != RPC_S_OK)
     throw new ApplicationException
       ("UuidCreateSequential failed: " + hr);
   return g;
}



Sample Code in VB:
Sub Main()
   Dim myId As Guid
   Dim code As Integer
   code = UuidCreateSequential(myId)
   If code <> 0 Then
     Console.WriteLine("UuidCreateSequential failed: {0}", code)
   Else
     Console.WriteLine(myId)
   End If
End Sub




*/

АЛЬТЕРНАТА ІДЕЯ:

Якщо ваші основні та віддалені db є "пов'язаними" (як у sp_linkserver) ......, ви можете використовувати головний db як "генератор uuid".

Ви не хочете отримувати uuid "один за одним", це занадто велика базікавість.

Але ти можеш схопити набір увайдів.

Нижче наведено код:

IF EXISTS (SELECT * FROM sys.objects WHERE object_id =
 OBJECT_ID(N'[dbo].[uspNewSequentialUUIDCreateRange]') AND type in (N'P',
 N'PC'))

 DROP PROCEDURE [dbo].[uspNewSequentialUUIDCreateRange]

 GO



 CREATE PROCEDURE [dbo].[uspNewSequentialUUIDCreateRange] (

 @newUUIDCount int --return

 )

 AS

 SET NOCOUNT ON

 declare @t table ( dummyid int , entryid int identity(1,1) , uuid
 uniqueidentifier default newsequentialid() )

 insert into @t ( dummyid ) select top (@newUUIDCount) 0 from dbo.sysobjects
 so with (nolock)

 select entryid , uuid from @t

 SET NOCOUNT OFF

 GO

/ *

--START TEST

 set nocount ON

 Create Table #HolderTable (entryid int , uuid uniqueidentifier )

 declare @NewUUIDCount int

 select @NewUUIDCount = 20

 INSERT INTO #HolderTable EXEC dbo.uspNewSequentialUUIDCreateRange
 @NewUUIDCount

 select * from #HolderTable

 DROP Table #HolderTable

 --END TEST CODE

* /


Цікавий - і підхід я не розглядав - я це більш детально вивчу, оскільки це виглядає приємно і запускаю деякі тестові проекти. Якби у нас було 150 баз даних, що генерують послідовні вказівки, про які повідомляється в центральній базі даних, це все одно не спричинило б фрагментацію, оскільки керівництво все одно буде досить випадковим при вставленні в центральну базу даних. Якщо, звичайно, ви маєте на увазі скинути кластеризований ПК і не маєте кластеризованого ПК?
Родлз

Чи 150 "віддалених" баз даних вставляють по черзі? Або вони переміщують дані в масових наборах вночі чи щось таке? Отже, ти ніби між скелею і важким місцем. Використання bigint з часом закінчиться (можливо), і вам все одно доведеться отримати унікальну цінність для багатьох db. Тож ось моя радикальна ідея. Чи може 150 віддалених баз даних отримати свої UUID від центральної служби? Це одна ідея. Чи пов'язані 150 віддалених баз даних (як у sp_addlinkedserver) до основної бази даних? Тоді у мене є АДС, що може бути розглянуто. Дозвольте мені побачити, чи зможу я його знайти.
granadaCoder

Ось стаття, яка розповідає про секвенціаліди (не пов’язані з тим, що я вже писав, я думаю, що це цікаво) codeproject.com/Articles/388157/…
granadaCoder

0

Виходячи з опису, перейдіть з BIGINT. Однак індекс для GUID може бути не унікальним, оскільки GUID повинні бути унікальними в усьому світі.


-1

Якщо GUID зберігається належним чином як унікальний ідентифікатор, він не повинен мати жодних проблем із виконанням ... і якщо ви можете використовувати послідовний GUID ще краще ...

Також @mattytommo має хорошу точку приблизно 11,5 років із використанням INT ...


Так - але керівництво генерується у віддалених 150 базах даних, а не в базі даних SQL Server - тому я не можу використовувати послідовний керівник - але дякую за відповідь.
Родлз

У такому випадку ваш план, на мою думку, є надійним, я зробив подібну річ на одній із БД, якими я керую, я створив INT DENTITY (1,1) і встановив його як кластерний ПК, а також гуманний читабельний ідентифікатор даних підтягнувся, і я залишив GUID (Index) як трекер, щоб можна було простежити, де він виник. Але моя мотивація була більшою мірою від економії місця ...
Борік

Велика подяка та вдячність за ваші відповіді та розуміння. :)
Роддлз
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.