Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Область применения: SQL Server
База данных SQL Azure Управляемый экземпляр SQL Azure
Предварительные требования
Сведения, необходимые для понимания этой статьи, доступны в следующих статьях:
Практические величины
При создании хэш-индекса для оптимизированной для памяти таблицы число контейнеров необходимо указывать во время создания. В большинстве случаев идеальное число контейнеров должно находиться в диапазоне, в 1–2 раза превышающем число уникальных значений в ключе индекса.
Однако даже если количество контейнеров BUCKET_COUNT умеренно ниже или выше предпочитаемого диапазона, производительность хэш-индекса, скорее всего, будет допустимой или приемлемой.
Рекомендуется задать параметру BUCKET_COUNT в хэш-индексе значение, примерно равное предполагаемому количеству строк, которые могут появиться в оптимизированной по памяти таблице.
Предположим, что ваша таблица, которая увеличивается, имеет 2 000 000 строк, но ожидается, что она увеличится в 10 раз до 20 000 000 строк. Начните с числа контейнеров, которое в 10 раз превышает количество строк в таблице. Так вы получите запас для увеличения количества строк.
- Желательно увеличить количество сегментов, когда количество строк достигает первоначального количества сегментов.
- Даже если количество строк увеличивается в пять раз больше, чем число контейнеров, производительность по-прежнему остается хорошей в большинстве ситуаций.
Предположим, что хэш-индекс содержит 10 000 000 уникальных значений ключей.
- Можно считать, что 2 000 000 - это минимально допустимое количество корзин. Степень снижения производительности может быть приемлемой.
В индексе слишком много повторяющихся значений?
Если значения хэш-индекса имеют высокий процент дубликатов, хэш-контейнеры имеют более длинные цепочки.
Возьмем таблицу SupportEvent, которая использовалась в одном из предыдущих блоков кода T-SQL. Следующий код T-SQL демонстрирует определение и отображение отношения всех значений к уникальным значениям:
-- Calculate ratio of: Rows / Unique_Values.
DECLARE @allValues float(8) = 0.0, @uniqueVals float(8) = 0.0;
SELECT @allValues = Count(*) FROM SupportEvent;
SELECT @uniqueVals = Count(*) FROM
(SELECT DISTINCT SupportEngineerName
FROM SupportEvent) as d;
-- If (All / Unique) >= 10.0, use a nonclustered index, not a hash.
SELECT Cast((@allValues / @uniqueVals) as float) as [All_divby_Unique];
go
- Соотношение 10.0 или выше означает, что хэш будет плохим типом индекса. Вместо этого можно использовать некластеризованный индекс.
Устранение неполадок, связанных с числом корзин хэш-индекса
В этом разделе рассказывается, как устранять неполадки, связанные с числом контейнеров хэш-индекса.
Отслеживание статистики для цепочек и пустых контейнеров
Для отслеживания показателей работоспособности хэш-индексов можно выполнить следующую инструкцию T-SQL SELECT. Эта инструкция SELECT использует динамическое административное представление с именем sys.dm_db_xtp_hash_index_stats.
SELECT
QUOTENAME(SCHEMA_NAME(t.schema_id)) + N'.' + QUOTENAME(OBJECT_NAME(h.object_id)) as [table],
i.name as [index],
h.total_bucket_count,
h.empty_bucket_count,
FLOOR((
CAST(h.empty_bucket_count as float) /
h.total_bucket_count) * 100)
as [empty_bucket_percent],
h.avg_chain_length,
h.max_chain_length
FROM
sys.dm_db_xtp_hash_index_stats as h
JOIN sys.indexes as i
ON h.object_id = i.object_id
AND h.index_id = i.index_id
JOIN sys.memory_optimized_tables_internal_attributes ia ON h.xtp_object_id=ia.xtp_object_id
JOIN sys.tables t on h.object_id=t.object_id
WHERE ia.type=1
ORDER BY [table], [index];
Сравните результаты, полученные от инструкции SELECT, со следующими статистическими рекомендациями.
- Пустые контейнеры:
- 33 % является хорошим целевым значением, но обычно подходит более высокий процент (даже 90 %).
- Если количество контейнеров равно количеству уникальных значений ключей, то примерно 33 % контейнеров пусты.
- Значение ниже 10 % считается слишком маленьким.
- Цепочки в контейнерах:
- Средняя длина цепочки, равная 1, является оптимальной в случае, когда нет повторяющихся значений ключей индекса. Обычно приемлемыми являются цепочки длиной до 10.
- Если средняя длина цепочки превышает 10 и доля пустых контейнеров превышает 10 %, это означает, что данные содержат так много дубликатов, что хэш-индекс может быть не самым подходящим типом индекса.
Демонстрация цепочек и пустых контейнеров
Следующий блок кода T-SQL позволяет легко протестировать SELECT * FROM sys.dm_db_xtp_hash_index_stats;
. Завершение блока кода занимает 1 минуту. Блок кода включает следующие этапы:
- Создает оптимизированную для памяти таблицу, которая имеет несколько хэш-индексов.
- Заполняет эту таблицу несколькими тысячами строк.
a. Для настройки частоты повторяющихся значений в столбце StatusCode используется оператор остатка от деления.
b. В цикле в таблицу вставляется (INSERT) 262 144 строки примерно за 1 минуту. - Выводит сообщение с просьбой выполнить предыдущую команду SELECT из sys.dm_db_xtp_hash_index_stats.
DROP TABLE IF EXISTS SalesOrder_Mem;
go
CREATE TABLE SalesOrder_Mem
(
SalesOrderId uniqueidentifier NOT NULL DEFAULT newid(),
OrderSequence int NOT NULL,
OrderDate datetime2(3) NOT NULL,
StatusCode tinyint NOT NULL,
PRIMARY KEY NONCLUSTERED
HASH (SalesOrderId) WITH (BUCKET_COUNT = 262144),
INDEX ix_OrderSequence
HASH (OrderSequence) WITH (BUCKET_COUNT = 20000),
INDEX ix_StatusCode
HASH (StatusCode) WITH (BUCKET_COUNT = 8),
INDEX ix_OrderDate NONCLUSTERED (OrderDate DESC)
)
WITH (MEMORY_OPTIMIZED = ON, DURABILITY = SCHEMA_AND_DATA)
go
--------------------
SET NOCOUNT ON;
-- Same as PK bucket_count. 68 seconds to complete.
DECLARE @i int = 262144;
BEGIN TRANSACTION;
WHILE @i > 0
BEGIN
INSERT SalesOrder_Mem
(OrderSequence, OrderDate, StatusCode)
Values
(@i, GetUtcDate(), @i % 8); -- Modulo technique.
SET @i -= 1;
END
COMMIT TRANSACTION;
PRINT 'Next, you should query: sys.dm_db_xtp_hash_index_stats .';
go
Предыдущий цикл INSERT
выполняет указанные ниже действия.
- Вставляет уникальные значения в индекс первичного ключа и в ix_OrderSequence.
- Вставляет несколько сотен тысяч строк, которые представляют только восемь разных значений для
StatusCode
. Следовательно, существует высокая доля дублирования значений в индексе ix_StatusCode.
Если число контейнеров не является оптимальным, изучите следующие выходные данные инструкции SELECT из sys.dm_db_xtp_hash_index_statsдля решения проблемы. Для этих результатов мы добавили WHERE Object_Name(h.object_id) = 'SalesOrder_Mem'
в операцию SELECT, скопированную из раздела Г.1.
Результаты SELECT
отображаются после кода и искусственно разбиваются на две более узкие таблицы для более удобного отображения.
- Приведем результаты для числа контейнеров.
ИмяИндекса | общее количество контейнеров | счетчик пустых корзин | Процент пустого ведра |
---|---|---|---|
ix_OrderSequence | 32768 | 13 | 0 |
ix_StatusCode | 8 | 4 | 50 |
PK_SalesOrd_B14003... | 262144 | 96525 | 36 |
- Приведем результаты для длины цепочки.
Название индекса | средняя длина цепи | максимальная_длина_цепи |
---|---|---|
ix_OrderSequence | 8 | 26 |
ix_StatusCode | 65536 | 65536 |
PK_SalesOrd_B14003... | 1 | 8 |
Разберем предыдущие таблицы результатов для трех хэш-индексов.
ix_StatusCode:
- 50 % контейнеров пусты, это хорошо.
- Однако средняя длина цепочки очень высока (65536).
- Это указывает на большое количество повторяющихся значений.
- Поэтому хэш-индекс в данном случае не подходит. Вместо этого следует пользоваться некластеризованным индексом.
ix_OrderSequence:
- 0 % контейнеров пусты, это слишком мало.
- Средняя длина цепочки составляет 8 даже несмотря на то, что все значения в этом индексе являются уникальными.
- Поэтому число контейнеров следует увеличить, чтобы уменьшить среднюю длину цепочки до 2 или 3.
- Поскольку ключ индекса имеет 262 144 уникальных значения, число контейнеров должно быть не менее 262 144.
- Если в будущем ожидается рост, количество сегментов должно быть больше.
Индекс первичного ключа (PK_SalesOrd_...):
- 36 % контейнеров пусты, это хорошо.
- Средняя длина цепочки равна 1, что тоже является хорошим показателем. Изменения не требуются.
Достижение компромисса
Рабочие нагрузки OLTP сосредотачиваются на отдельных строках. Полное сканирование таблиц обычно не является критическим для производительности для рабочих нагрузок OLTP. Таким образом, приходится выбирать между использованием памяти и производительностью проверок на равенство и операций вставки.
Если использование памяти имеет большее значение:
- Выберите число контейнеров, близкое к количеству уникальных значений ключа индекса.
- Количество контейнеров не должно быть значительно ниже числа уникальных значений ключа индекса, так как это влияет на большинство операций DML, а также время, необходимое для восстановления базы данных после перезапуска сервера.
Если производительность проверок на равенство имеет большее значение:
- допустимо увеличить число контейнеров, так чтобы оно превышало количество уникальных значений индекса в 2–3 раза. Более высокое число означает:
- Более быстрое получение значений при поиске конкретного значения.
- Увеличенное использование памяти.
- Увеличение времени, необходимого для полного сканирования хэш-индекса.
Дополнительные материалы
Хэш-индексы для оптимизированных для памяти таблиц
Некластеризованные индексы для таблиц, оптимизированных для памяти