Лучшее решение для исправления дизайна базы данных с GUID в качестве первичного ключа

18

Я после некоторого подтверждения этой идеи, чтобы исправить плохо работающую базу данных или лучшее предложение, если кто-то есть. Всегда открыт для лучших предложений.

У меня очень большая база данных (более 20 миллионов записей растет примерно на 1/2 миллиона в день), которые используют GUID в качестве PK.

С моей стороны это недосмотр, но ПК кластеризован на сервере SQL и вызывает проблемы с производительностью.

Причина для guid - эта база данных частично синхронизирована со 150 другими базами данных, поэтому PK должен быть уникальным. SQL Server не управляет синхронизацией, скорее, существует специальный процесс, который синхронизирует данные в соответствии с требованиями системы - все на основе этого GUID.

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

Центральная база данных используется не только для синхронизации всего, но и к этой очень большой фрагментированной базе данных будут выполняться запросы от 3000+ пользователей. Уже это большая проблема при первоначальном тестировании.

К счастью, мы еще не живы - так что я могу вносить изменения и отключать их, если требуется, что является чем-то еще.

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

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

Итак, мои мысли по поводу исправления проблемы с производительностью - создать новый столбец BIGINT IDENTITY без знака (1,1), а затем изменить кластеризованный PK столбца BIGINT таблицы.

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

Меньшим удаленным базам данных 150 не нужно знать о новом ПК в базе данных Central SQL Server - он будет использоваться исключительно для организации данных в базе данных и предотвращения плохой производительности и фрагментации.

Будет ли это работать и улучшить производительность центральной базы данных SQL и предотвратить адскую фрагментацию в будущем (в определенной степени, конечно)? или я пропустил что-то очень важное здесь, которое подпрыгнет и укусит меня и вызовет еще больше горя?

Roddles
источник
2
@mattytommo Я согласен.
Пол Флеминг
2
Проводите ли вы дефрагментацию индекса хотя бы раз в неделю?
Andomar
1
Есть ли у вас что-нибудь, что имеет смысл кластеризовать? Т.е. какой запрос должен быть быстрым? Это определенно не будет сканирование диапазона в guid, поэтому вместо того, чтобы просто выбрать автоинкремент, подумайте, есть ли какая-то оптимальная по времени кластеризация, которую вы можете выбрать. Если нет, то иди дальше и используй bigint
2
@Borik Не очень хорошая идея, исходя из того, что у него есть и его темпов роста, он исчерпает intза 4255 дней (11,5 лет). Если бы он сделал это, он бы обвинял вас только в 11,5 лет;)
mattytommo
1
Противоположное мнение: как вы думаете, почему тип данных GUID является проблемой? Это 128-битное целое число. Как вы думаете, почему замена его на 64-разрядное целое число (bigint) или 32-разрядное целое число (int) приведет к заметной разнице в скорости? Я думаю, что вам определенно следует изменить ключ кластеризации на что-то другое, чтобы избежать разделения страниц, которое приводит к фрагментации, но я не думаю, что вам следует менять тип данных, если вы не уверены, что тип данных является проблемой.
Гринстоун Уолкер

Ответы:

8

Вам, конечно, не нужно кластеризоваться на GUID. Если у вас есть что-то, что позволит вам уникально идентифицировать записи, отличные от этого GUID, я бы посоветовал вам взглянуть на создание уникального индекса для этого другого поля и кластеризацию этого индекса. Если нет, вы можете кластеризовать другие поля, даже используя неуникальные индексы. Подход, заключающийся в кластеризации, однако, лучше всего облегчает разделение ваших данных и запросов - так что, если у вас есть поле «регион» или что-то еще, это может быть кандидатом для вашей схемы кластеризации.

Проблема с переходом на a BIGINTбудет заключаться в добавлении данных из других баз данных и интеграции их базы данных в центральное хранилище. Если это не рассмотрение - и никогда не будет рассмотрение - тогда, да, BIGINTэто решило бы проблему перебалансировки индекса.

За кулисами, если вы не укажете кластеризованный индекс, SQL Server делает то же самое: он создает поле идентификатора строки и отображает в нем все остальные индексы. Таким образом, делая это самостоятельно, вы решаете это так же, как это решает SQL.

