4

У меня есть следующий запрос, который выполняется в 16 мс - 30 мс.Медленный запрос с поиском cfqueryparam на индексированном столбце, содержащем хеши

<cfquery name="local.test1" datasource="imagecdn"> 
    SELECT hash FROM jobs WHERE hash in(
     'EBDA95630915EB80709C69089315399B', 
     '3617B8E6CF0C62ECBD3C48DDF8585466', 
     'D519A38F09FDA868A2FEF1C55C9FEE76', 
     '135F94C3774F7719CFF8FF3A275D2D05', 
     'D58FAE69C559273D8427673A08193789', 
     '2BD7276F209768F2FCA6635659D7922A', 
     'B1E3CFBFCCFF6F5B48A849A050E6D424', 
     '2288F5B8A797F5302E8CA24323617236', 
     '8951883E36B5D38A4643DFAA0396BF13', 
     '839210BD564E30BE1355D1A6D4EF7081', 
     'ED4A2CB0C28B608C29576819CF7BE19B', 
     'CB26925A4874945B810707D5FF0B91F2', 
     '33B2FC229F0CC797A02AD163CDBA0875', 
     '624986E7547DBAC0F47B3005CFDE0A16', 
     '6F692C289BD805CEE41EF59F83F16F4D', 
     '8551F0033C617BD9EADAAD6CEC4B3E9E', 
     '94C3C0A74C2DE085FF9F1BBF928821A4', 
     '28DC1A9D2A69C2EDF5E6C0E6368A0B3C' 
    ) 
</cfquery> 

Если я выполняю тот же запрос, но использую cfqueryparam, он работает в 500 мс - 2000 мс.

<cfset local.hashes = "[list of the same ids as above]"> 
<cfquery name="local.test2" datasource="imagecdn"> 
    SELECT hash FROM jobs WHERE hash in(
     <cfqueryparam cfsqltype="cf_sql_varchar" value="#local.hashes#" list="yes"> 
    ) 
</cfquery> 

В таблице имеется примерно 60 000 строк. Столбец «хэш» - это varchar (50) и имеет уникальный некластеризованный индекс, но не является первичным ключом. Сервер БД - MSSQL 2008. На веб-сервере установлена ​​последняя версия CF9.

Любая идея, почему cfqueryparam заставляет производительность бомбить? Он ведет себя так каждый раз, независимо от того, сколько раз я обновляю страницу. Если я скрою список всего лишь на 2 или 3 хэша, он все равно будет работать примерно на 150-200 мс. Когда я удаляю cfqueryparam, производительность будет такой, как ожидалось. В этой ситуации есть возможность для SQL-инъекций, и, следовательно, использование cfqueryparam, безусловно, будет предпочтительным, но не должно принимать 100 мс, чтобы найти 2 записи из индексированного столбца.

редактирует:

  1. Мы используем хэши, сгенерированные hash() не UUIDs или Guids. Хэш генерируется hash(SerializeJSON({ struct })), который содержит план выполнения ряда операций, выполняемых на изображении. Цель этого заключается в том, что он позволяет нам знать перед вставкой и перед запросом точный уникальный идентификатор для этой структуры. Эти хэши действуют как «индекс» того, какие структуры уже хранятся в БД. В дополнение к хэшам такая же структура будет хешем с тем же результатом, что неверно для UUIDS и GUIDS.

  2. Запрос выполняется на 5 разных серверах CF9, и все они имеют одинаковое поведение. Для меня это исключает мысль о том, что CF9 кэширует что-то. Все серверы подключаются к одному и тому же БД, поэтому, если кэширование происходит, это должен быть уровень БД.

+3

Вы пытались использовать только cf_sql_char вместо varchar? Это может заставить MSSQL более внимательно рассмотреть массив и дать вам лучший план выполнения. Очевидно, что план, который он выдает из кеша, не так эффективен, как тот, который он компилирует в режиме реального времени. Также попробуйте добавить подсказку индекса. Запустите анализатор трассировки на нем, если вы можете изолировать его в dev - может дать вам некоторые подсказки в плане выполнения. это все, что у меня есть :) –

+1

Возможно, это потому, что он не может повторно использовать инструкцию подготовки для списков, поэтому он перекомпилит каждый раз ... Если вы уверены, что используете свою собственную логику для предотвращения внедрения sql, возможно, это целесообразно пропустить cfqueryparam для этого. – Henry

