У меня есть таблица в SQL Server 2014, которая выглядит следующим образом:
CREATE TABLE dbo.MyTable
(
[id1] [bigint] NOT NULL,
[id2] [bigint] NOT NULL,
[col1] [int] NOT NULL default(0),
[col2] [int] NOT NULL default(0)
)
где (id1, id2) является PK. По сути, id1 - это идентификатор для группировки набора результатов (id2, col1, col2), чей pk равен id2.
Я пытаюсь использовать таблицу в памяти, чтобы избавиться от существующей дисковой таблицы, которая является моим узким местом.
- Данные в таблице записываются -> читаются -> удаляются один раз.
- Каждое значение id1 имеет несколько (десятки / сотни) тысяч id2.
- Данные хранятся в таблице в течение очень короткого промежутка времени, например, 20 секунд.
Запросы, выполненные в этой таблице, следующие:
-- INSERT (can vary from 10s to 10,000s of records):
INSERT INTO MyTable
SELECT @fixedValue, id2, col1, col2 FROM AnotherTable
-- READ:
SELECT id2, col1
FROM MyTable INNER JOIN OtherTbl ON MyTable.id2 = OtherTbl.pk
WHERE id1 = @value
ORDER BY col1
-- DELETE:
DELETE FROM MyTable WHERE id1 = @value
Вот текущее определение, которое я использовал для таблицы:
CREATE TABLE dbo.SearchItems
(
[id1] [bigint] NOT NULL,
[id2] [bigint] NOT NULL,
[col1] [int] NOT NULL default(0),
[col2] [int] NOT NULL default(0)
CONSTRAINT PK_Mem PRIMARY KEY NONCLUSTERED (id1,id2),
INDEX idx_Mem HASH (id1,id2) WITH (BUCKET_COUNT = 131072)
) WITH (MEMORY_OPTIMIZED = ON, DURABILITY = SCHEMA_ONLY)
К сожалению, это определение приводит к снижению производительности по сравнению с предыдущей ситуацией с таблицей на основе диска. Порядок величины более или менее на 10% выше (что в некоторых случаях достигает 100%, поэтому удваивается).
Более того, я ожидал получить супер-преимущество в сценариях с высоким уровнем параллелизма, учитывая архитектуру без блокировок, рекламируемую Microsoft. Вместо этого наихудшие показатели происходят именно тогда, когда несколько таблиц одновременно выполняют несколько запросов к таблице.
Вопросов:
- какой правильный BUCKET_COUNT установить?
- какой индекс я должен использовать?
- почему производительность хуже, чем с дисковой таблицей?
Запрос sys.dm_db_xtp_hash_index_stats возвращает:
total_bucket_count = сто тридцать одна тысяча семьдесят два empty_bucket_count = 0 avg_chain_len = 873 max_chain_length = 1009
Я изменил количество сегментов, поэтому вывод sys.dm_db_xtp_hash_index_stats :
total_bucket_count = 134217728 empty_bucket_count = 131664087 avg_chain_len = 1 max_chain_length = 3
Тем не менее, результаты почти такие же, если не хуже.
источник
OPTION(OPTIMIZE FOR UNKNOWN)
(см. Советы по таблице )?select * from sys.dm_db_xtp_hash_index_stats
? Кроме того, эта ссылка должна ответить на большинство / все ваши вопросы: msdn.microsoft.com/en-us/library/…Ответы:
Хотя этот пост не будет полным ответом из-за недостатка информации, он должен быть в состоянии указать вам правильное направление или иным образом получить представление, которым вы впоследствии сможете поделиться с сообществом.
Это беспокоит, поскольку это определенно не должно иметь место. Определенные рабочие нагрузки не предназначены для таблиц памяти (SQL 2014), и некоторые рабочие нагрузки поддаются ему. В большинстве ситуаций может быть минимальное снижение производительности, просто путем миграции и выбора правильных индексов.
Первоначально я думал очень узко о ваших вопросах относительно этого:
Первоначально я полагал, что есть проблема с фактической таблицей в памяти и индексами, не являющимися оптимальными. Хотя существуют некоторые проблемы с определением хеш-индекса, оптимизированного для памяти, я считаю, что реальная проблема связана с используемыми запросами.
Эта вставка должна быть очень быстрой, если она задействует только таблицу в памяти. Это, однако, также включает таблицу на основе диска и подвергается всем блокировкам и блокировкам, связанным с этим. Таким образом, в реальном времени трата здесь на основе таблицы диска.
Когда я провел быструю проверку на 100 000 вставок строк из таблицы на основе диска после загрузки данных в память - это было время отклика менее секунды. Однако большая часть ваших данных хранится только в течение очень короткого промежутка времени, менее 20 секунд. Это не дает много времени, чтобы действительно жить в кеше. Кроме того, я не уверен, насколько велик на
AnotherTable
самом деле, и не знаю, считываются ли значения с диска или нет. Мы должны положиться на вас за эти ответы.С помощью запроса Выбрать:
Опять же, мы во власти производительности таблиц interop + disk. Кроме того, сортировки недешевы для индексов HASH, поэтому следует использовать некластеризованный индекс. Об этом говорится в руководстве по индексам, которое я привел в комментариях
Чтобы привести некоторые фактические факты, основанные на исследованиях, я загрузил
SearchItems
в память таблицу с 10 миллионами строк иAnotherTable
100 000, поскольку я не знал ее фактического размера или статистики. Затем я использовал запрос select для выполнения. Кроме того, я создал расширенный сеанс событий в wait_completed и поместил его в кольцевой буфер. Это было убрано после каждого запуска. Я также побежалDBCC DROPCLEANBUFFERS
имитировать среду, в которой все данные могут быть не резидентными.Результаты не были чем-то впечатляющим, если смотреть на них в вакууме. Так как ноутбук, на котором я тестирую этот компьютер, использует SSD более высокого класса, я искусственно уменьшил производительность диска для используемой виртуальной машины.
Результаты были получены без информации ожидания после 5 запусков запроса только по таблице в памяти (без объединения и без подзапросов). Это в значительной степени, как и ожидалось.
Однако при использовании исходного запроса у меня были ожидания. В данном случае это был PAGEIOLATCH_SH, который имеет смысл, когда данные считываются с диска. Поскольку я являюсь единственным пользователем в этой системе и не тратил время на создание обширной тестовой среды для вставок, обновлений, удалений для объединенной таблицы, я не ожидал, что какая-либо блокировка или блокировка вступят в силу.
В этом случае, опять же, значительная часть времени была потрачена на таблицу на основе диска.
Наконец запрос на удаление. Поиск строк, основанных только на ID1, не очень эффективен с индексом has. Хотя верно, что предикаты равенства - это то, для чего нужны хеш-индексы, область, в которую попадают данные, основана на целых хэшированных столбцах. Таким образом, id1, id2, где id1 = 1, id2 = 2 и id1 = 1, id2 = 3, будут хэшироваться в разные сегменты, поскольку хэш будет проходить через (1,2) и (1,3). Это не будет простым сканированием диапазона B-Tree, поскольку хеш-индексы структурированы не одинаково. Тогда я ожидал бы, что это не будет идеальным показателем для этой операции, однако я бы не ожидал, что это займет на несколько порядков больше, чем у опытных. Мне было бы интересно увидеть wait_info по этому вопросу.
Хотя верно, что блокировки используются для логической согласованности, операции все еще должны быть атомарными. Это делается с помощью специального оператора сравнения на основе ЦП (именно поэтому In-Memory работает только с некоторыми [хотя и почти всеми процессорами, изготовленными за последние 4 года]). Таким образом, мы не получаем все бесплатно, еще будет время для выполнения этих операций.
Еще один момент, о котором следует упомянуть, - это тот факт, что почти во всех запросах используется интерфейс T-SQL (а не компилируемый в естественном порядке SPROC), который затрагивает хотя бы одну таблицу на основе дисков. Вот почему я считаю, что, в конце концов, у нас фактически нет увеличения производительности, поскольку мы все еще ограничены производительностью таблиц на основе дисков.
Следовать за:
Создайте расширенный сеанс событий для wait_completed и укажите известный вам SPID. Запустите запрос и дайте нам вывод или используйте его для внутреннего использования.
Дайте нам обновленную информацию о выходе из # 1.
Не существует магического числа для определения количества сегментов для хеш-индексов. В основном, до тех пор, пока сегменты не будут полностью заполнены, а цепочки рядов останутся ниже 3 или 4, производительность должна оставаться приемлемой. Это все равно, что спросить: «На что мне установить файл журнала?» - это будет зависеть от процесса, базы данных, типа использования.
источник