Как я могу выбрать некоторое количество строк, например, «получить как можно больше строк за 5 секунд»? - PullRequest
7 голосов
/ 11 декабря 2011

Цель: получить наибольшее количество строк и не получить больше строк, чем загруженных строк, через 5 секунд. Целью является не создание тайм-аута.

спустя месяцы я подумал, что, возможно, это сработает, а это не так:

declare @d1 datetime2(7); set @d1=getdate();
select c1,c2 from t1 where (datediff(ss,@d1,getdate())<5)

Ответы [ 5 ]

27 голосов
/ 16 декабря 2011

Хотя в последние годы тенденция к реляционным базам данных все больше смещается в сторону оптимизации запросов на основе затрат, СУБД не существует. Мне известно, что она по своей сути поддерживает определение максимальной стоимости (во времени или в / в) для запроса .

Идея "просто дать ему время и использовать записи, собранные до сих пор" является ошибочным решением. Недостаток заключается в том, что сложный запрос может тратить первые 5 секунд на выполнение хэша на поддереве плана запроса, чтобы сгенерировать данные, которые будут использоваться более поздней частью плана. Так что через 5 секунд у вас все еще может не быть записей.

Чтобы получить максимально возможное количество записей за 5 секунд, вам понадобится запрос с известным расчетным планом выполнения, который затем можно будет использовать для оценки оптимального количества записей, запрашиваемых для создания запрос выполняется как можно ближе к 5 секундам. Другими словами, зная, что оптимизатор запросов оценивает, что он может обрабатывать 875 записей в секунду, вы можете запросить 4 375 записей. Запрос может выполняться немного дольше, чем 5 секунд, но со временем ваше среднее выполнение должно упасть почти на 5 секунд.

Итак ... как это сделать?

В вашей конкретной ситуации это невозможно. Улов - «известный примерный план выполнения». Чтобы это работало надежно, вам понадобится хранимая процедура с известным планом выполнения, а не специальный запрос. Поскольку вы не можете создавать хранимые процедуры в своей среде, это не является началом. Тем не менее, для тех, кто хочет исследовать это решение, вот академическая статья группы, которая внедрила эту концепцию в Oracle. Я не читал полный текст статьи, но, основываясь на резюме, это звучит как их работа может быть переведен в любую СУБД с оптимизацией на основе затрат (например, MS SQL, MySQL и т. д.)

ОК, так что вы можете сделать в вашей ситуации?

Если вы не можете сделать это «правильным» способом, решите это с помощью взлома.

Мое предложение: ведите собственную статистику "оценочной стоимости".

Сделайте некоторое тестирование заранее и оцените, сколько строк вы обычно можете вернуть за 4 секунды. Допустим, это число 18 000.

Таким образом, вы ограничиваете свой запрос до 18 000 строк. Но вы также отслеживаете время выполнения при каждом запуске и сохраняете скользящее среднее, скажем, за последние 50 выполнений. Если это среднее значение меньше 4,5 с, добавьте 1% к размеру запроса и сбросьте скользящее среднее. Итак, теперь ваше приложение каждый раз запрашивает 18 180 строк. После 50 итераций, если скользящее среднее меньше 4,5 с, снова добавьте 1%.

И если ваша скользящая средняя превышает 4,75 с, вычтите 1%.

Со временем этот метод должен сходиться к оптимизированному N-рядному решению для вашего конкретного запроса / среды / и т. Д. И должен корректироваться (медленно, но неуклонно) при изменении условий (например, высокий параллелизм или низкий параллелизм)

