2015-12-14 3 views
7

У меня есть следующие таблицы:Как увеличить скорость моего оператора выбора Postgres?

CREATE TABLE views (
    view_id bigint NOT NULL, 
    usr_id bigint, 
    ip inet, 
    referer_id bigint, 
    country_id integer, 
    validated smallint, 
    completed smallint, 
    value numeric 
); 

ALTER TABLE ONLY views 
    ADD CONSTRAINT "Views_pkey" PRIMARY KEY (view_id); 

CREATE TABLE country (
    country_id integer NOT NULL, 
    country character varying(2) 
); 

ALTER TABLE ONLY country 
    ADD CONSTRAINT country_pkey PRIMARY KEY (country_id); 

CREATE TABLE file_id_view_id (
    file_id bigint, 
    view_id bigint, 
    created_ts timestamp without time zone 
); 

CREATE TABLE file_owner (
    file_id bigint NOT NULL, 
    owner_id bigint 
); 

ALTER TABLE ONLY file_owner 
     ADD CONSTRAINT owner_table_pkey PRIMARY KEY (file_id); 

CREATE TABLE referer (
    referer_id bigint NOT NULL, 
    referer character varying(255) 
); 

ALTER TABLE ONLY referer 
    ADD CONSTRAINT referer_pkey PRIMARY KEY (referer_id); 

views и file_id_view_id таблицы имеют примерно 340M строк каждый. Каждый час они оба увеличиваются на 600K строк.

В таблице file_owner имеет 75K строк и ежечасно увеличится на строк.

Таблица country имеет ряды и редко меняются.

Таблица referer имеет ряды и редко меняются.

Моя цель состоит в том, чтобы быть в состоянии выполнить запрос, такие как:

SELECT Count(ft.*)      AS total_views, 
     (Count(ft.*) - SUM(ft.valid)) AS invalid_views, 
     SUM(ft.valid)     AS valid_views, 
     SUM(ft.values)     AS VALUES, 
     ft.day       AS day, 
     (CASE 
      WHEN r.referer IS NULL THEN 'Unknown' 
      ELSE r.referer 
     END)       AS referer, 
     (CASE 
      WHEN c.country IS NULL THEN 'Unknown' 
      ELSE c.country 
     END)       AS country 
FROM country c 
     right join (referer r 
        right join (SELECT v.validated AS valid, 
             v.value  AS VALUES, 
             vf.day  AS day, 
             vf.view_id AS view_id, 
             v.referer_id AS referer_id, 
             v.country_id AS country_id 
           FROM VIEWS v, 
             (SELECT view_id, 
fivi.created_ts :: timestamp :: DATE AS 
day 
FROM file_id_view_id fivi 
join (SELECT file_id 
     FROM file_owner 
     WHERE owner_id = 75 
     GROUP BY file_id) fo 
    ON (fo.file_id = fivi.file_id) 
WHERE (fivi.created_ts BETWEEN 
    '2015-11-01' AND '2015-12-01') 
GROUP BY view_id, 
    day) vf 
WHERE v.view_id = vf.view_id) ft 
ON (ft.referer_id = r.referer_id)) 
ON (ft.country_id = c.country_id) 
GROUP BY day, 
      referer, 
      country; 

Для производства:

