Каковы плюсы и минусы выполнения вычислений в sql и в вашем приложении

shopkeeper таблица имеет следующие поля:

id (bigint),amount (numeric(19,2)),createddate (timestamp)

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

один из способов сделать это, чтобы выполнить вычисления в моем приложении java и выполнить простой запрос

Date previousDate ;//  calculate in application

Date todayDate;//  calculate in application

select amount where createddate between  and  

а затем цикл через записи и конвертировать сумму в центы в моем приложении java и генерировать доклад

другой способ - это выполнение вычислений в самом sql-запросе:

select cast(amount * 100 as int) as "Cents"
from shopkeeper  where createddate  between date_trunc('day', now()) - interval '1 day'  and  date_trunc('day', now())

а затем цикл через записи и генерировать отчет

в одном случае вся моя обработка выполняется в java-приложении, и запускается простой запрос. В другом случае все преобразования и вычисления выполняются в Sql query.

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

можете ли вы сказать мне, какой подход лучше с точки зрения производительности и других аспектов, и почему?

13 ответов


Это зависит от многих факторов, но самое главное:

  • сложность вычислений (предпочитаю делать сложный хруст на app-сервере, так как это масштабирует из; а не сервер БД, который масштабирует до)
  • объем данных (если вам нужно получить доступ/агрегировать много данных, делать это на сервере БД сохранит пропускную способность, и диск io, если агрегаты могут быть сделаны внутри индексов)
  • удобство (sql не самый лучший язык для сложной работы-особенно не большой для процедурной работы, но очень хороший для работы на основе набора; паршивая обработка ошибок, хотя)

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

Re ваше Примечание:

и затем цикл по записям

циклы through records почти всегда неправильно делать в sql-предпочтительнее писать операцию на основе набора.

как правило, Я предпочитаю свести работу базы данных к минимуму "хранить эти данные, извлекать эти данные" - однако всегда есть примеры сценариев, когда элегантный запрос на сервере может сэкономить много пропускной способности.

также рассмотрим: если это вычислительно дорого, Можно ли его кэшировать где-то?

Если вы хотите точный "что лучше"; кодируйте его в обоих направлениях и сравните его (отметив, что первый проект либо, вероятно, не настроен на 100%). Но фактор в типичном использовании к этому: если, в действительности, он вызывается 5 раз (отдельно) сразу, то имитируйте это: не сравнивайте только один "1 из этих против 1 из тех".


позвольте мне использовать метафору: если вы хотите купить золотая цепочканикогда корабль тонн золотой руды из Южной Африки во Францию для этого. Руда перерабатывается на месте добычи (или, по крайней мере, в общем районе), отгружается только золото. То же самое должно быть верно для приложений и баз данных.

насколько PostgreSQL вы может делать практически все на сервере, достаточно эффективно. СУРБД выделяется на сложные запросы. Для процедурных потребностей вы можете выбрать из множества языки сценариев на стороне сервера: tcl, python, perl и многое другое. В основном я использую PL / pgSQL, хотя.

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

второй в линия, Если вы отправляете каскад запросов, каждый в зависимости от предыдущего, в то время как все это может быть сделано в одном запросе или процедуре на сервере. (Это как доставка золота, и каждый из драгоценных камней с отдельным кораблем, последовательно.)

переход между приложением и сервером стоит дорого. Для сервера и клиент. Попробуйте сократить это, и вы выиграете-ergo: используйте серверные процедуры и / или сложный SQL, где необходимый.

мы только что закончили проект, где мы упаковали почти все сложные запросы в функции Postgres. Приложение передает параметры и получает необходимые наборы данных. Быстрый, чистый, простой (для разработчика приложений), ввод-вывод сведен к минимуму ... блестящее ожерелье с низким содержанием углерода.


в этом случае вы наверное немного лучше выполнять вычисления в SQL, поскольку компонент database engine, вероятно, будет иметь более эффективные десятичные арифметические процедуры, чем Java.

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

где это имеет значение, это:

  • агрегатные вычисления, такие как SUM (), AVG (), MIN (), MAX () здесь компонент database engine будет на порядок быстрее, чем реализация Java.
  • в любом месте расчет используется для фильтрации строк. Фильтрация в БД намного эффективнее, чем чтение строки, а затем ее отбрасывание.

нет черного / белого относительно того, какие части логики доступа к данным должны выполняться в SQL и какие части должны выполняться в вашем приложении. Мне нравится Марк Gravell-это формулировка, различие между

  • сложные вычисления
  • данные-вычисления

мощность и выразительность SQL сильно недооценивается. С момента введения окне функции, очень строго set-oriented вычисления можно выполнить очень легко и элегантно в базе данных.

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

  • держите количество данных, передаваемых между базой данных и приложением slim (в пользу расчета материала в БД)
  • держите количество данных, загруженных с диска базой данных slim (в пользу того, чтобы позволить базе данных оптимизировать операторы, чтобы избежать ненужный доступ к данным)
  • не подталкивайте базу данных к ее пределам процессора со сложными параллельными вычислениями (в пользу извлечения данных в память приложения и выполнения вычислений там)

