it-swarm.com.ru

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

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

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

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

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

Date previousDate ;// $1 calculate in application

Date todayDate;// $2 calculate in application

select amount where createddate between $1 and $2 

а затем перебрать записи и конвертировать сумму в центы в моем приложении 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.

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

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

132
hellojava

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

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

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

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

а затем перебрать записи 

Зацикливание сквозные записи - почти всегда неправильная вещь в sql - написание операции на основе множества является предпочтительным.

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

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

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

187
Marc Gravell

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

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

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

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

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

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

74
Erwin Brandstetter

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

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

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

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

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

  • сложные расчеты
  • интенсивные вычисления

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

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

  • сохранить объем данных, передаваемых между базой данных и приложением (в расчете на данные в БД)
  • уменьшите объем данных, загружаемых с диска базой данных (в пользу того, чтобы позволить базе данных оптимизировать операторы, чтобы избежать ненужного доступа к данным)
  • не выдвигайте базу данных до предела своего ЦП с помощью сложных параллельных вычислений (в пользу извлечения данных в память приложения и выполнения вычислений там)

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

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

12
Lukas Eder

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

В некоторых случаях это не относится, но когда это имеет смысл. Кроме того, в целом дБ-бокс имеет лучшее оборудование и производительность.

2
Davide Piras

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

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

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

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

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

1
Ryan

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

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

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

0
Donovanr

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

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

  1. Вариант 1 Сделайте это в Python/Node и т.д.
  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()

Хотите верьте, хотите нет, запрос выполняется быстрее, чем версия Pure Python для выполнения ВЕСОВЕДНОГО СРЕДНЕГО ДВИЖЕНИЯ !!! Я пошёл шаг за шагом в написании этого запроса, так что подожди, и у тебя всё будет хорошо

Скорость

0,42141127300055814 секунд Python

0,23801879299935536 секунд SQL

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

0
PirateApp

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

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

0
Johannes Gehrs

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

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

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

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

0
Chris Schoon

Важно отметить, что «производительность» не определена.

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

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

0
user2757750

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

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

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

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

Моя идея состоит в том, что если вы будете делать вычисления в БД, вам будет гораздо проще написать второй интерфейсный или промежуточный слой позже, потому что вам не нужно все заново реализовывать. На практике, однако, я думаю, что «где я могу сделать это с помощью кода, который люди поймут», является наиболее важным фактором.

0
Kajetan Abt

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

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

Есть много других аспектов, о которых вы можете подумать, прежде чем решить, где разместить код. Одно восприятие совершенно неверно - все может быть лучше всего сделано в Java (код приложения) и/или все лучше всего сделать с помощью db (код SQL).

0
Neo