GroupAggregate (cost=38893491.99..40443007.61 rows=182295955 width=52) (actual time=183725.696..205882.889 rows=172 loops=1) 
    Group Key: ((fivi.created_ts)::date), r.referer, c.country 
    -> Sort (cost=38893491.99..38984639.97 rows=182295955 width=52) (actual time=183725.655..200899.098 rows=8390217 loops=1) 
     Sort Key: ((fivi.created_ts)::date), r.referer, c.country 
     Sort Method: external merge Disk: 420192kB 
     -> Hash Left Join (cost=16340128.88..24989809.75 rows=182295955 width=52) (actual time=23399.900..104337.332 rows=8390217 loops=1) 
       Hash Cond: (v.country_id = c.country_id) 
       -> Hash Left Join (cost=16340125.36..24800637.72 rows=182295955 width=49) (actual time=23399.782..102534.655 rows=8390217 loops=1) 
        Hash Cond: (v.referer_id = r.referer_id) 
        -> Merge Join (cost=16340033.52..24051874.62 rows=182295955 width=29) (actual time=23397.410..99955.000 rows=8390217 loops=1) 
          Merge Cond: (fivi.view_id = v.view_id) 
          -> Group (cost=16340033.41..16716038.36 rows=182295955 width=16) (actual time=23397.298..30454.444 rows=8390217 loops=1) 
           Group Key: fivi.view_id, ((fivi.created_ts)::date) 
           -> Sort (cost=16340033.41..16434985.73 rows=189904653 width=16) (actual time=23397.294..28165.729 rows=8390217 loops=1) 
             Sort Key: fivi.view_id, ((fivi.created_ts)::date) 
             Sort Method: external merge Disk: 180392kB 
             -> Nested Loop (cost=6530.43..8799350.01 rows=189904653 width=16) (actual time=63.123..15131.956 rows=8390217 loops=1) 
              -> HashAggregate (cost=6530.31..6659.62 rows=43104 width=8) (actual time=62.983..90.331 rows=43887 loops=1) 
                Group Key: file_owner.file_id 
                -> Bitmap Heap Scan on file_owner (cost=342.90..6508.76 rows=43104 width=8) (actual time=5.407..50.779 rows=43887 loops=1) 
                 Recheck Cond: (owner_id = 75) 
                 Heap Blocks: exact=5904 
                 -> Bitmap Index Scan on owner_id_index (cost=0.00..340.74 rows=43104 width=0) (actual time=4.327..4.327 rows=45576 loops=1) 
                   Index Cond: (owner_id = 75) 
              -> Index Scan using file_id_view_id_indexing on file_id_view_id fivi (cost=0.11..188.56 rows=4406 width=24) (actual time=0.122..0.306 rows=191 loops=43887) 
                Index Cond: (file_id = file_owner.file_id) 
                Filter: ((created_ts >= '2015-11-01 00:00:00'::timestamp without time zone) AND (created_ts <= '2015-12-01 00:00:00'::timestamp without time zone)) 
                Rows Removed by Filter: 184 
          -> Index Scan using "Views_pkey" on views v (cost=0.11..5981433.17 rows=338958763 width=25) (actual time=0.088..46804.757 rows=213018702 loops=1) 
        -> Hash (cost=68.77..68.77 rows=6591 width=28) (actual time=2.344..2.344 rows=6495 loops=1) 
          Buckets: 1024 Batches: 1 Memory Usage: 410kB 
          -> Seq Scan on referer r (cost=0.00..68.77 rows=6591 width=28) (actual time=0.006..1.156 rows=6495 loops=1) 
       -> Hash (cost=2.70..2.70 rows=233 width=7) (actual time=0.078..0.078 rows=233 loops=1) 
        Buckets: 1024 Batches: 1 Memory Usage: 10kB 
        -> Seq Scan on country c (cost=0.00..2.70 rows=233 width=7) (actual time=0.005..0.042 rows=233 loops=1) 
Planning time: 1.015 ms 
Execution time: 206034.660 ms 
(37 rows) 
:

total_views | invalid_views | valid_views | values | day  |  referer  | country 
------------+---------------+-------------+--------+------------+-----------------+--------- 

При выполнении такого запроса с EXPLAIN ANALYZE производится следующая

План на explain.depesz.com: http://explain.depesz.com/s/OiN

206s Время работы.

Некоторые вещи, чтобы отметить,

Postgresql Версия 9,4

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

  1. shared_buffers = 30GB
  2. work_mem = 32MB
  3. random_page_cost = 2.0
  4. cpu_tuple_cost = 0,0030
  5. cpu_index_tuple_cost = 0,0010
  6. cpu_operator_cost = 0.0005
  7. effective_cache_size = 52GB

В настоящее время существуют следующие показатели:

  1. CREATE INDEX country_index ON стране ИСПОЛЬЗОВАНИЕ ВТКЕЕ (страна);
  2. CREATE INDEX created_ts_index ON file_id_view_id ИСПОЛЬЗОВАНИЕ btree (created_ts);
  3. CREATE INDEX file_id_created_ts_index ON file_id_view_id ИСПОЛЬЗОВАНИЕ btree (created_ts, file_id);
  4. CREATE INDEX file_id_view_id_indexing ON file_id_view_id ИСПОЛЬЗОВАНИЕ btree (file_id);
  5. CREATE INDEX owner_id_file_id_index ON file_owner ИСПОЛЬЗОВАНИЕ btree (file_id, owner_id);
  6. CREATE INDEX owner_id_index ON file_owner ИСПОЛЬЗОВАНИЕ btree (owner_id);
  7. CREATE INDEX referer_index ON referer ИСПОЛЬЗОВАНИЕ btree (referer);

предыдущий запрос был использованием владельца идентификатор который был определена консервативно, некоторые запросы могут привести к 1/3 таблицы file_id_view_id соединены с видом.

Изменение структуры данных Последнее Курорт. На данном этапе такое изменение должно быть вызвано серьезными проблемами.

