Напомню, в предыдущей статье мы начали разговор об интеллектуальных возможностях по настройке производительности SQL Server 2019, и рассмотрели возможности по умной настройке запросов в SQL сервере. В этой статье мы разовьем тему и подробно поговорим об еще одной возможности у улучшению производительности данной СУБД. Речь пойдет о Базе данных в памяти (In-Memory Database).
В SQL Server 2014 мы представили функцию OLTP в памяти (In-Memory OLTP), ключевой концепцией которой была концепция таблиц, оптимизированных для выделения памяти. При использовании этой функции вся таблица хранится в памяти, но именно оптимизированный (читайте: без кратковременных блокировок, latch-free) доступ делает ее особенной. В SQL Server 2016 мы значительно усовершенствовали функцию In-Memory OLTP. Более подробно об этой функции рассказывается по ссылке.
Когда мы работали над новыми функциями для SQL Server 2019, Слава Окс (Slava Oks), Пэм Лахуд (Pam Lahoud), Брайан Карриг (Brian Carrig), Аргенис Фернандес (Argenis Fernandez) и другие члены команды инженеров собрались вместе и решили совместно назвать новый набор функций базой данных в памяти (In-Memory Database). Это новое семейство функций, в которые входит и In-Memory OLTP.
То, что в SQL Server 2019 названо базой данных в памяти, включает в себя целый набор различных функций:
- In-Memory OLTP;
- оптимизированные для выделения памяти метаданные TempDB;
- гибридный буферный пул;
- поддержка постоянной памяти.
Полный список этого набора функций доступен по адресу.
В этом разделе мы рассмотрим все эти новые возможности, кроме In-Memory OLTP (которая не является «новшеством» SQL Server 2019).
Метаданные TempDB, оптимизированные для выделения памяти
На протяжении всего времени, в течение которого я работал с SQL Server, производительность параллельных подключений при использовании временных таблиц была очень низкой. Это приводило к тому, что почти каждый администратор SQL Server настраивал tempdb для использования нескольких файлов. Вы можете прочитать больше об истории приключения с этими ресурсами:
- Inside TempDB (Подробно о TempDB) - выступление Боба Уорда (Bob Ward) на саммите PASS 2017 года;
- блог Пола Рэндала (Paul Randal) о добавлении файлов
tempdb
.
Один из аспектов использования файлов tempdb, которому большинство профессионалов SQL не уделяют особого внимания (поскольку сейчас это стало обычной практикой), заключается в том, что вы создаете для механизма SQL Server схему разделения данных для доступа к страницам размещения, таким как страницы PFS, GAM и SGAM. Подобный тип схемы полезен, потому что работа с использованием временных таблиц приводит к «утяжелению» таких операций, как создание таблиц, выделение страниц, удаление таблиц. В этом случае возникает ситуация конкуренции на этих системных страницах размещения - так называемые кратковременные блокировки, или «защелки» (latches). Создавая несколько файлов, вы распространяете на них и конкуренцию за временные блокировки на страницах этих файлов, что повышает производительность в ситуации, когда с tempdb работает множество параллельных подключений.
После создания нескольких файлов (начиная с версии SQL Server 2016 программа установки может сделать это автоматически, или же вы можете настроить этот параметр установки вручную) при более высокой одновременной рабочей нагрузке на базу данных tempdb число ожиданий временно заблокированных страниц может увеличиться. Эти ожидания затрагивают страницы, принадлежащие объектам, которые вы не можете распознать, например такие, как sysschobjs. Это ожидания блокировки страниц для страниц системных таблиц в базе данных tempdb. При быстром создании и удалении таблиц SQL Server должен выполнять внутренние операции чте- ния/записи на страницах системных таблиц, чтобы поддерживать согласованность метаданных таблиц. Эти операции приводят к увеличению конкуренции пользователей за страницы, где установлены кратковременные блокировки, и, следовательно, к увеличению времени ожидания пользователей в очереди доступа к заблокированному ресурсу. В прошлом, когда клиенты сталкивались с высокой конкуренцией за заблокированные страницы в системных таблицах и обращались ко мне в службу поддержки, я отвечал: «К сожалению, вы должны уменьшить нагрузку на базу данных tempdb, чтобы избежать этой проблемы».
Пэм Лахуд (Pam Lahoud) очень хорошо описывает эту проблему в своем блоге, размещенном по адресу.
В SQL Server 2019 появляется решение - метаданные tempdb, оптимизированные для выделения памяти. В таблицах, оптимизированных для выделения памяти (помните известный проект Hekaton («Гекатон»)?) не может быть кратковременных блокировок страниц, поскольку все данные этих таблиц размещены и хранятся в памяти. Если таблицы, оптимизированные для выделения памяти, являются «просто схемой», то у них нет ограничений, связанных со временем их жизни. Это идеальная платформа для системных таблиц tempdb. Поскольку база данных tempdb создается заново при каждом перезапуске сервера, системные таблицы не должны быть долговечными. А так как в таблицах, оптимизированных для выделения памяти, хранятся только метаданные (а не данные во временных таблицах), потребление памяти для них должно быть небольшим. Равиндер Вуппула (Ravinder Vuppula), ведущий разработчик этого проекта, назвал создание системных таблиц tempdb
«гекатонизированным».
Метаданные tempdb
по умолчанию не используют таблицы, оптимизированные для выделения памяти, при установке SQL Server. Вы должны выполнить следующую команду T-SQL, чтобы включить эту возможность:
ALTER SERVER CONFIGURATION SET MEMORY_OPTIMIZED TEMPDB_METADATA = ON
Вы можете просмотреть мою демонстрацию данной функции на выступлении SQLBits 2019. Брент Озар (Brent Ozar) в своем блоге сказал об этой возможности, увидев ее демонстрацию на саммите PASS в 2018 году: «...это улучшение работы с TempDB просто великолепно. Это реальное улучшение, которое будет иметь огромную популярность. Люди долго боролись с неразрешимыми проблемами конкуренции TempDB и с блокировками и никак не могли справиться с этим».
Но вы должны попробовать эту возможность сами, чтобы увидеть ее в действии. Давайте рассмотрим пример того, как метаданные tempdb, оптимизированные для выделения памяти, могут значительно улучшить параллельную работу приложений, использующих временные таблицы. Все сценарии для этого примера размещены в каталоге tempdb. Этот пример немного сложнее, и для него потребуется координировать или эмулировать одновременную работу нескольких пользователей. Поэтому вам понадобится бесплатный инструмент для нагрузочного тестирования под названием ostress, который можно загрузить по ссылке. Этот инструмент в настоящее время доступен только для Windows. Данный пример будет по-прежнему работать с SQL Server, установленным на Linux; просто вам понадобится клиент Windows для управления параллельными пользовательскими подключениями с помощью ostress.
Кроме того, я настроил свой SQL Server на виртуальной машине с восемью логическими процессорами. Когда я запустил программу установки SQL Server, она автоматически создала восемь файлов данных tempdb
. Я рекомендую вам убедиться, что у вас есть как минимум восемь файлов данных tempdb
, если в вашей системе имеется восемь или более логических процессоров. Подробнее о том, как это сделать, читайте в следующей статье технической поддержки.
- Запустите сценарий disableopttempdb.cmd, чтобы отключить функцию оптимизации метаданных tempdb для выделения памяти. По умолчанию эта возможность отключена; однако рекомендуется запустить этот сценарий на тот случай, если она была включена в какой-то момент. Вам нужно запустить данный сценарий на том сервере, где установлен SQL Server (или использовать другой метод для удаленного перезапуска SQL Server). Для этого сценария понадобится ввести имя учетной записи системного администратора и имя сервера. Вы можете изменить этот способ аутентификации и использовать встроенную аутентификацию, изменив
-Usa
на-E
, и не забудьте заменить текущее значение параметра-S
на имя вашего сервера:sqlcmd -Usa -idisableopttempdb.sql -Sbwsql2019 net stop mssqlserver net start mssqlserver
Как видите, этот сценарий дает инструкцию Windows Server перезапустить службу SQL Server (mssqlserver). Вы можете написать свой собственный сценарий для Linux, используя команду типа
sudo systemctl restart mssql-server
для перезапуска SQL Server.Сценарий disableopttempdb.sql содержит следующую инструкцию T-SQL:
ALTER SERVER CONFIGURATION SET MEMORY_OPTIMIZED TEMPDB_ METADATA = OFF GO
- Запустите сценарий T-SQL tempstress_ddl.sql, чтобы создать базу данных и хранимую процедуру, которая просто создает временную таблицу:
DROP DATABASE IF EXISTS DallasMavericks GO CREATE DATABASE DallasMavericks GO USE DallasMavericks GO CREATE OR ALTER PROCEDURE letsgomavs AS CREATE TABLE #gomavs (col1 INT) GO
Как видите, хранимая процедура на самом деле ничего не делает с временной таблицей. Это показывает минимальный объем рабочей нагрузки, который может повлиять на параллельную работу с метаданными временной таблицы (поскольку любой выход из хранимой процедуры автоматически удаляет временную таблицу).
- Теперь вы готовы к тому, чтобы создать паралельную рабочую нагрузку на tempdb с помощью ostress. Используйте сценарий tempstress. cmd для создания этой рабочей нагрузки с применением
ostress
:ostress -Usa -Q"exec letsgomavs" -n50 -r10000 -dDallasMavericks -Sbwsql2019
Вам может потребоваться изменить некоторые из параметров сценария, например изменить
-Usa
на-E
для использования встроенной аутентификации Windows. Вы также можете изменить имя сервера (изменив параметр-S
). Параметр-n50
- это количество пользователей, создающих рабочую нагрузку, а -r10000
- количество итераций для каждого пользователя. Обратите внимание на использование параметра-Q
для непосредственного запуска хранимой процедуры - прием, который я узнал, работая над ранними версиями этого сценария для демонстрации. Использование параметра-Q
для ostress для непосредственного запуска запроса работает быстрее, чем инструкция сценария с-i
.Если вы используете учетную запись администратора (
-Usa
), вам будет предложено ввести пароль, после чего гонки начнутся. В зависимости от мощности вашего компьютера этот нагрузочный сценарий будет выполняться несколько минут. - Пока он выполняется, создайте новое подключение к SQL Server и откройте сценарий T-SQL pageinfo.sql:
USE tempdb GO SELECT object_name(page_info.object_id), page_info.* FROM sys.dm_exec_requests AS d CROSS APPLY sys.fn_PageResCracker(d.page_resource) AS r CROSS APPLY sys.dm_db_page_info(r.db_id, r.file_id, r.page_id,'DETAILED') AS page_info GO
Этот сценарий использует новые функциональные возможности в SQL Server 2019 для извлечения информации о странице из ресурса, указанного в sys.dm_exec_requests, и для выгрузки в многоколоночном формате полей заголовка страницы.
Почему и в каком случае вам нужно использовать этот сценарий? Потому, что когда у вас появляются ожидания кратковременных блокировок страниц для базы данных tempdb, вы одновременно с этим видите отчет о ресурсах в формате
<dbid>: <fileid>: <pageid>
. До того, как появилась эта функция, вам нужно было вручную выполнять командуDBCC PAGE
(команду, которая официально не поддерживается), чтобы выяснить, к какому объекту относится страница, для которой наблюдается ожидание снятия кратковременной блокировки. Метод, продемонстрированный здесь, теперь официально поддерживается при определении страницы в сценарии ожидания снятия кратковременной блокировки.Ваши результаты при выполнении запроса на предыдущем шаге должны выглядеть примерно так, как показано на рис. 1.
Рис. 1. Кратковременная блокировка страницы: ожидание системных таблиц в базе данных
tempdb
Как описано выше,
sysschobjs
- это системная таблица, являющаяся «узким местом», поскольку временные таблицы создаются и удаляются. - Теперь давайте включим оптимизацию метаданных
tempdb
для выделения памяти. Запустите сценарий optimizetempdb.cmd на сервере, где установлен SQL Server. Этот сценарий выполняет следующее, однако вы можете использовать другие методы для включения функции и перезапуска SQL Server:sqlcmd -Usa -ioptimizetempdb.sql -Sbwsql2019 net stop mssqlserver net start mssqlserver
Сценарий optimizetempdb.sql содержит следующую инструкцию T-SQL:
ALTER SERVER CONFIGURATION SET MEMORY_OPTIMIZED TEMPDB_METADATA = ON GO
- Убедитесь, что оптимизация метаданных
tempdb
для выделения памяти включена, изучив файл журнала ошибок SQL ServerERRORLOG
. Вы должны увидеть такую запись вERRORLOG
:Tempdb started with memory-optimized metadata.
- Теперь снова запустите сценарий tempstress.cmd, создающий рабочую нагрузку. На этот раз при той же рабочей нагрузке, без изменений в приложении, для выполнения сценария потребуется чуть более 30 секунд.
- Запустите сценарий pageinfo.sql еще раз, чтобы увидеть, имеет ли место ожидание снятия кратковременной блокировки страницы. Ваши результаты должны содержать 0 строк!
- Пока выполняется сценарий, запущенный на предыдущем шаге, создайте новое подключение к SQL Server и в нем запустите сценарий T-SQL find_memoptimized_tables.sql. Ваши результаты должны выглядеть примерно так, как показано на рис. 2.
Рис. 2. Системные таблицы tempdb
после оптимизации для выделения памяти
В окне результатов вы можете видеть все системные таблицы, оптимизированные для выделения памяти (ваш результат может содержать даже больше таблиц, поскольку эта функция улучшается). Обратите внимание на серьезные изменения значений в sysschobjs
(но это не единственная системная таблица, которую затронула оптимизация).
Вы можете задаться вопросом: сколько дополнительной памяти потребляется при использовании этой функции? Пока вы все еще работаете в данной среде, выполните запрос к DMV sys.dm_os_memory_clerks. Вы увидите строку, в которой будут содержаться записи: type = MEMORYCLERK_XTP
и name = DB_ID_2
. Столбец pages_kb
- это примерное количество памяти, потребляемой метаданными tempdb
, оптимизированными для выделения памяти. Как мы видим, для этого примера оно составляет около 200 Мб.
На данном этапе вы можете оставить флаг, переключающий оптимизацию для выделения памяти метаданных tempdb, включенным для своего сервера, но если вы хотите выключить его, используйте сценарий disableopttempdb.cmd.
Вы можете видеть преимущества этой функции, встроенной в ядро СУБД. Вам нужно лишь установить один параметр конфигурации сервера, перезапустить SQL Server - и уже можно начинать работать.
Если вы имеете доступ к представлениям каталога (catalog views
) в базе данных tempdb
, то можете заметить, что при использовании метаданных tempdb
, оптимизированных для выделения памяти, существует несколько ограничений. Вы можете прочитать об этих ограничениях, а также найти более подробную информацию об этой возможности, в документации, размещенной по адресу.
Гибридный буферный пул
Устройства с постоянной памятью существуют уже несколько лет, но сейчас они начинают набирать популярность. Концепция основана на постоянно работающей аппаратной памяти с бесперебойным источником питания. Подумайте о скорости оперативной памяти, обладающей еще одним новым преимуществом: любые сохраненные данные гарантированно выдержат перезапуск питания. Одним из наиболее популярных решений в области постоянной памяти является постоянная память от Intel под названием Optane.
Наша команда разработчиков SQL Server все время ищет способы оптимизировать доступ к данным, и при наличии постоянной памяти существует несколько возможностей для такой оптимизации. Фактически еще в SQL Server 2016 существовала функция, называемая «остатком кеширования журнала», основанная на использовании постоянной памяти (см. сообщение в блоге Кевина Фарли (Kevin Farlee) на эту тему).
Поскольку постоянная память - это одна из разновидностей памяти, SQL Server может обращаться к любым данным, хранящимся на постоянном запоминающем устройстве, как будто это обычная память. Это означает, что SQL Server может найти способ обойти код ядра при обработке ввода-вывода, выполняемого при доступе к данным на устройствах постоянной памяти.
Одной из таких новых возможностей является гибридный буферный пул. Идея заключается в том, что если вы разместите файлы данных вашей базы данных в постоянной памяти, SQL Server может просто получить доступ к страницам файла данных с этого устройства без необходимости копировать данные из файла данных на страницу буферного пула. Гибридный буферный пул использует вызовы ядра, привязанные к памяти, чтобы реализовать концепцию базы данных в памяти. Если страница базы данных была изменена, ее необходимо скопировать в буферный пул и затем в конечном итоге записать обратно на постоянное запоминающее устройство.
Производительность такой системы может варьироваться в зависимости от возможности использования тех преимуществ, которые предоставляет использование гибридного буферного пула, но обычно от этой технологии можно ожидать некоторого повышения производительности, особенно если для вашей базы данных характерны высокие нагрузки на чтение.
В SQL Server, если вы разместили один или несколько файлов базы данных на постоянном запоминающем устройстве, вы можете включить гибридный буферный пул для всех баз данных для вашего сервера с помощью оператора T-SQL:
ALTER SERVER CONFIGURATION SET MEMORY_OPTIMIZED HYBRID_BUFFER_POOL = ON
Примечание. При включении гибридного буферного пула для всех баз данных необходимо перезапустить SQL Server.
Вы можете включить гибридный буферный пул для конкретной базы данных с помощью следующего оператора T-SQL (при этом вам не потребуется перезапускать сервер):
ALTER DATABASE <имя_базы_данных> SET MEMORY_OPTIMIZED = ON
Чтобы узнать больше о том, как включить на ваших устройствах постоянную память для баз данных, как отключить гибридный буферный пул и как использовать гибридный буферный пул, обратитесь к документации по адресу.
Поддержка постоянной памяти
Если вы не хотите включать гибридный буферный пул, но хотели бы, чтобы SQL Server использовал возможности чтения и записи данных и журнала транзакций на устройствах с постоянной памятью, вы можете настроить используемое устройство как устройство постоянной памяти в Linux. SQL Server автоматически обнаружит его и будет использовать операции на основе памяти для перемещения данных в кеш SQL Server и на устройство, минуя стек ввода-вывода ядра Linux. Этот процесс называется «просветлением» (элементом паравиртуализции).
Dell EMC показала значительные улучшения производительности при использовании «просветления», как описано в публикации www.emc.com/ about/news/press/2019/20190402-01.htm. По словам Dell, «благодаря новой постоянной памяти Intel® Optane™ DC клиенты могут ускорить работу баз данных в оперативной памяти, виртуализации и анализа данных, увеличив объем памяти в 2,5 раза для отдельных серверов PowerEdge. Используя постоянную память Intel® Optane™ DC, PowerEdge R740xd обеспечивает увеличение объема обрабатываемых транзакций до 2,7 раза, по сравнению с числом транзакций в секунду, обеспечиваемым дисками NVMe в виртуализированной среде предварительного просмотра Microsoft SQL Server 2019, работающим на VMware ESXi.1».
Вы можете прочитать все подробности о том, как включить устройство постоянной памяти в Linux для SQL Server, по ссылке.
Конфликт вставки на последней странице
Давайте рассмотрим распространенную проблему пользователей SQL Server, которая оставалась нерешенной в течение очень долгого времени. Вы хотите построить таблицу с первичным ключом, который будет использоваться в кластеризованном индексе. И для этого первичного ключа используются последовательные значения. Другими словами, каждая вставка строки приводит к добавлению нового значения ключа в порядке увеличения. Наиболее распространенным примером такого ключа является столбец, использующий объект SEQUENCE
или свойство IDENTITY
.
Хотя этот подход в большинстве случаев работает нормально, все же здесь скрывается сложная проблема, связанная с производительностью приложений. Каждый раз, когда запрос должен изменить страницу, SQL Server должен физически защищать другие запросы от одновременного изменения или чтения структуры страницы (даже при наличии такой возможности, как блокировка на уровне строк), используя кратковременную блокировку страницы.
Если бы все пользователи пытались изменить одну и ту же страницу, производительность вашего приложения ухудшилась бы из-за конкуренции за блокировку страницы. Если вы строите кластеризованный индекс по последовательному ключу, данные сортируются по этому ключу. Каждая вставка будет пытаться вставить новую строку на последней странице листового уровня кластеризованного индекса. И если многие пользователи одновременно выполняют вставки, все они могут в конечном итоге попытаться изменить последнюю страницу индекса, отсюда и термин «конфликт вставки на последней странице».
Хотя описанная ситуация и не идеальна, она, как правило, не вызывает больших проблем, пока не произойдет явление, называемое «колонной» кратковременных блокировок. Пэм Лахуд (Pam Lahoud), старший руководитель программы в команде, работающей над SQL Server (известная также как @SQLGoddess), показала мне следующий ресурс, посвященный проблеме с «колонной». В контексте проблемы конкуренции за вставку на последней странице для SQL Server разделение страницы является примером сценария, в котором может сформироваться «колонна». Разделение страницы может произойти, если на странице недостаточно строк для нового INSERT
и в кластеризованном индексе должна быть создана новая страница. Пэм также провела отличную аналогию с проблемой «колонны». По словам Пэм, «пробки - обычная аналогия, используемая при описании этой проблемы. Если у вас есть дорога, по которой движется транспортный поток, равный максимальной пропускной способности этой дороги, то пока весь поток продолжает двигаться с одинаковой скоростью, движение будет постоянным, хотя и немного замеделенным. Как только на дороге происходит что-то, приводящее к тому, что водители нажимают на тормоза, например впереди оказывается водитель, едущий слишком медленно, возникает опасность на дороге или автомобили приближаются к сложной развязке, движение затрудняется. Если автомобили продолжают подъезжать к месту затора движения с той же скоростью, что и раньше, ситуация на дороге становится все хуже и хуже. Водители все еще продвигаются вперед, но очень медленно. На этом этапе пропускная способность не восстановится до тех пор, пока число автомобилей, въезжающих на дорогу, резко не уменьшится и не станет намного ниже, чем то значение транспортного потока, которое соответствует пропускной способности дороги».
Многие пользователи в сообществе SQL, технической поддержке и разработке решали эту проблему различными способами на протяжении многих лет. О многих из них упоминается в статье технической поддержки, размещенной на странице. А как насчет решения, реализованного внутри самого ядра СУБД и которое не потребует внесения изменений в приложение? Когда я увидел, что наше решение появилось в версии SQL Server 2019 CTP 3.1, я знал, что эта проблема уже обсуждалась нашей командой инженеров, рассмотревших множество возможных решений. Я расспросил Вонсок Ким (Wonseok Kim), ведущего разработчика этой функции, об истории ее реализации. Он показал мне ветку сообщений электронной почты, которая на самом деле имелась в моей почтовой папке, но я забыл о ней. Оказывается, в переписке, относящейся к работе над этим подходом, часто встречалось знакомое имя - Слава Окс (Slava Oks). Слава работал над этой идеей вместе со многими другими выдающимися программистами из команды разработчиков SQL Server.
Решение теперь существует в виде нового параметра для создания индексов - OPTIMIZE_FOR_SEQUENTIAL_KEY. Добавив этот параметр в ограничение индекса или первичного ключа, вы сообщаете SQL Server, что нужно использовать новый код, чтобы попытаться избежать проблем с «колонной» . Этот параметр не устраняет кратковременные блокировки страниц и не предотвращает проблему конфликта кратковременных блокировок. Что он делает, так это старается избежать серьезных проблем в ситуации, когда образуется «колонна», чтобы выровнять вашу рабочую нагрузку на сервер, сделать ее постоянной.
Подробнее об этой возможности и о том, как ее использовать, вы можете прочитать в документации по ссылке.
Примечание. Если вы используете этот параметр, то можете заметить появившийся новый тип ожиданий (
wait_type
) с названиемBTREE_INSERT_FLOW_CONTROL
. Он является частью механизма, позволяющего избежать или уменьшить проблему с «колонной».
Однако этот вариант не для всех. Если вы не используете последовательные значения ключа для кластеризованного индекса или не видите серьезных конфликтов, я бы не рекомендовал включать этот параметр. Фактически вы можете сильно ухудшить производительность вашей системы, слепо применяя его в любом кластеризованном индексе.
Если вы хотите попробовать это сами, убедитесь, что у вас есть достаточно «широкая» таблица. Когда я тестировал эту возможность, в моем случае простое создание таблицы с одним столбцом IDENTITY
не привело к существенному увеличению производительности. Что вам нужно сделать - так это вызвать условия, при которых происходит достаточное количество разбиений страниц, чтобы заметить проблему с «колонной».
Примечание. Возможно, что методы, описанные в статье, помогут вам обеспечить более высокую производительность, но этот новый параметр для индекса может дать вам необходимую стабильную производительность, и при этом вам придется гораздо меньше вмешиваться в работу вашего приложения.
Выводы
Эта серия статей была очень длинной и показала невероятные возможности интеллектуальной настройки производительности, встроенные в SQL Server 2019, призванные помочь вам повысить производительность без изменения уже работающих приложений. Я привел в ней много подробных примеров, чтобы вы могли сами убедиться в богатых возможностях новых функций, а также посмотреть, как они могут помочь повысить производительность и сэкономить ваше время, затрачиваемое на настройку производительности как при развертывании SQL Server 2019 в вашей организации, так и в разработке пользовательских приложений.