Каковы улучшения производительности Sequential Guid по сравнению со стандартным Guid? - PullRequest
64 голосов
/ 04 октября 2008

Кто-нибудь когда-нибудь измерял производительность последовательного Guid по сравнению со Standard Guid при использовании в качестве первичных ключей в базе данных?

Ответы [ 9 ]

102 голосов
/ 04 октября 2008

GUID против последовательного GUID



Типичным примером является использование Guid в качестве PK для таблиц, но, как указано в других обсуждениях (см. Преимущества и недостатки ключей базы данных GUID / UUID ) Есть некоторые проблемы с производительностью.

Это типичная последовательность Guid

f3818d69-2552-40b7-a403-01a6db4552f7
7ce31615-fafb-42c4-b317-40d21a6a3c60
94732fc7-768e-4cf2-9107-f0953f6795a5


Проблемы такого рода данных: << br /> -

  • Широкое распределение значений
  • Почти случайные единицы
  • Использование индекса очень, очень, очень плохо
  • много движется листьев
  • Почти каждый ПК должен быть как минимум по некластерному индексу
  • Проблема возникает как в Oracle, так и SQL Server



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

cc6466f7-1066-11dd-acb6-005056c00008
cc6466f8-1066-11dd-acb6-005056c00008
cc6466f9-1066-11dd-acb6-005056c00008


Как их сгенерировать из кода C #:

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

public static Guid SequentialGuid()
{
    const int RPC_S_OK = 0;
    Guid g;
    if (UuidCreateSequential(out g) != RPC_S_OK)
        return Guid.NewGuid();
    else
        return g;
}


Преимущества

  • Лучшее использование индекса
  • Разрешить использование кластерных ключей проверено в сценариях NLB)
  • Меньше использования диска
  • 20-25% прироста производительности при минимальная стоимость



Измерение в реальной жизни: Сценарий:

  • Guid хранится как UniqueIdentifier типы на SQL Server
  • Guid хранится как CHAR (36) в Oracle
  • Лот операций вставки в пакетном режиме вместе в одной транзакции
  • От 1 до 100 сек вкладышей в зависимости на столе
  • Некоторые таблицы> 10 миллионов строк



Лабораторный тест - SQL Server

Тест VS2008, 10 одновременных пользователей, без обдумывания, процесс тестирования с 600 вставками в пакете для конечного стола
Стандартный гид
Avg. Продолжительность процесса: 10,5 сек
Avg. Запрос второй: 54,6
Avg. Соответственно Время: 0,26

последовательный гид
Avg. Продолжительность процесса: 4,6 сек
Avg. Запрос второй: 87,1
Avg. Соответственно Время: 0,12

Результаты по Oracle (извините, другой инструмент, используемый для теста) 1.327.613 вставить на стол с Guid PK

Стандартный гид , 0,02 сек. истекшее время для каждой вставки, 2,861 сек. процессорного времени, всего 31,049 сек. истекшее

Последовательный гид , 0,00 сек. Истекшее время для каждой вставки, 1,142 сек. процессорного времени, всего 3,667 сек. истекшее

Время ожидания последовательного чтения файла БД прошло с 6,4 миллионов событий ожидания для 62,415 секунд до 1,2 миллионов событий ожидания для 11,063 секунд.

Важно понимать, что все последовательные руководства могут быть угаданы, поэтому не стоит использовать их, если безопасность вызывает беспокойство, все еще используя стандартный guid.
Короче говоря ... если вы используете Guid в качестве PK, используйте последовательный guid каждый раз, когда они не передаются назад и вперед от пользовательского интерфейса, они ускорят работу и ничего не будут стоить для реализации.

50 голосов
/ 04 октября 2008

Я могу что-то упустить здесь (не стесняйтесь поправлять меня, если я есть), но я вижу очень небольшое преимущество в использовании последовательных идентификаторов GUID / UUID для первичных ключей.

