Внутренние компоненты ESENT: ожидаемое поведение JetPrereadKeys () - PullRequest
0 голосов
/ 15 января 2020

У меня есть приложение, работающее со значительным объемом данных (более 100 ГБ), хранящихся в ESENT. Схема таблицы: 12-байтовые JET_bitColumnFixed ключи и JET_coltypLongBinary значения с типичным размером около 2 КиБ. Размер страницы установлен на 32 КиБ. Я не изменяю пороговое значение размера по умолчанию в 1024 байта для внешних длинных значений, поэтому я думаю, что эти значения в основном хранятся внешне.

Я заинтересован в улучшении поиска холодного кэша и восстановить производительность, потому что операции выполняются партиями, а ключи известны заранее. Насколько я понимаю, API JetPrereadKeys () предназначен для повышения производительности в таких случаях, но, как оказалось, я не вижу никаких изменений в реальном поведении с или без этого вызова.

Далее следуют подробности:

  • В моем случае JetPrereadKeys () всегда сообщает о достаточном количестве предварительно прочитанных ключей, равном количеству ключей Я представил при вызове API. Представленные ключи соответствующим образом отсортированы, как указано в документации.

  • Я пробовал как синхронный, так и асинхронный подходы, где асинхронный подход: отправка вызова предварительного чтения в пул потоков, продолжая искать и извлекать данные в текущем потоке.

  • Я пробовал оба доступных режима кэширования ESENT, где он использует MMAP или выделенный кэш страницы, пробуя все доступные комбинации параметры JET_paramEnableViewCache и JET_paramEnableFileCache .

  • Я не могу, за небольшим исключением, увидеть какие-либо различия в зарегистрированных операциях ввода-вывода с и без предварительного чтения. То есть я ожидаю, что эта операция приведет к (предпочтительно асинхронному) извлечению необходимых внутренних узлов B-дерева. Но единственное, что я вижу, - это случайное синхронное маленькое чтение, возникающее из стека самого JetPrereadKeys (). Размер чтения небольшой, в том смысле, что я не думаю, что он мог бы предварительно извлечь всю необходимую информацию.

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

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


Вопросы:

  1. Каково ожидаемое поведение JetPrereadKeys () в описанном случае?

  2. Стоит ли ожидать увидеть другой шаблон ввода-вывода и лучшую производительность, если я использую этот API? Должен ли я ожидать увидеть синхронное или асинхронное предварительное чтение данных?

  3. Есть ли другой подход, который я мог бы попытаться улучшить производительность ввода-вывода, каким-то образом намекая ESENT о предстоящая партия?

1 Ответ

1 голос
/ 16 января 2020

API JetPrereadKeys () выполняет синхронизацию c до уровня родительского уровня, а затем ставит в очередь asyn c IOs для всех листовых страниц, необходимых для нужных ключей / записей ... Я думаю, что ответы # 2. Если записи вашей основной таблицы (обратите внимание, что длинные значения пакета / LV хранятся в отдельном дереве) являются поверхностными или полностью кэшированными, этот JetPrereadKeys () может не помочь. Однако, если ваше основное дерево в таблице большое и глубокое, то этот API может существенно помочь ... он зависит только от формы и распространения ваших данных, которые вы получаете. Вы можете рассказать некоторые основные сведения о своей таблице, выбрасывая пространство и просматривая глубину деревьев и получая представление о страницах «Данные». Могу я предложить:

esentutl /ms Your.Db /v /fName,Depth,Internal,Data

Список имен таблиц, глубины, сколько внутренних страниц и сколько страниц данных конечного уровня. Отдельные строки будут перечислены для основного дерева записей по имени таблицы, а затем LV ниже / как «[Длинные значения]» под ним.

Также обратите внимание, что эти предварительно прочитанные ключи также не распространяются на пакетные LV. .. так что опять же, если вы сразу же прочитаете столбец LV пакета - к сожалению, вы прикрепитесь к IO.

