Связать Известны Соответствия Курсор Объяснение


Привет у меня возникли небольшие проблемы, пытаясь найти простое объяснение для bind aware cursor matching в oracle.. Это bind aware cursor matching, в основном имеющий Oracle monitor запрос с переменной bind в течение долгого времени и видящий, есть ли увеличение CPU при использовании некоторых переменных. Затем из этого он почти генерирует более подходящий план выполнения скажем полное сканирование таблицы затем помечает запрос как bind aware затем в следующий раз запрос выполняется есть выбор из двух вариантов выполнения планы? Любая помощь будет очень признательна! Ура!

1 2

1 ответ:

В простейшем случае представьте, что у вас есть таблица ORDERS. В этой таблице есть столбец status. Есть только несколько значений status, и некоторые из них очень, очень популярны, в то время как другие очень редки. Представьте, что таблица содержит 10 миллионов строк. Для наших целей скажем, что 93% являются" завершенными", 5% - "отмененными", а оставшиеся 2% распределены между 8 различными статусами, которые отслеживают поток заказов (неполный, полный, в исполнении, в пути и т. д.).

Если у вас есть больше всего основная статистика в вашей таблице оптимизатор знает, что существует 10 миллионов строк и 10 различных статусов. Он не знает, что некоторые значения status более популярны, чем другие, поэтому он предполагает, что каждый статус соответствует 1 миллиону строк. Поэтому, когда он видит запрос типа

SELECT *
  FROM orders
 WHERE status = :1

Он предполагает, что ему нужно извлечь 1 миллион строк из таблицы независимо от значения переменной bind, поэтому он решает использовать полное сканирование таблицы.

Теперь человек приходит и спрашивает, почему Оракул быть глупым и делать полное сканирование таблицы, когда он просит горсть orders , которые находятся в транзитном состоянии-очевидно, сканирование индекса было бы предпочтительнее там. Этот человек понимает, что оптимизатору нужно больше информации, чтобы узнать, что некоторые значения status более популярны, чем другие, так что человек решает собрать гистограмму (есть варианты, которые заставляют Oracle собирать гистограммы на определенных столбцах автоматически, но я игнорирую эти варианты, чтобы попытаться сохранить историю простой).

Как только гистограмма собрана, оптимизатор знает, что значение status сильно искажено-есть много завершенных заказов, но очень мало транзитных заказов. Если он видит запрос, который использует литералы, а не переменные привязки, то есть
SELECT *
  FROM orders
 WHERE status = 'IN TRANSIT'

Vs

SELECT *
  FROM orders
 WHERE status = 'COMPLETED'

Тогда оптимизатору очень легко решить использовать индекс в первом случае и сканирование таблицы во втором. Однако, когда у вас есть переменная привязки, задача оптимизатора сложнее - как предполагается, что он определяет, следует ли использовать индекс или выполнить сканирование таблицы...

Первое решение Oracle было известно как"bind variable peeking". При таком подходе, когда оптимизатор видит что-то вроде
SELECT *
  FROM orders
 WHERE status = :1
Если он знает (из-за гистограммы на status), что план запроса должен зависеть от значения, переданного для переменной bind, Oracle "заглядывает" в первое значение, которое передается, чтобы определить, как оптимизировать оператор. Если первое значение переменной привязки- ' IN Транзит", будет использоваться сканирование индекса. Если первое значение переменной привязки - "COMPLETE", то будет использоваться проверка таблицы.

Для многих случаев это работает довольно хорошо. Многие запросы действительно имеют смысл только для очень популярных или очень редких значений. В нашем примере довольно маловероятно, что кто-то действительно захочет получить список из всех 9 миллионов полных заказов, но кто-то может захотеть получить список из пары тысяч заказов в одном из различных переходных состояний.

Но привязать переменный подглядывание не работает хорошо в других случаях. Если у вас есть система, в которой приложение иногда связывает очень популярные значения, а иногда связывает очень редкие значения, вы в конечном итоге оказываетесь в ситуации, когда производительность приложения сильно зависит от того, кто первым выполнит запрос. Если первый пользователь, выполняющий запрос, использует очень редкое значение, то будет создан и кэширован план сканирования индекса. Если второе лицо, выполняющее запрос, использует очень распространенное значение, будет использован кэшированный план, и вы получите сканирование индекса, которое займет целую вечность. Если роли поменялись местами, второй пользователь использует редкое значение, получает кэшированный план, который выполняет полное сканирование таблицы, и должен просканировать всю таблицу, чтобы получить пару сотен строк, которые его интересуют. Такого рода недетерминированное поведение, как правило, сводит с ума СУБД и разработчиков, потому что это может быть безумно трудно диагностировать и может привести к довольно странным объяснениям - у Тома Кайта есть отличный пример клиента, который пришел к выводу, что им нужна перезагрузка. база данных днем, если в понедельник утром шел дождь .

Привязать известны соответствующие курсора решение проблемы связывания переменной выглядывал. Теперь, когда Oracle видит запрос

SELECT *
  FROM orders
 WHERE status = :1

И видит, что есть гистограмма на status, которая указывает, что некоторые значения являются более распространенными, чем другие, он достаточно умен, чтобы сделать этот курсор "привязкой". Это означает, что когда вы привязываете значение in FULFILLMENT, оптимизатор достаточно умен, чтобы сделать вывод, что это один из редкие значения и дать вам план индекса. Когда вы привязываете значение COMPLETE, оптимизатор достаточно умен, чтобы сделать вывод, что это одно из общих значений, и дать вам план с помощью сканирования таблицы. Таким образом, оптимизатор теперь знает о двух различных планах для одного и того же запроса, и когда он видит новое значение привязки, как в транзите, он проверяет, похоже ли это значение на другие, которые он видел раньше, и либо дает вам один из существующих планов, либо создает другой новый план. В данном случае, это решил бы, что в транзите примерно так же часто, как и в выполнении, поэтому он повторно использует план с индексным сканированием, а не генерирует третий план запроса. Это, надеюсь, приведет к тому, что каждый получит свой предпочтительный план без необходимости генерировать и кэшировать планы запросов каждый раз, когда изменяется значение переменной привязки.

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