Дэвид Т. Макнет
источник
Единственное действительно уникальное поле в таблице - это GUD - остальные столбцы не являются уникальными, и существуют комбинации столбцов, которые могут быть уникальными для начала, но со временем существует небольшая вероятность того, что они сгенерируют дублирующую запись. Очень далеко, но это возможно, учитывая характер данных. Я читал, что все другие некластеризованные индексы ссылаются на кластеризованный индекс для повышения производительности поиска и т. Д. Разве кластерный PK в качестве GUID не повлияет на производительность? Я осведомлен о пространстве и, хотя беспокойство - производительность имеет первостепенное значение.
Роддлс
Если вы не укажете кластеризованный индекс, производительность снизится из-за того, что SQL создаст один за вас и сопоставит все остальные индексы с этим. Таким образом, в вашем случае вы получите повышение производительности, если позволите SQL сделать это, потому что сейчас вы постоянно перемещаете все свои данные на диске, чтобы сохранить порядок сортировки, когда порядок сортировки не важен. Вам понадобится больше места для хранения, но вы увидите, что объем хранилища значительно улучшится и не окажет никакого влияния на поиск.
Дэвид Т. Макнет
Так что вопрос, который я предполагаю, заключается в том, что если я не использую BIGINT Clustered PK, а просто заменяю PK на Non Clustered PKID, как это влияет на производительность? В таблице есть другие некластеризованные индексы, которые будут часто искать. Повлияет ли это на эффективность этих поисков?
Роддлс
+1 Я бы также предложил остаться с GUID. Очень сложно заменить их в распределенных системах. Кластерный индекс вашей большой таблицы должен быть очевиден в зависимости от того, как вы запрашиваете данные.
Ремус Русану
1
Привет, ребята, просто обновление - я внес изменения и сделал PK не кластеризованным по GUID, а SQL Server занят вставкой более 2 миллионов записей в базу данных. В то же время, когда данные были вставлены, я смог запросить информацию в базе данных и запросы, которые время от времени до изменения превышали время в 10 минут и выполнялись в течение 1-2 секунд. Таким образом, создание ПК без кластеров и не беспокоиться о BIGINT, похоже, сработало. Большое спасибо за каждый вклад и помощь.
Роддлс
1

Это высокий заказ.

Позвольте мне предложить подход среднего человека.

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

Как только я перешел на UuidCreateSequential на стороне клиента, моя производительность стала НАМНОГО лучше, особенно на 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




*/

АЛЬТЕРНАТИВНАЯ ИДЕЯ:

Если ваша основная база данных и удаленная база данных «связаны» (как, например, sp_linkserver) ...... тогда вы можете использовать основную базу данных в качестве «генератора uuid».

Вы не хотите, чтобы 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

* /

granadaCoder
источник
Интересный - и подход, который я не рассматривал - я рассмотрю это более внимательно, так как это выглядит красиво, и запустим несколько тестовых проектов. Если бы у нас было 150 баз данных, генерирующих последовательные направляющие, которые передаются обратно в центральную базу данных, это не привело бы к фрагментации, поскольку при вставке в центральную базу данных направляющие элементы были бы довольно случайными Если, конечно, вы имеете в виду отбросить кластеризованный PK и иметь некластеризованный PK?
Роддлс
Вставляют ли 150 «удаленных» баз данных по одной за раз? Или они перемещают данные в больших массивах ночью или что-то? Значит, ты как бы между молотом и наковальней. Использование bigint в конечном итоге исчерпает пространство (возможно), и вам все равно придется получить уникальное значение среди множества БД. Так вот моя радикальная идея. Могут ли 150 удаленных баз данных получить свои UUID из центральной службы? Это одна идея. 150 удаленных баз данных «связаны» (как в sp_addlinkedserver) с основной базой данных? Тогда у меня есть UDF, который можно рассмотреть. Дай мне посмотреть, смогу ли я найти это.
granadaCoder
Вот статья, в которой говорится о sequentialid (не связанном с тем, что я уже написал, я думаю, что это интересно) codeproject.com/Articles/388157/…
granadaCoder
0

Исходя из вашего описания, идти с BIGINT. Однако индекс для GUID может быть неуникальным, поскольку предполагается, что GUID в любом случае должен быть глобально уникальным.

Джимбо
источник
-1

Если GUID хранится правильно как uniqueidentifier, не должно быть проблем с производительностью ... и если вы можете использовать Sequential GUID еще лучше ...

Кроме того, @mattytommo имеет хороший момент около 11,5 лет с использованием INT ...

Борик
источник
Да, но guid генерируется в удаленных 150 базах данных, а не в базе данных SQL Server - поэтому я не могу использовать sequentialguid - но спасибо за ответ.
Роддлс
В этом случае ваш план, на мой взгляд, является надежным, я сделал аналогичную вещь на одной из управляемых мной БД, я создал INT DENTITY (1,1) и установил его как Clustered PK, а также гуманный читаемый идентификатор для данных. потянул вверх и я сохранил GUID (индекс) в качестве трекера, чтобы можно было отследить, где он возник Но моя мотивация была больше из экономии места ...
Борик
Большое спасибо и высоко ценим за ваши ответы и идеи. :)
Roddles