Различные размеры параметров приводят к неэффективному кэшу плана запросов - PullRequest
6 голосов
/ 18 марта 2012

Профилировщик Nhibernate показывает множество сообщений об ошибках относительно плана запроса:

Различные размеры параметров приводят к неэффективному использованию кэша плана запроса

Это также приводит к объяснениюв http://nhprof.com/Learn/Alerts/UncachedQueryPlan и предупреждает вас об использовании параметра prepare_sql = true при построении сеанса.Я делаю это так свободно:

.ExposeConfiguration(configuration => configuration
    .SetProperty("current_session_context_class", "thread_static")
    .SetProperty("prepare_sql", "true")
    .SetProperty("generate_statistics", "true")
    )

Но похоже, что это не работает, так как сообщения об ошибках все еще там.Это ограничение для OracleClientConfiguration или я делаю это неправильно?

Редактировать Чтобы предоставить дополнительную информацию об этом ...

В моем хранилище я делаю это

session.Query<TEntity>.Where(predicate).ToList();

и это вызов

var value = ParameterRepository.First(p => (p.Pipeline.Id == pipelineId && p.Name == name));

Например, это два SQL, сгенерированных из этого вызова, и что профилировщик nhibernate показывает как «Размеры параметров DIfferent приводят к неэффективному использованию кэша плана запроса»

select GUID1_12_,
       PARAMETER2_12_,
       PARAMETER3_12_,
       GUID4_12_
from   (select pipelineex0_.GUID_PIPELINE_EXEC_PARAMETER as GUID1_12_,
               pipelineex0_.PARAMETER_NAME               as PARAMETER2_12_,
               pipelineex0_.PARAMETER_VALUE              as PARAMETER3_12_,
               pipelineex0_.GUID_PIPELINE_TRACKING       as GUID4_12_
        from   FCT_PIPELINE_EXEC_PARAMETER pipelineex0_
        where  pipelineex0_.GUID_PIPELINE_TRACKING = 'A5916E73CF1E406DA26F65C24BFBF694' /* :p0 */
               and pipelineex0_.PARAMETER_NAME = 'lid' /* :p1 */)
where  rownum <= 1 /* :p2 */

и секунда

select GUID1_12_,
       PARAMETER2_12_,
       PARAMETER3_12_,
       GUID4_12_
from   (select pipelineex0_.GUID_PIPELINE_EXEC_PARAMETER as GUID1_12_,
               pipelineex0_.PARAMETER_NAME               as PARAMETER2_12_,
               pipelineex0_.PARAMETER_VALUE              as PARAMETER3_12_,
               pipelineex0_.GUID_PIPELINE_TRACKING       as GUID4_12_
        from   FCT_PIPELINE_EXEC_PARAMETER pipelineex0_
        where  pipelineex0_.GUID_PIPELINE_TRACKING = 'A5916E73CF1E406DA26F65C24BFBF694' /* :p0 */
               and pipelineex0_.PARAMETER_NAME = 'period' /* :p1 */)
where  rownum <= 1 /* :p2 */

ИМХО это PARAMETER_NAME с 'крышкой' и 'периодом', который генерирует разные планы запросов.

заранее спасибо

Ответы [ 2 ]

0 голосов
/ 29 января 2014

Я протестировал это с переопределенным OracleClientDriver (используя старый драйвер Microsoft Oracle, а не ODP.NET), аналогично коду в ответе от mattk, и я не увидел никаких отличий в выполнении Oracle, хотя строковые параметрытеперь имеет общий размер.

Вот мой пост на Stackexchange DBA.

Oracle Enterprise Manager не показывал повторяющихся запросов для моего сгенерированного NHibernate SQL, и в обеих версиях каждаявызов вызвал синтаксический анализ (до 1000 для длительного тестирования), но очень мало жестких анализов, без различий между переменной и фиксированной длиной параметра.

Фактически, Oracle создавал повторяющиеся планы запросов только для запросов без параметров привязки, но со значениями, объединенными в строку SQL (чего следует избегать в кодированном SQL).Так что теперь мне кажется, что размер параметра не имеет значения для Oracle, когда речь идет о повторном использовании планов запросов (или, с точки зрения Oracle, совместное использование курсоров).

Возможно, Oracle сравнивает только строку SQL для сопоставления плана, а SQL Server также проверяет определения параметров.Вы также можете увидеть разницу, глядя на динамические команды SQL EXECUTE IMMEDIATE (Oracle) и sp_executesql (SQL Server): sp_executesql также получает строка с определениями параметров (в строке, а не в качестве параметров для самого вызова sp_executesql !).Я знаю, что NHibernate / ADO.NET использует sp_executesql при отправке параметризованных запросов в SQL Server, поэтому он, вероятно, имеет другую обработку в SQL Server.Кроме того, при подключении к SQL Server через NHibernate все строковые параметры имеют уникальные размеры (из сопоставления NHibernate или максимальной длины по умолчанию), поэтому проблема, вероятно, была устранена в соответствующих случаях.Поправьте меня, если я ошибаюсь!

Использование Prepare / prepare_sql в ADO.NET/NHibernate имеет ряд недостатков: в зависимости от реализации, перед выполнением любого SQL-запроса в базу данных должен быть отправлен запрос Prepare,Приложение должно хранить дескриптор подготовленного оператора и может использоваться только для одного соединения.Значение: новые ручки должны создаваться часто.Когда я тестировал с Oracle и ODP.NET, он был несколько медленнее, чем не подготовленная версия, хотя запросы по самому дескриптору (немного) более производительны, чем по параметризованному SQL, которые сопоставляются в базе данных по строковому равенству.Вероятно, Prepare подходит, если приложение много раз использует один и тот же запрос в рамках одного и того же соединения с БД или сеанса NHibernate.

0 голосов
/ 26 сентября 2012

Чтобы генерировать один и тот же план каждый раз, когда параметр должен быть установлен на одинаковую длину, независимо от значения параметра.

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

public class CustomOracleClientDriver : OracleClientDriver
{
    protected override void InitializeParameter(IDbDataParameter dbParam, string name, SqlType sqlType)
    {
        base.InitializeParameter(dbParam, name, sqlType);

        if (sqlType.LengthDefined)
            dbParam.Size = sqlType.Length;
    }
}

(ПРИМЕЧАНИЕ. Унаследовано от OracleDataClientDriver, если вы используете ODP.Net)

Если вы используете Fluent NHibernate, вы регистрируете реализацию своего драйвера следующим образом:

Fluently.Configure()
                .Database(
                    OracleDataClientConfiguration.Oracle10
                        .ConnectionString(c => c.FromAppSetting("ConnectionString"))
                        .Driver<CustomOracleClientDriver>())
...