Точка использования GUID или UUID для автоинкрементных целых чисел:

  • Они могут быть созданы где угодно без обращения к базе данных
  • Это идентификаторы, которые полностью уникальны в вашем приложении (а в случае UUID универсально уникальны)
  • Учитывая один идентификатор, невозможно угадать следующий или предыдущий (или даже любой другой допустимый идентификатор) вне грубой форсировки огромного пространства ключей.

К сожалению, используя ваше предложение, вы теряете все эти вещи.

Так что да. Вы сделали GUID лучше. Но в процессе вы отбросили почти все причины, чтобы использовать их в первую очередь.

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

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

20 голосов
/ 25 июня 2009

Как уже сказал massimogentilini, производительность может быть улучшена при использовании UuidCreateSequential (при генерации направляющих в коде). Но факт, по-видимому, отсутствует: SQL Server (по крайней мере, Microsoft SQL 2005/2008) использует ту же функциональность, НО: сравнение / упорядочение руководств различаются в .NET и в SQL Server, что по-прежнему вызывает больше операций ввода-вывода потому что направляющие не будут упорядочены правильно. Чтобы сгенерировать направляющие, упорядоченные правильно для сервера sql (упорядочение), необходимо выполнить следующее (см. сравнение подробности):

[System.Runtime.InteropServices.DllImport("rpcrt4.dll", SetLastError = true)]
static extern int UuidCreateSequential(byte[] buffer);

static Guid NewSequentialGuid() {

    byte[] raw = new byte[16];
    if (UuidCreateSequential(raw) != 0)
        throw new System.ComponentModel.Win32Exception(System.Runtime.InteropServices.Marshal.GetLastWin32Error());

    byte[] fix = new byte[16];

    // reverse 0..3
    fix[0x0] = raw[0x3];
    fix[0x1] = raw[0x2];
    fix[0x2] = raw[0x1];
    fix[0x3] = raw[0x0];

    // reverse 4 & 5
    fix[0x4] = raw[0x5];
    fix[0x5] = raw[0x4];

    // reverse 6 & 7
    fix[0x6] = raw[0x7];
    fix[0x7] = raw[0x6];

    // all other are unchanged
    fix[0x8] = raw[0x8];
    fix[0x9] = raw[0x9];
    fix[0xA] = raw[0xA];
    fix[0xB] = raw[0xB];
    fix[0xC] = raw[0xC];
    fix[0xD] = raw[0xD];
    fix[0xE] = raw[0xE];
    fix[0xF] = raw[0xF];

    return new Guid(fix);
}

или эта ссылка или эта ссылка .

4 голосов
/ 18 февраля 2011