По умолчанию ESE выделяет и управляет исключительно собственным буфером базы данных / кэшем страниц. JET_paramEnableFileCache в первую очередь предназначен для (обычно меньших) клиентских процессов, которые выходят (или, по крайней мере, JetTerm / JetDetach их БД) и перезапускают много ... так, где частный буферный кэш ESE будет потерян при каждом выходе ... но JET_paramEnableFileCache параметр, так что данные могут все еще находиться в файловом кеше, если они недавно выходили. Однако это не рекомендуется для больших БД, поскольку это приводит к двойному кешированию данных в буферном кеше ESE и в файловом кеше NTFS / ReFS. JET_paramEnableViewCache улучшает предыдущий параметр и несколько улучшает этот двойной кеширование ... но он может только сэкономить память / не двойной буфер на чистых / неизмененных буферах страниц. Для больших БД оставьте оба этих параметра выключенными / ложными. Кроме того, если вы не используете эти параметры, тогда проще протестировать «холодный перфоманс» ... просто скопируйте большой файл (100 МБ, может быть 1 или 2 ГБ) пару раз на вашем HD после того, как ваше приложение выйдет (очистить HD кеш), и ваши данные будут холодными. ; -)

Итак, теперь, когда мы упомянули о кешировании ... последнее, что я думаю, вероятно, является вашей реальной проблемой (если это не «форма ваших данных», о которой я упоминал выше) .. Откройте perfmon и найдите объекты perf «База данных» и / или «База данных ==> Экземпляры» (они предназначены для ESENT) и посмотрите, насколько велик размер вашего кэша [«Размер кэша базы данных» или «Размер кэша базы данных (МБ)». "] и посмотрите, насколько велик ваш доступный пул / [" Database Cache% Available "] ... вам, конечно, придется взять этот% и сделать математику с размером кэша вашей базы данных, чтобы получить представление ... НО, если это низкий, это может быть вашей проблемой ... это потому, что JetPrereadKeys будет использовать только уже имеющиеся буферы, поэтому вы должны иметь здоровый / достаточно большой доступный пул. Либо увеличьте JET_paramCacheSizeMin, чтобы он был больше, либо задайте JET_paramStartFlushThreshold / JET_paramStopFlushThreshold, чтобы размер доступного кеша был больше% от общего размера кеша ... обратите внимание, что они установлены пропорционально JET_paramCacheSizeMax, например, установка:

paramCacheSizeMin = 500
paramCacheSizeMax = 100000
paramStartFlush.. =   1000
paramStopFlushT.. =   2000

будет означать, что ваши пороги запуска и остановки равны 1% и 2% соответственно от вашего текущего размера кэша, каким бы он ни был. Таким образом, если кэш находится в 500 буферах (мин), 5 и 10 будут вашими порогами начала / остановки - то есть диапазон, в котором будет находиться ваш доступный пул, если позже он увеличится до 10000 буферов, то ваш доступный пул будет в диапазоне от 100 до 200 буферы. В любом случае, вы хотите, чтобы эти числа были достаточно хорошим диапазоном, чтобы у вас было достаточно буферов для всех конечных страниц, которые могут понадобиться JetPrereadKeys.

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

Спасибо,

Бретт Ширли [MSFT]

Extensible Storage Engine Developer

Эта публикация предоставляется "КАК ЕСТЬ" без каких-либо гарантий и не дает никаких прав.

PS - Последнее, с чем вам может понравиться играть: JetGetThreadStats / JET_THREADSTATS, он сообщает вам некоторые из наших внутренних операций, которые мы делаем в API. Вы в основном читаете значения до и после и JET API и вычитаете их, чтобы получить количество операций для этого JET API. Итак, вы увидите cPagePreread там ... это будет хороший способ узнать, отправляет ли JetPrereadKeys с Asyn c IOs, что должно помочь перфекту. Обратите внимание, что в старых ОС, к сожалению, был сломан конкретный счетчик, но я не помню, когда он был сломан и исправлен ... win7 - win8, win8 - win8.1. Если у вас Win10, то никаких проблем к тому времени это точно не решило. ;-) А также cPageRead синхронизирует c чтение страниц (что может go для внутренних узлов) ... Я думаю, вы найдете их очень поучительными для затрат на различные JET API.

...