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

StackOverflow https://stackoverflow.com/questions/170346

  •  05-07-2019
  •  | 
  •  

Вопрос

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

Это было полезно?

Решение

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



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

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

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


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

  • Широкое распределение значений
  • Почти случайно
  • Использование индексов очень, очень, очень плохое
  • Много листвы шевелится
  • Почти каждый PK должен быть, по крайней мере, на не кластерном индексе
  • Проблема происходит как на 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, хранящийся в виде уникальных типов на SQL Server
  • Guid хранится как CHAR (36) в Oracle.
  • Много операций вставки, объединенных в одну транзакцию
  • От 1 до 100 -х годов вставки в зависимости от таблицы
  • Некоторые таблицы > 10 миллионов строк



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

Тест VS2008, 10 одновременных пользователей, никакого времени на размышление, процесс тестирования с 600 пакетными вставками для конечной таблицы
Стандартное руководство
СреднееПродолжительность процесса: 10.5 сек
СреднееЗапрос на второй: 54.6
СреднееРесп.Время: 0.26

Последовательное руководство
СреднееПродолжительность процесса: 4.6 сек
СреднееЗапрос на второй: 87.1
СреднееРесп.Время: 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, поскольку PK использует последовательные Guid каждый раз, когда они не передаются вперед и назад из пользовательского интерфейса, они ускорят работу и не требуют никаких затрат на реализацию.

Другие советы

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

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

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

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

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

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

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

Как уже сказал massimogentilini, производительность может быть улучшена при использовании UuidCreateSequential (при генерации направляющих в коде). Но факт, по-видимому, отсутствует: SQL Server (по крайней мере, Microsoft SQL 2005/2008) использует ту же функциональность, НО: сравнение / упорядочение Guids различаются в .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);
}

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

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

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

От MSDN:

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

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

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

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

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

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

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

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

В ElasticSearch НЕТ заметной разницы между ними в индексировании.Я по-прежнему буду использовать COMB_GUIDS в случае, если контент попадает в индексы BTREE в любом месте цепочки, поскольку контент подается с привязкой ко времени, или может быть предварительно отсортирован по полю id, чтобы он ЯВЛЯЕТСЯ Связанный со временем и частично последовательный, он ускорится.

Довольно интересно.Код 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() );
    }

}

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top