Только один - поцарапайте это, два - больше вещей ...

  1. Как администратор базы данных, я должен сказать ... что любой запрос должен быть чрезвычайно редким, чтобы занимать более 5 секунд. В частности, если этот запрос часто выполняется и используется внешним приложением, то он не должен выполняться в течение 5 секунд. Если у вас действительно есть запрос пользователя, который не может быть выполнен за 5 секунд, это признак того, что дизайн базы данных нуждается в улучшении.

  2. Закон Джонатана В.М. «Отчет о зеленой полосе» Раньше я работал в компании, которая все еще использовала приложение для мэйнфреймов, которое ежедневно выплевывало множество отчетов с точечной матрицей, напечатанных точечной матрицей. Большинство из них были проигнорированы, а из немногих, которые использовались, большинство никогда не читалось за пределами первой страницы. В отчете могут быть отсортированы тысячи строк по убыванию возраста учетной записи ... и все, что нужно пользователю, - это просмотреть 10 самых старших. Мой закон таков: Количество вариантов использования, которые на самом деле требуют просмотра огромного количества строк, бесконечно мало. Подумайте - действительно подумайте - о сценарии использования для вашего запроса, а также о наличии большого или большого количества записей действительно то, что нужно этому пользователю.

5 голосов
/ 11 декабря 2011

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

Лично я бы не стал пытаться решить именно эту проблему. Вместо этого я бы провел некоторое тестирование, и методом проб и ошибок определил количество записей, которые, я уверен, будут загружены менее чем за пять секунд. Затем я бы просто поместил LIMIT на запрос загрузки.

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

Наконец, учтите, что на большинстве современных аппаратных средств для большинства запросов вы можете вернуть очень большое количество записей в течение пяти секунд. Трудно представить, чтобы все эти данные возвращались в пользовательский интерфейс и все же можно было их использовать, если вы этого хотите.

1009 * Джейсон *

0 голосов
/ 16 декабря 2011

Чтобы получить данные из таблицы, вам нужно сделать две вещи:

  1. выполнить запрос (ВЫБРАТЬ что-нибудь из таблицы)
  2. заполните таблицу или прочитайте данные

Вы спрашиваете о втором. Я не очень знаком с php, но я думаю, что это не имеет значения. Мы используем выборку, чтобы быстро получить первые записи и показать их пользователю, а затем извлекать записи по мере необходимости. В ADO.NET вы можете использовать IDataReader для получения записей одна за другой, в php я думаю, что вы можете использовать аналогичные методы, например - mysqli_fetch_row в расширении mysqli или mysql_fetch_row в расширении mysql. В этом случае вы можете прекратить чтение данных в любой момент.

0 голосов
/ 16 декабря 2011

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

У вас есть интервал 10 с, вы пробуете один запрос, он возвращает вам строку за 0,1 с. Это будет означать, что вы можете получить не менее 99 аналогичных запросов в оставшихся 9.9. Однако одновременное получение 99 запросов должно быть быстрее, чем получать их один за другим (что и предполагает ваш первоначальный расчет). Таким образом, вы получаете 99 запросов и проверяете время снова.

Скажем, операция выполнялась в 1,5 раза быстрее, чем один запрос, потому что одновременное получение большего количества запросов более эффективно, в результате чего вы получаете 100 строк за 7,5 с. Вы подсчитываете, что в среднем вы уже получили 100 строк за 7,5 с, вычисляете новое количество возможных запросов за оставшееся время и снова делаете запрос, и так далее. Однако вам необходимо установить пороговое ограничение для этого цикла, скажем, что-то вроде: Больше не получать новых запросов после 9.9 с.

Это решение, очевидно, не является ни самым плавным, ни чем-то, что я действительно использовал бы, но, возможно, оно служит для решения проблемы ОП. Кроме того, jmacinnes уже указывал: «Возможно, что самая первая итерация в цикле может занять более 10 [5] секунд».

Мне, конечно, было бы интересно, если бы кто-то смог найти правильное решение этой проблемы.

0 голосов
/ 16 декабря 2011

Я никогда не пробовал этого, но если скрипт выполняет этот запрос, вы можете попробовать выполнить небуферизованный запрос (в php это будет что-то вроде mysql_unbuffered_query ()) ... затем вы можете сохранить их в массиве, пока запрос выполняется. Затем вы можете установить тайм-аут запроса MySQL на пять минут. Когда запрос завершен, если вы установили цикл while () для проверки ответа на тайм-аут, он может завершить цикл, и у вас будет массив со всеми записями, возвращенными через 5 минут. Опять же, я не уверен, что это сработает, но мне было бы интересно посмотреть, достигнет ли это того, чего вы хотите.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...