db может считаться прочитанным только в случае необходимости, записываемые данные выполняются почасово, и после каждой записи предоставляется Postgres. В настоящий момент в течение 600K почасовая запись db возвращается в 1100-х годах (это связано с другими причинами наряду со стоимостью вставки). Существует множество возможностей для добавления дополнительных индексов, если это увеличит скорость чтения, причем скорость чтения является приоритетом.

Спецификации аппаратного обеспечения являются:

CPU: http://ark.intel.com/products/83356/Intel-Xeon-Processor-E5-2630-v3-20M-Cache-2_40-GHz

Оперативная память: 128GB

ХРАНЕНИЕ: 1.5TB PCIE SSD

Как я могу оптимизировать либо свою базу данных или запрос так что я могу получить информацию, которая мне нужна из db, в разумные сроки?

Что можно сделать для оптимизации моего текущего дизайна?

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

UPDATE

Я попытался:

  1. Анализ таблицы, не влияет на производительность.
  2. Увеличьте work_mem, это привело к увеличению скорости до 116 с.
  3. В зависимости от планировщика запросов Postgres, избегая подвыборов, это отрицательно влияет на производительность.
  4. Отдельный поиск db перед рукой, это, по-видимому, не оказывает положительного/отрицательного эффекта.

Есть ли у кого-нибудь опыт перестройки столов? Возможно ли это? Будут ли дни, часы (оценка, конечно)?

Я рассматриваю возможность де-нормализации базы данных, так как в этом методе это будет только ссылка. Мое единственное беспокойство в том, что: если бы 100M строк вызывались из таблицы с индексированным owner_id, это было бы достаточно быстро или мне бы пришлось столкнуться с теми же проблемами производительности? Не хотелось бы идти в одну сторону, а потом отступать.

Другое решение, которое я рассматриваю, - это предложение @ ivan.panasuik, группировать данные за весь день в другую таблицу, поскольку как только день прошел, эта информация является постоянной и ее не нужно менять или обновлять. Однако я не уверен, как реализовать это гладко - должны ли я запускать запросы через данные, в то время как вставки находятся на удержании и как можно быстрее улавливают дни? С этого момента есть триггерный набор?

+2

Оценки на самом деле не такие точные.Вы анализировали вовлеченные таблицы? У вас также есть два довольно больших типа, которые выполняются на диске. Вы можете попробовать увеличить work_mem резко _ для этого запроса_, например. 'set work_mem = '512MB'' или даже' set work_mem =' 1GB'' –

+0

У меня было впечатление, что Postgres автоматически проанализирует таблицы, должен ли я также вручную это делать? Когда вы говорите _that query_, вы имеете в виду, что существует определенный способ установить work_mem для одного запроса? –

+0

Это _should_ автоматически делает это, но иногда (например, после начальной загрузки) оно не срабатывает достаточно быстро. Операции, которые я показывал при запуске _before_, ваш запрос изменит «work_mem» для текущего сеанса: http://www.postgresql.org/docs/current/static/sql-set.html –

ответ

2

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

  1. Старайтесь избегать подзапросов - особенно при работе с большим количеством данных. Планировщик запросов не может быть оптимизирован. В большинстве случаев вы должны иметь возможность конвертировать простые подзапросы в JOINs или даже отдельные запросы к базам данных перед обработкой, если это необходимо.

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

  3. Рассмотрите стратегию хранилища данных - когда вы имеете дело с этим объемом данных, вы должны рассмотреть возможность хранения копии данных в денормализованном виде, который очень быстро извлекается, потому что неприятные JOINs уже позаботились. Мы делаем это с помощью Redshift (производная от PostgeSQL), поэтому нам не нужно делать какие-либо JOIN при запуске отчетов.

+0

Мне нравится ваше третье предложение для некоторых вещей, над которыми я работаю ... Сохранение временного кэша полностью разнесенных данных параллельно с эффективно хранящимися данными до тех пор, пока не пройдет разумное окно. Благодаря! –

2
  1. Remove (Count (футы *) -. SUM (ft.valid)) AS invalid_views, так как у вас уже есть эти значения, и вы можете вычислить его позже, во время отображения результатов
  2. Добавить индекс по file_owner.file_id и убедитесь, что каждое поле, использующее запрос, имеет индекс (поля, которые вы используете в условиях: где, группа и т. д.)
  3. Я больше не анализировал запрос, но вам кажется, что вы должны разделить запрос в нескольких меньших (и более быстрых) запросах и подключить его с помощью временных таблиц или хранимой процедуры.
  4. Предполагая, что результат за вчерашний день не изменится ... вы можете запустить запрос с условием day = today() и избегать группировки по дням. Результаты всех дней можно сохранить в отдельной таблице. Я вижу, что большую часть времени он работает с группировкой.

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

 Смежные вопросы

  • Нет связанных вопросов^_^