Каковы плюсы и минусы выполнения расчетов в SQL по сравнению с вашим приложением - PullRequest
136 голосов
/ 22 сентября 2011

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.

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

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

Ответы [ 13 ]

192 голосов
/ 22 сентября 2011

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

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

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

Примечание:

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

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

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

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

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

77 голосов
/ 22 сентября 2011

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

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

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

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

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

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

17 голосов
/ 22 сентября 2011

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

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

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

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

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

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

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

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

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

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

2 голосов
/ 22 сентября 2011

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

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

1 голос
/ 07 сентября 2013

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

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

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

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

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

0 голосов
/ 06 июня 2018

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

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

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

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

  • Если бы мне пришлось делать это в Python, по сути, мне пришлось бы извлекать все сохраненные записи в худшем случае, выполнять вычисления и сохранять все обратно, что, на мой взгляд, является огромной потерей 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 голосов
/ 09 сентября 2013

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

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

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

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

0 голосов
/ 08 сентября 2013

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

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

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

0 голосов
/ 08 сентября 2013

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

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

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

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

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

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