Предположим, у меня есть граф G и следующий запрос:
x y z w q r s
(?a)--(?b)--(?c)--(?d)--(?e)--(?f)--(?g)--(?h)
, где {? A,? B,? c, ...,? H} переменные, и {x, y, z, w, q, r, s} являются метками ar c.
На уровне хранения у меня есть одна таблица для каждой метки, но также для комбинации двух меток. Например, у меня может быть таблица x со столбцами | a | b | , но у меня также есть таблица xy со столбцами | a | б | с | . Да, у меня есть избыточные таблицы.
Основываясь на этом параметре, у меня есть две проблемы:
a) Мне нужно найти таблицы таким образом, чтобы соединение между ними приводило к наилучшему времени выполнения ( маленький). Пусть {xy zw, q, rs} будут этими таблицами для примера выше.
b) Мне нужно выполнить соединения в заданном порядке, поэтому мне нужно найти этот порядок, например: ( rs ⨝ q) ⨝ (zw ⨝ xy) (⨝ - естественное объединение).
Предполагая, что я знаю, какие таблицы использовать, т.е. что я решил a), мой вопрос заключается в том, как решить второй. Spark API позволяет мне выполнять все объединения в одной строке:
val res1 = xy.join(zw, Seq("c")).join(q, Seq("e")).join(rs, Seq("f"))
, но я также могу выполнить это в несколько строк:
val tmp1 = xy.join(zw, Seq("c"))
val tmp2 = q.join(rs, Seq("f"))
val res2 = tmp1.join(tmp2, Seq("e"))
Время выполнения res1.count и res2.count (средний из нескольких прогонов) отличается в моих экспериментах. Кажется, способ построения дерева влияет на выполнение.
1) Какую стратегию я могу использовать для построения дерева, которое приводит к оптимальному времени выполнения в Spark?
2 ) Если каждое отдельное дерево приводит к разной производительности, какова роль оптимизатора запросов? порядок объединения. Кажется, он ничего не делает, особенно в случае, когда у меня есть все объединения в одной строке кода:
val res1 = xy.join(zw, Seq("c")).join(q, Seq("e")).join(rs, Seq("f"))
и
val res3 = rs.join(q, Seq("f")).join(zw, Seq("e")).join(xy, Seq("c"))
В одном случае я мог иметь разумный время исполнения. В другой тайм-аут. Catalyst ничего не делает?