+0

Вот несколько способов улучшить производительность SELECT IN. Http://florianreischl.blogspot.ca/2012/03/performance-comparison-of-sql-server.html – Henry

ответ

8

Ваша проблема может быть связана с VARCHAR vs NVARCHAR.Эти 2 ссылки могут помочь Querying MS SQL Server G/UUIDs from ColdFusion и nvarchar vs. varchar in SQL Server, BEWARE

Что может быть происходит, есть установка в ColdFusion администратора, если cfqueryparam посылает VARCHARS как Юникод или нет. Если этот параметр не соответствует настройке столбца (в вашем случае, если этот параметр включен), MS SQL не будет использовать этот индекс.

+0

Знаете ли вы, где бы я изменил эту настройку или проверить, так ли это? На этом конкретном источнике данных у меня нет '- Включить High ASCII-символы и Unicode для источников данных, настроенных для проверки нелатинских символов. Нужно ли мне? Столбец - varchar. – Nucleon

+0

Похоже, он должен быть снят. Можете ли вы попробовать изменить настройку, чтобы увидеть, имеет ли это значение? – Yisroel

+0

BOOM! Вот это Идроэль, спасибо тебе большое. На моем локальном ящике dev у меня он был снят. Он был проверен на наших dev/live-серверах. Как только я снял флажок, производительность сразу же улучшилась. Если в будущем я столкнулся с этой же проблемой, но мне нужно было это проверить (потому что unicode был необходим), хотел бы я, чтобы мои столбцы были nvarchar таким образом, индекс также находится в Юникоде? – Nucleon

0

Как указывает Марк, в кэш, вероятно, попал плохой план выполнения. Одним из преимуществ cfqueryparam является то, что когда вы передаете разные значения, он может повторно использовать сохраненный в кэше план для этого утверждения. Вот почему, когда вы пытаетесь с меньшим списком, вы не видите улучшения. Если вы не используете cfqueryparam, SQL Server должен каждый раз разрабатывать план выполнения. Как правило, это плохо, если в кэше он не имеет оптимального плана. Попробуйте очистить кеш, как описано здесь http://www.devx.com/tips/Tip/14401. Это, надеюсь, будет означать, что при следующем запуске вашего утверждения с помощью cfqueryparam в нем будет храниться лучший план.

Имеют смысл?

+1

Я подозреваю, хотя я не уверен на 100% - там будет однако, это другой план для списков разного размера. CF не передает SQL как 'SELECT * FROM table WHERE col IN (: listParam)' (т. Е. Есть один параметр привязки), он передает его как 'SELECT * FROM table WHERE col IN (: each,: element, : отдельно) '. Таким образом, сервер увидит запрос для трехэлементного списка, отличный от одного с десятиэлементным списком, и я сильно подозреваю, что каждый будет скомпилирован отдельно. –

+1

Я полностью согласен с вами в том, что оператор IN не связан как один список. Но я не знаю, что это будет идентифицировать как новый подготовленный оператор и подсчитать новый план запроса. Если вы думаете об этом, количество элементов в инструкции IN действительно не должно влиять на то, использовать ли сканирование таблицы или индекс и т. Д. – baynezy

+0

Я не уверен, что это релевантно, но тот же запрос выполняется на 5 разных CF9. Каждый из них демонстрирует то же поведение, которое, я думаю, исключает любую форму кэширования запросов Coldfusion. Все ящики подключаются к одному и тому же БД, поэтому, если кеширование находится на уровне SQL, это может быть возможно. – Nucleon

0

Я не думаю, что cfqueryparam вызывает проблему. Как вы отметили большой поход в исполнение, это может быть индекс, который не будет использоваться для вашего запроса при попытке с помощью cfqueryparam. Я создал такой же сценарий на моем компьютере разработки, но у меня такое же время выполнения с и без cfqueryparam. Может быть некоторый накладной, используя список, поскольку в первом запросе вы передаете его непосредственно в качестве теста, а во втором холодном соединении необходимо создать из параметра запроса из предоставленного списка, но опять же этого не должно быть так много. Я предлагаю запустить «Провайдер SQL Server» и отслеживать запрос, выполняемый на сервере, это даст вам лучше, кто стоит еще 500 мс.

Смежные вопросы