2016-08-02 5 views
0

**** EDIT ****Как я могу оптимизировать запрос 'IN'?

14ms может показаться не очень много, однако, как вы можете видеть ниже в «PostgresSQL Explain», PostgreSQL выполняет Seq Scan на 80 000 строк. Должен быть способ избежать этого сканирования и сделать несколько запросов индекса.

**** EDIT END ****

Я играл с идеей schemaless и я следующие три таблицы:

Таблицы заполняются 100000 случайных записей.

entities(_primary_key SERIAL PRIMARY KEY, _id CHAR(32) UNIQUE, 
    data BYTEA) 

index_username_profile_names(_id CHARE(32) PRIMARY KEY, 
    key VARCHAR UNIQUE) 

index_username_email(_id CHAR(32) PRIMARY KEY, key VARCHAR) 

с неоднозначным индексом index_username_email(key)

Моего SQL запрос:

SELECT data FROM entities WHERE 
    _id IN (SELECT _id FROM index_users_email WHERE key = 'test') 
OR 
    _id in (SELECT _id FROM index_users_profile_name WHERE key = 'test') 

Это занимает коклюш 14ms, хотя «тест» не завершает работу ни в одном из «индекса ', независимо от того, использую ли я PostgreSQL или MySQL, поэтому это должно быть то, что я делаю неправильно.

Любая идея, как я могу ее оптимизировать или что я делаю неправильно?

Спасибо!

Postgres объяснить:

Seq Scan on entities (cost=16.88..4776.15 rows=80414 width=163) (actual time=15.169..15.169 rows=0 loops=1) 
    Filter: ((hashed SubPlan 1) OR (hashed SubPlan 2)) 
    Rows Removed by Filter: 107218 
    SubPlan 1 
    -> Index Scan using index_users_email_key_idx1 on index_users_email (cost=0.42..8.44 rows=1 width=33) (actual time=0.039..0.039 rows=0 loops=1) 
      Index Cond: ((key)::text = 'test'::text) 
    SubPlan 2 
    -> Index Scan using index_users_profile_name_key_idx1 on index_users_profile_name (cost=0.42..8.44 rows=1 width=33) (actual time=0.071..0.071 rows=0 loops=1) 
      Index Cond: ((key)::text = 'test'::text) 
Planning time: 0.202 ms 
Execution time: 15.216 ms 
+4

14 * миллисекунды * не "колоссальную". –

+0

Когда это должно быть меньше 1 мс, это: D –

+0

Ничто не меньше, чем 8 мс – Drew

ответ

1

OR издание (сшиваемых) условия, как правило, плохо, попробуйте UNION вместо:

SELECT data FROM entities 
WHERE _id IN 
(SELECT _id 
    FROM index_users_email 
    WHERE key = 'test' 
) 
UNION 
SELECT data FROM entities 
WHERE _id in 
(SELECT _id 
    FROM index_users_profile_name 
    WHERE key = 'test' 
) 
+0

Yeap. СОЮЗ сделал это. Я также изменил «IN» на JOINs, и он работает как шарм. –

+0

еще раз спасибо: D –

0
SELECT data 
FROM entities e 
    LEFT OUTER JOIN index_users_email iue ON e._id=iue._id and iue.key = 'test' 
    LEFT OUTER JOIN index_users_profile_name iupn ON e._id=iupn._id and iupn .key = 'test' 
WHERE 
    iue._id IS NOT NULL or iupn._id IS NOT NULL 
+0

Кажется, это не трюк. 40ms –

+0

Хотя этот код может помочь решить проблему, предоставив дополнительный контекст относительно _why_ и/или _how_ ответа , этот вопрос значительно улучшит его долгосрочную ценность. Пожалуйста, отредактируйте свой ответ, чтобы добавить объяснения, включая то, что применяются ограничения и допущения . –

0

Похоже, один потенциальный ответ union и join:

explain select data from entities as t join (select _id from index_users_email where key = 'test' union select _id from index_users_profile_name where key = 'test') u on t._id = u._id; 
                   QUERY PLAN                 
------------------------------------------------------------------------------------------------------------------------------------------- 
Nested Loop (cost=17.32..33.82 rows=2 width=163) 
    -> Unique (cost=16.90..16.91 rows=2 width=33) 
     -> Sort (cost=16.90..16.91 rows=2 width=33) 
       Sort Key: index_users_email._id 
       -> Append (cost=0.42..16.89 rows=2 width=33) 
        -> Index Scan using index_users_email_key_idx1 on index_users_email (cost=0.42..8.44 rows=1 width=33) 
          Index Cond: ((key)::text = 'test'::text) 
        -> Index Scan using index_users_profile_name_key_idx1 on index_users_profile_name (cost=0.42..8.44 rows=1 width=33) 
          Index Cond: ((key)::text = 'test'::text) 
    -> Index Scan using entities__id_key on entities t (cost=0.42..8.44 rows=1 width=196) 
     Index Cond: (_id = index_users_email._id) 
(11 rows) 

Time: 0.714 ms 
+1

Пожалуйста, используйте 'explain analysis' вместо' explain' – joop

0

Это будет иметь более высокую производительность, как это имеет преимущество первичного ключа в этих таблицах :

select data 
from entities 
where 
    exists (
     select _id 
     from index_users_email 
     where key = 'test' and _id = entities._id 
    ) or exists (
     select _id 
     from index_users_profile_name 
     where key = 'test' and _id = entities._id 
    ) 
+0

Требуется 16 мс. Он использует все ключи, как вы упомянули, но в конце делает seq-сканирование на сущности ... »Seq Scan on entity (стоимость = 0.00..955782.93 rows = 80414 width = 163) (фактическое время = 15.524..15.524 строки = 0 петель = 1) " –

1

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

Во-вторых, если вы делаете реальные тайминги, имейте в виду следующее:

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

С точки зрения запроса, одна вещь, которую я могу думать о том, чтобы использовать = и exists:

SELECT e.data 
FROM entities e 
WHERE _id = (SELECT _id FROM index_users_email WHERE key = 'test') OR 
     EXISTS (SELECT 1 FROM index_users_profile_name iupn WHERE iupn._id = e.id AND iupn.key = 'test'); 

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

+0

Пожалуйста, ознакомьтесь с объяснением Postgres. Для Seq Scan нет никаких оснований. Кроме того, 14 мс для запроса, который должен принимать 0,5 мс, - ОГРОМНОЕ деградация. –

+0

Существует веская причина для seqscan: abscense соответствующей статистики. – wildplasser

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