по моему опыту, с приличным DBA и некоторыми приличными знаниями о вашей достойной базе данных вы не столкнетесь с ограничениями процессора DBs очень скоро.

некоторые дальнейшие чтения, где эти вещи объясняются:


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

в некоторых случаях это не касается, но когда это имеет смысл. также вообще коробка db имеет самое лучшее оборудование и представления.


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

давайте подумаем об этом в два шага: (1) OLTP (небольшое количество записей) транзакций. (2) OLAP (длительное сканирование многих записей).

в OLTP случай, если вы хотите быть быстрым (10k-100k транзакций в секунду), вы должны удалить защелку, блокировку и dead lock contention из базы данных. Это означает, что в транзакциях необходимо исключить длинные стойла: одним из таких длинных стойл являются поездки от клиента к БД для перемещения обработки к клиенту. У вас не может быть долгоживущих транзакций (чтобы сделать чтение/обновление атомарным) и иметь очень высокую пропускную способность.

Re: горизонтальное масштабирование. Современные базы данных масштабируются горизонтально. Эти системы реализуют HA и отказоустойчивость уже. Используйте это и попытайтесь упростить пространство приложения.

давайте посмотрим на OLAP -- в этом случае должно быть очевидно, что перетаскивание, возможно, terrabytes данных обратно в приложение-ужасная идея. Эти системы построены специфически для того чтобы работать весьма эффективно против обжатых, pre-организованных столбчатых данных. Современные системы OLAP также масштабируются по горизонтали и имеют сложные планировщики запросов, которые рассеивают работу по горизонтали (внутреннее перемещение обработка данных).


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

  1. Если вы можете достичь чего-то простого с помощью базы данных sql, то лучше пойти на это, поскольку db будет работать намного лучше и делать вычисления там, а затем с помощью результат выборки. Однако, если фактическое вычисление требует слишком много вычислений здесь и там, то вы можете пойти с кодом приложения. Почему? Поскольку сценарий похож на цикл, в большинстве случаев не лучше всего обрабатывается sql, где интерфейсные языки лучше разработаны для этих вещей.
  2. в случае, если подобный расчет требуется из многих мест, то, очевидно, размещение кода расчета в конце БД будет лучше держать вещи в том же месте.
  3. Если есть много вычислений, которые нужно сделать, чтобы достичь конечного результата через множество различных запросов, а затем также перейти к концу БД, поскольку вы можете поместить один и тот же код в хранимую процедуру, чтобы выполнить лучше, чем извлечение результатов из бэкэнда, а затем вычислить их на переднем конце.

есть много других аспектов, которые вы можете подумать, прежде чем решить, где разместить код. Одно восприятие совершенно неправильно - все можно сделать лучше всего в Java (код приложения) и / или все лучше всего сделать по БД (код sql).


сформировать точку зрения производительности: это очень простая арифметическая операция, которая почти наверняка может быть выполнена намного быстрее, чем на самом деле извлечение данных с дисков, которые находятся в базе данных. Кроме того, вычисление значений в предложении where, вероятно, будет очень быстрым в любой среде выполнения. Таким образом, узким местом должен быть диск IO, а не вычисление значений.

в соответствии с удобочитаемостью, я думаю, если вы используете ORM, вы должны сделать это в своей среде сервера приложений, потому что ORM позволит вам работать с базовыми данными очень легко, используя операции на основе набора. Если вы все равно собираетесь писать raw SQL, нет ничего плохого в том, чтобы делать вычисления там, ваш SQL также будет выглядеть немного лучше и легче читать, если отформатирован правильно.


принципиально, "производительность" не определена.

наиболее важным для меня является время разработчика.

напишите SQL-запрос. Если это слишком медленно или БД становится узким местом, то пересмотреть. К этому времени вы сможете сравнить два подхода и принять решение на основе реальных данных, относящихся к вашей настройке (аппаратное обеспечение и любой стек, на котором вы находитесь).


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

Что вы можете поддерживать лучше? Например, вы можете переключить интерфейс с Java на Flash, HTML5, C++ или что-то еще. Огромное количество программ прошли через такое изменение или даже существуют на нескольких языках, потому что они должны работать на нескольких устройствах.

даже если у вас правильный средний слой (из приведенного примера кажется, что это не так), этот слой может измениться, и JBoss может стать Ruby/Rails.

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

моя идея заключается в том, что если вы делаете вычисления в БД, будет намного проще написать второй front-end или middle-layer позже, потому что вам не нужно повторно реализовывать все. На практике, однако, я думаю, что "где я могу сделать это с кодом, который люди поймут" является наиболее важным фактором.


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