См. Эту статью: (http://www.shirmanov.com/2010/05/generating-newsequentialid-compatible.html)

Несмотря на то, что MSSql использует эту же функцию для генерации NewSequencialIds (UuidCreateSequential (из Guid guid)), MSSQL переворачивает 3-й и 4-й байтовые шаблоны, которые не дают того же результата, который вы получили бы при использовании этой функции в своем коде. Ширманов показывает, как получить те же результаты, что и MSSQL.

4 голосов
/ 04 октября 2008

Если вам нужно для использования последовательных GUI, SQL Server 2005 может сгенерировать их для вас с помощью функции NEWSEQUENTIALID().

Однако , поскольку основное использование GUI - это генерация ключей (или альтернативных ключей), которые невозможно угадать (например, чтобы люди не передавали угаданные ключи в GET), я не понимаю, насколько они применимы потому что их так легко угадать.

С MSDN :

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

3 голосов
/ 04 октября 2008

Извлечение COMB . Автор Jimmy Nilsson: тип GUID, в котором число битов заменено значением, подобным метке времени. Это означает, что COMB могут быть упорядочены, и при использовании в качестве первичного ключа приводит к меньшему количеству разбиений страницы индекса при вставке новых значений.

Можно ли использовать уникальный идентификатор (GUID) в качестве первичного ключа?

2 голосов
/ 02 ноября 2013

Я перепутал разницу между Guid (кластеризованным и некластеризованным), Sequential Guid и int (Identity / autoincrement) с использованием Entity Framework. Последовательный Guid был удивительно быстрым по сравнению с int с идентичностью. Результаты и код Последовательного Руководства здесь .

2 голосов
/ 27 сентября 2013

Хорошо, я наконец-то дошел до этой точки в проектировании и производстве.

Я генерирую COMB_GUID, где старшие 32 бита основаны на битах с 33 по 1 времени Unix в миллисекундах. Таким образом, каждые 2 миллисекунды есть 93 бита случайности, а опрокидывание старших бит происходит каждые 106 лет. Фактическое физическое представление COMB_GUID (или UUID типа 4) - это кодированная в base64 версия 128 битов, которая представляет собой строку из 22 символов.

При вставке в postgres соотношение скорости между полностью случайным UUID и COMB _GUID сохраняется как выгодное для COMB_GUID. COMB_GUID на моих аппаратных средствах 2X быстрее, чем за несколько тестов, для теста на миллион записей. Записи содержат идентификатор (22 символа), строковое поле (110 символов), двойную точность и INT.

В ElasticSearch, нет никакой заметной разницы между этими двумя для индексации. Я все еще собираюсь использовать COMB_GUIDS в случае, если содержимое переходит к индексам BTREE в любом месте цепочки, поскольку содержимое передается по времени или может быть предварительно отсортировано в поле идентификатора, так что оно IS связано со временем и частично последовательно , это ускорится.

Довольно интересно. Java-код для создания COMB_GUID приведен ниже.

import java.util.Arrays;
import java.util.UUID;
import java.util.Base64; //Only avail in Java 8+
import java.util.Date;

import java.nio.ByteBuffer; 

    private ByteBuffer babuffer = ByteBuffer.allocate( (Long.SIZE/8)*2 );
private Base64.Encoder encoder = Base64.getUrlEncoder();
public  String createId() {
    UUID uuid = java.util.UUID.randomUUID();
        return uuid2base64( uuid );
}

    public String uuid2base64(UUID uuid){ 

        Date date= new Date();
        int intFor32bits;
        synchronized(this){
        babuffer.putLong(0,uuid.getLeastSignificantBits() );
        babuffer.putLong(8,uuid.getMostSignificantBits() );

                long time=date.getTime();
        time=time >> 1; // makes it every 2 milliseconds
                intFor32bits = (int) time; // rolls over every 106 yers + 1 month from epoch
                babuffer.putInt( 0, intFor32bits);

    }
        //does this cause a memory leak?
        return encoder.encodeToString( babuffer.array() );
    }

}

1 голос
/ 04 октября 2008

Я не вижу необходимости, чтобы уникальные ключи были угадываемыми или нет, передача их из веб-интерфейса или в какой-либо другой части кажется плохой практикой сама по себе, и я не вижу, если у вас есть проблемы с безопасностью, как использовать guid может улучшить положение вещей (если это так, используйте генератор реальных случайных чисел, используя соответствующие криптографические функции платформы).
Другие элементы охватываются моим подходом, последовательный гид может быть сгенерирован из кода без необходимости доступа к БД (также если только для Windows), и он уникален во времени и пространстве.
И да, был задан вопрос с намерением ответить на него, чтобы дать людям, которые выбрали Guids для своего ПК, способ улучшить использование базы данных (в моем случае позволил клиентам поддерживать гораздо более высокую рабочую нагрузку без необходимости менять серверы).

Похоже, что проблем безопасности много, в этом случае не используйте Sequential Guid или, что еще лучше, используйте стандартный Guid для PK, которые передаются назад и вперед из вашего пользовательского интерфейса, и последовательный guid для всего остального. Как всегда нет абсолютной истины, я отредактировал также основной ответ, чтобы отразить это.

...