Каково улучшение производительности Sequential Guid по стандартным рекомендациям?

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

Ответ 1

GUID vs .Sequential GUID



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

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

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

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

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



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

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


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

[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% минимальная стоимость



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

  • Руководство хранится как UniqueIdentifier типы на SQL Server
  • Указатель хранится как 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 сек. истекшее

Время ожидания последовательного чтения DB файла, прошедшее от 6,4 миллионов ожидающих событий для 62.415 секунд до 1.2 миллионов ожидающих событий для 11.063 секунд.

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

Ответ 2

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

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

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

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

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

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

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

Ответ 3

Как уже сообщал 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

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

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

От MSDN:

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

Ответ 5

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

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

Ответ 6

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

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

Ответ 7

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

Ответ 8

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

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

Ответ 9

ОК, я, наконец, добрался до этого момента в дизайне и производстве.

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

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

В ElasticSearch нет никакой заметной разницы между этими двумя индексами. Я все еще собираюсь использовать COMB_GUIDS в случае, если контент идет в индексы BTREE в любом месте цепочки, поскольку контент загружается связанным временем или может быть предварительно обработан в поле id, так что это 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() );
    }

}