Тройное внутреннее соединение с более чем 10.000 строками и приложением для вычислений asp - PullRequest
1 голос
/ 20 января 2012

Мое приложение ASP Classic получает более 10.000 строк через тройное внутреннее соединение.После этого он проходит по каждой строке, вычисляет некоторые вещи и помещает данные в многомерный массив с 17 столбцами.

Все это выбирается через jQuery AJAX при нажатии кнопки поиска.* Логически это занимает некоторое время, 5+ минут на самом деле.

Все работает отлично.НО ... При этом вся система зависает не только для пользователя, выполняющего вычисления, но и для всех других пользователей, использующих систему другими способами. Как я могу оптимизировать это?!

Небольшой соответствующий фрагмент кода:

dim userArray()

sql = "select first.*, second.fullname, second.info, third.inputs from first inner join second on first.userid = second.id inner join third on first.info = third.id where convert(varchar, first.period, 112) between '20020115' and '20120115' order by second.fullname, first.userid"
set rs = conn.execute(sql)

if rs.eof then
  response.write("Nothing were found in the period")
else
  counter = 0
  redim userArray(17, 1000)
  do until rs.eof
    response.flush
    counter = counter + 1

  ' A LOT of calculations and putting in array...

    rs.movenext
  loop

  for i = 1 to counter
    ' Doing all the response.writes to the user...
  next
end if

Ответы [ 3 ]

1 голос
/ 20 января 2012

Давайте проанализируем это, учитывая, что в SQL есть предложение ORDER BY: -

do until rs.eof 
  response.flush 
  counter = counter + 1 

  ' A LOT of calculations and putting in array... 

  rs.movenext 
loop

Обратите внимание на Response.Flush, первое, что я хотел бы сделать, это избавиться от этого.Возможно, вам потребуется увеличить лимит буферизации ответов ASP (в диспетчере IIS).Flush отправляет сгенерированный контент клиенту и ждет, пока клиент подтвердит получение всех пакетов, отправленных до его завершения.Именно здесь я предполагаю, что 90% из 5+ минут тратятся.

Теперь "МНОГО расчетов".VBScript не известен своей производительностью.Этот код может занять некоторое время.В некоторых случаях некоторые вычисления могут быть выполнены намного лучше с помощью SQL, чем в сценарии, так что это один из вариантов.Другим может быть создание некоторого компонента, скомпилированного COM, для выполнения сложной работы (хотя некоторые учетные записи должны быть сделаны для сортировки, которая может уничтожить преимущества).Однако это может быть неизбежно, что вам нужно сделать эти вызовы в VBScript.

Теперь rs.movenext.Этот цикл означает, что вы держите соединение и набор строк открытыми в течение почти всего времени, необходимого для обработки.Это происходит, когда сервер отправляет байты по сети клиенту, а VBScript обрабатывает числа.Гораздо лучшим подходом было бы быстро высосать весь набор строк и отключиться от БД, , затем хруст числа и наконец сбросить буфер на клиент.отключенный набор записей (вы указываете статический курсор на стороне клиента) или даже простой GetRows метод объекта набора записей, который сбрасывает весь набор строк в 2-мерный массив.Это будет означать, что вы будете поддерживать блокировки различных таблиц в течение как можно меньшего времени.

1 голос
/ 20 января 2012

Я бы реорганизовал этот код следующим образом:

sql = "select first.*, second.fullname, second.info, third.inputs from first inner join second on first.userid = second.id inner join third on first.info = third.id where convert(varchar, first.period, 112) between '20020115' and '20120115' order by second.fullname, first.userid"
Set rs = conn.Execute(sql)
If NOT rs.EOF Then
    aRecords = rs.GetRows() ' retrieve your records and assign them to an array '
End If
rs.Close ' record set is now released, so it shouldn't lock up your database anymore

If IsArray(aRecords) Then ' just a small sanity check '
    iCount = UBound(aRecords, 2)
    For iCounter = 0 To iCount
        ' Your calculations and your Response.Writes '
    Next
    Erase aRecords
Else
    ' no result found '
End If


Обновление

Вы можете назначать записи переменнымв вашем For цикле, например

id = aRecords(0, iCounter)

Тогда вам нужно обращаться к id только тогда, когда вам это нужно.Хотя вы правы в том, что если то, что вы выбираете, является динамическим (то есть позиции столбца могут смещаться), то этот подход может создать проблемы, когда вы пытаетесь назначить записи переменным дальше по линии.Например, чтобы назначить fullname из таблицы second, вам нужно знать, сколько столбцов извлекается из first.*.

1 голос
/ 20 января 2012

Я вижу, что вы уже используете response.flush () для прерывистой очистки данных в браузере во время процесса, но если вы используете AJAX, вызов должен сначала завершиться до вызова функции обратного вызова AJAX, поэтому я думаю, что response. флеш там не будет никакой пользы. Вы можете попробовать вызвать URL-адрес AJAX напрямую и вставить в ответ response.write (), чтобы увидеть, что происходит (и какова скорость)

Чтобы получить еще больше информации, вы можете добавить таймер перед запросом, после запроса и внутри цикла и response.write время, прошедшее с момента запуска сценария. Это даст вам очень хорошее представление о том, где происходит задержка: http://www.codefixer.com/codesnippets/vbscript_timer_function.asp

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

Попробуйте добавить WITH NOLOCK после каждого имени таблицы в вашем запросе, чтобы выбрать без блокировки базы данных для записи. Это может дать вам некоторые данные, которые были перезаписаны во время выполнения вашего запроса, но обычно это не проблема.

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

НТН,

Эрик

...