Служебная фабрика - Как мы могли генерировать ключ ключа?
У меня есть служба с отслеживанием состояния с набором ключей разделов, идущих от
От -9223372036854775808 до 9223372036854775807 (UniformInt64Partition).
Как я могу генерировать адекватный ключ раздела при вызове службы, чтобы улучшить распределение рабочих нагрузок по всем разделам?
Спасибо
2 ответа
Для этого большого диапазона ключей секционирования наилучшим подходом является использование алгоритма хеширования поверх поля или набора полей для генерации ключа (числа) с наименьшим возможным коллизией.
Предполагая, что вы храните информацию о клиенте, например, хеш для имени клиента из "Джона Смита" может сгенерировать значение хеш-функции 32, потому что любой пользователь с тем же именем, что и "Джон Смит", сгенерирует тот же хеш, если он не часто, не будет проблемой, потому что 32 не является идентификатором, и они могут повторяться, имея тот же хеш, который будет храниться в одном разделе.
Если вы действительно хотите распределить эти значения как можно более равномерно, вы можете использовать другое объединенное поле, чтобы отличать "Джона Смита" от "Джона Смита", например, дату рождения. И если оба они не родились в одну и ту же дату, вы найдете разные значения для каждый.
В вашем случае, поскольку диапазон очень большой, вы должны использовать алгоритм хеширования, чтобы хэшировать эти значения, чтобы соответствовать диапазону от -9223372036854775808 до 9223372036854775807.
Вам нужно столько ключей?
Если ваша система не ожидает слишком большого количества разделов, простой способ справиться с этим - использовать натуральное число, которое близко отражает диапазон клавиш, предоставляемых выбранной вами функцией хеширования, вы можете выбрать один с более высокой производительностью, или более низкое столкновение, или оба.
Если вы уже используете GUID в качестве ключа для идентификации своих данных, это не сложно сделать. Главное, чтобы знать, что GUID, хотя (практически) глобально уникальны, даже близко не распределены равномерно по диапазону. Я использую алгоритм хэширования SHA1 для хеширования GUID, потому что, несмотря на его недостатки в качестве криптографического алгоритма, он хорошо генерирует равномерно распределенные хэши, не требуя слишком большой нагрузки на сервер (с точки зрения вычислений и оперативной памяти).
Как примечание, переходя от GUID к long, вы создаете потерю данных (GUID эквивалентны 128-битному целому числу). Поскольку цель состоит в том, чтобы распределить данные по разделам, это нормально... не переживайте по мелочам. На самом деле вы можете использовать меньший диапазон, чем Int64, но если у вас уже есть GUID, то зачем беспокоиться.
См. Код, приведенный выше, для метода расширения для создания ключа раздела из GUID. Мой код реализации сворачивает его в две строки, но я разбил его ниже, чтобы я мог аннотировать его.
public static ServicePartitionKey ToPartitionKey(this Guid id)
{
// Hash algorithms need byte arrays, so we're converting the Guid here
byte[] guidBytes = id.ToByteArray();
// SHA1 is light weight and good at creating distribution across the range.
// Do not use for encryption!
SHA1CryptoServiceProvider hasher = new SHA1CryptoServiceProvider();
// Hash the Guid's bytes.
byte[] hashedBytes = hasher.ComputeHash(guidBytes);
// Now that our data is repeatibly but distributed evenly, we make it a long
long guidAsLong = BitConverter.ToInt64(hashedBytes, 0);
// return the partition key
return new ServicePartitionKey(guidAsLong);
}