также в большинстве архитектур это SQL server(ы), которые составляют ядро системы и внешних систем, которые добавляются.

но математика выше настолько тривиальна, что если вы не толкаете свою систему до предела, лучшее место для ее размещения-там, где вы хотите ее разместить. Если бы математика не была тривиальной, например, вычисление sin/cos / tan для расчета расстояния, то усилие могло бы стать нетривиальным и потребовать тщательного планирования и тестирования.


другие ответы на этот вопрос интересны. удивительно, что никто не ответил на ваш вопрос. вам интересно:

  1. лучше ли бросать в центы в запросе? Я не думаю, что актеры to cents добавляет что-либо в ваш запрос.
  2. лучше ли использовать now () в запросе? Я бы предпочел передать даты в запрос, а не вычислять их в запросе.

Подробнее: На вопрос вы хотите быть уверены, что объединение фракций работает без ошибок округления. Я думаю, что число 19,2 разумно для денег и во втором случае целые числа в порядке. Использование поплавка для денег неправильно по этой причине.

для второго вопроса мне нравится иметь полный контроль в качестве программиста дата считается "сейчас". Может быть трудно написать автоматический блок тесты при использовании таких функций, как сейчас(). Кроме того, когда у вас больше сценарий транзакции может быть хорошо установить переменную, равную now() и используйте переменную so что вся логика использует одно и то же значение.


позвольте мне привести реальный пример для решения этого вопроса

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

  1. Вариант 1 Сделайте это в Python / Node etc и т. д.
  2. Вариант 2 Сделайте это в самом SQL!

какой из них лучше?

  • если бы мне пришлось сделать это в Python, по существу, мне пришлось бы извлечь все сохраненные записи в в худшем случае выполните вычисления и сохраните все, что, на мой взгляд, является огромной потерей IO
  • взвешенная скользящая средняя меняется каждый раз, когда вы получаете новую свечу, что означает, что я буду делать огромное количество IO через регулярные промежутки времени, что не является хорошее мнение в моем знаке
  • в SQL все, что мне нужно сделать, это, вероятно, написать триггер, который вычисляет и хранит все, поэтому нужно только получать окончательные значения WMA для каждой пары время от времени, и это намного больше эффективный

требования

  • если бы мне пришлось вычислить WMA для каждой свечи и сохранить ее, я бы сделал это на Python
  • но так как мне нужно только последнее значение, SQL намного быстрее, чем Python

чтобы дать вам некоторое поощрение, это версия Python, чтобы сделать взвешенную скользящую среднюю

WMA сделано через код

import psycopg2
import psycopg2.extras
from talib import func
import timeit
import numpy as np
with psycopg2.connect('dbname=xyz user=xyz') as conn:
with conn.cursor() as cur:
t0 = timeit.default_timer()
cur.execute('select distinct symbol from ohlc_900 order by symbol')
for symbol in cur.fetchall():
cur.execute('select c from ohlc_900 where symbol = %s order by ts', symbol)
ohlc = np.array(cur.fetchall(), dtype = ([('c', 'f8')]))
wma = func.WMA(ohlc['c'], 10)
# print(*symbol, wma[-1])
print(timeit.default_timer() - t0)
conn.close()

WMA Через SQL

"""
if the period is 10
then we need 9 previous candles or 15 x 9 = 135 mins on the interval department
we also need to start counting at row number - (count in that group - 10)
For example if AAPL had 134 coins and current row number was 125
weight at that row will be weight = 125 - (134 - 10) = 1
10 period WMA calculations
Row no Weight c
125 1
126 2
127 3
128 4
129 5
130 6
131 7
132 8
133 9
134 10
"""
query2 = """
WITH
condition(sym, maxts, cnt) as (
select symbol, max(ts), count(symbol) from ohlc_900 group by symbol
),
cte as (
select symbol, ts,
case when cnt >= 10 and ts >= maxts - interval '135 mins'
then (row_number() over (partition by symbol order by ts) - (cnt - 10)) * c
else null
end as weighted_close
from ohlc_900
INNER JOIN condition
ON symbol = sym
WINDOW
w as (partition by symbol order by ts rows between 9 preceding and current row)
)
select symbol, sum(weighted_close)/55 as wma
from cte
WHERE weighted_close is NOT NULL
GROUP by symbol ORDER BY symbol
"""
with psycopg2.connect('dbname=xyz user=xyz') as conn:
with conn.cursor() as cur:
t0 = timeit.default_timer()
cur.execute(query2)
# for i in cur.fetchall():
# print(*i)
print(timeit.default_timer() - t0)
conn.close()

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

скорость

0.42141127300055814 секунд Python

0.23801879299935536 секунд SQL

у меня есть 134000 поддельных записей OHLC в моей базе данных, разделенных между 1000 запасов, так что это пример того, где SQL может превзойти ваш сервер приложений