У меня есть следующая проблема оптимизации запросов в Spanner, и я надеюсь, что мне не хватает уловки, которая поможет мне подчинить планировщик запросов моей воле.
Вот упрощенная схема:
create table T0 (
key0 int64 not null,
value int64,
other int64 not null,
) primary key (key0);
create table T1 {
key1 int64 not null,
other int64 not null
} primary key (key1);
И запрос с подзапросом в предложении IN
:
select value from T0 t0
where t0.other in (
select t1.other from T1 t1 where t1.key1 in (42, 43, 44) -- note: this subquery is a good deal more complex than this
)
Что создает набор из 10 элементов через хеш-соединение T0 с выходом подзапроса:
Operator Rows Executions
----------------------- ----- ----------
Serialize Result 10 1
Hash Join 10 1
Distributed union 10000 1
Local distributed union 10000 1
Table Scan: T0 10000 1
Distributed cross apply: 5 1
...lots moar T1 subquery stuff...
Обратите внимание: хотя подзапрос сложен, на самом деле он создает очень маленький набор. К сожалению, он также сканирует целиком T1 для передачи в хеш-соединение, что очень медленно.
Однако, если я возьму результат подзапроса на T1 и вручную вставлю его в предложение IN
:
select value from T0
where other in (5, 6, 7, 8, 9) -- presume this `IN` clause to be the output of the above subquery
Это значительно быстрее, по-видимому, потому, что он просто попадает в индекс T0 один раз для каждой записи, не используя хеш-соединение для всего содержимого:
Operator Rows Executions
----------------------- ---- ----------
Distributed union 10 1
Local distributed union 10 1
Serialize Result 10 1
Filter 10 1
Index Scan: 10 1
Я мог бы просто выполнить два запроса, и пока это мой лучший план. Но я надеюсь, что смогу найти способ уговорить Шпаннера решить, что это то, что он должен делать с выводом подзапроса в первом примере. Я перепробовал все, что мог придумать, но это может быть просто невыразимо в SQL.
Также: я еще не совсем доказал это, но в некоторых случаях я опасаюсь, что вывод подзапроса из 10 элементов может взорваться до нескольких тысяч элементов (T1 будет расти более или менее неограниченно, легко до миллионов). Я вручную протестировал несколько сотен элементов в выделенном предложении IN
, и, похоже, он работает приемлемо, но меня немного беспокоит, что это может выйти из-под контроля.
Обратите внимание, что я также пробовал присоединиться к подзапросу, например:
select t0.other from T0 t0
join (
-- Yes, this could be a simple join rather than a subquery, but in practice it's complex
-- enough that it can't be expressed that way.
select t1.other from T1 t1 where t1.key = 42
) sub on sub.other = t0.other
Но в планировщике запросов произошла поистине ужасающая вещь, которую я даже не буду пытаться здесь объяснять.
distinct
, которая, я думаю, также может быть необходима). Это сканирует строки примерноO(sub-query)
, на что я и рассчитывал. 13.06.2017