Тайм-аут запроса к базе данных на Герою - PullRequest
0 голосов
/ 09 марта 2012

Я стресс-тестирую приложение, добавляя грузы и грузы предметов и заставляя его выполнять большую работу.

select *, (
    select price 
    from prices 
    WHERE widget_id = widget.id 
    ORDER BY id DESC
    LIMIT 1
    ) as maxprice
FROM widgets 
ORDER BY created_at DESC 
LIMIT 20 OFFSET 0
  • этот запрос выбирает из виджетов (около 8500), а цены содержат 777000 или около того записей в нем.

Время запроса истекло в тестовой среде, котораяиспользует базовую общую базу данных Heroku.(Макс. 5 мегабайт в 193 МБ)

Что решит проблему с тайм-аутом?Цены обновляются каждый час, поэтому каждый час вы получаете 8500x новых строк.

Это слишком много для приложения (на самом деле маловероятно, что в нем будет 8500 виджетов), но мне интересно, что уместно для решения этой проблемы?

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

Или я просто превышаю предел общей базы данныхи следует ли ожидать перехода на выделенную базу данных (например, минимум $ 200 в месяц выделенного экземпляра postgres от Heroku.) с учетом размера таблицы цен?Есть ли более глубокая проблема с точки зрения того, как я спроектировал БД?(т.е. это один ко многим, один виджет имеет много цен.) Есть ли более разумный подход?

Я совершенно новичок в мире SQL и запросов и т. д. в масштабе, отсюда и полное невежество, выраженноевыше.:)

Ответы [ 2 ]

1 голос
/ 09 марта 2012

Окончательная версия после комментариев ниже:

@ Дейв хочет latest price за виджет. Вы можете сделать это в подзапросах и LIMIT 1 на виджет, но в современном PostgreSQL оконная функция делает работу более элегантно. Рассмотрим first_value() / last_value():

SELECT w.*
     , first_value(p.price) OVER (PARTITION BY w.id
                                  ORDER BY created_at DESC) AS latest_price
FROM (
    SELECT *
    FROM   widgets
    ORDER  BY created_at DESC
    LIMIT  20
    )  w
JOIN   prices p ON p.widget_id = w.id
GROUP  BY w.col1, w.col2 -- spell out all columns of w.*

Оригинальный пост по максимальной цене за виджет:

SELECT w.*
     , max(p.price) AS max_price
FROM (
    SELECT *
    FROM   widgets
    ORDER  BY created_at DESC
    LIMIT  20
    )  w
JOIN   prices p ON p.widget_id = w.id
GROUP  BY w.col1, w.col2 -- spell out all columns of w.*
  • Исправление псевдонимов таблицы.

  • Получить все столбцы widgets, как показывает вопрос

  • В PostgreSQL 8.3 вы должны указать все неагрегированные столбцы списка SELECT в предложении GROUP BY. В PostgreSQL 9.1 или новее столбец первичного ключа будет покрывать всю таблицу. Я цитирую руководство здесь :

Разрешить не-GROUP BY столбцы в списке целей запроса, когда основной ключ указан в предложении GROUP BY

  • Я советую никогда не использовать идентификаторы смешанного регистра как maxWidgetPrice. По умолчанию в PostgreSQL идентификаторы без кавычек свернуты в нижний регистр. Сделайте себе одолжение и используйте исключительно строчные идентификаторы.

  • Всегда используйте явные условия JOIN, где это возможно. Это канонический способ SQL, и он более читабелен.

  • OFFSET 0 просто шум


Индексы:

Однако ключом к производительности являются правильные индексы . Я бы пошел два индекса, как эти:

CREATE INDEX widgets_created_at_idx ON widgets (created_at DESC);
CREATE INDEX prices_widget_id_idx ON prices(widget_id, price DESC);

Второй - это многоколонный индекс , который должен обеспечить максимальную производительность для получения максимального выигрыша после определения 20 лучших виджетов с использованием первого индекса. Не уверен, что PostgreSQL 8.3 (по умолчанию для совместно используемой базы данных Heroku) уже достаточно умен, чтобы максимально использовать его. PostgreSQL 9.1, безусловно, есть.

Для последней цены (см. Комментарии) используйте этот индекс вместо:

CREATE INDEX prices_widget_id_idx ON prices(widget_id, created_at DESC);

Вы не должны (и не должны) просто доверять мне. Проверьте производительность и создайте планы запросов с помощью EXPLAIN ANALYZE с индексами и без них и убедитесь сами. Создание индекса должно быть очень быстрым, даже для миллиона строк.


Если вы решите перейти на автономную базу данных PostgreSQL на Heroku, вас может заинтересовать это недавнее сообщение в блоге Heroku :

  1. По умолчанию сейчас используется PostgreSQL 9.1.
  2. Теперь вы можете отменить длительные запросы.
0 голосов
/ 09 марта 2012

Я не совсем понимаю, о чем вы спрашиваете, но вот мое понимание:

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

SELECT w.id
  FROM widgets
  ORDER BY created_at DESC
  LIMIT 20 OFFSET 0  

Для каждого из 20 найденных виджетов, кажется, вы хотите найти наибольшую связанную цену из таблицы виджетов:

SELECT s.id, MAX(p.price) AS maxWidgetPrice
  FROM (SELECT w.id
          FROM widgets
          ORDER BY created_at DESC
          LIMIT 20 OFFSET 0
        ) s -- widget subset
      , prices p
  WHERE s.id = p.widget_id
  GROUP BY s.id

values.widget_id должен быть проиндексирован, чтобы это было эффективным.Вы не хотите обрабатывать всю таблицу цен каждый раз, если она относительно велика, вам нужно только подмножество строк.РЕДАКТИРОВАТЬ: добавлено "group by" (и нет, это не было проверено)

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