HighLoad.org – блог о высоких нагрузках
HighLoad.org > Правда о JOIN'ах

Правда о JOIN'ах
2010-03-09 20:23 my_fess 
Каждый раз, когда у меня появляется возможность рассказать моим коллегам о крутости App Engine, мне причиняет боль необходимость говорить: "... но вы не можете делать JOIN'ы". Я знаю насколько полезными могут быть JOIN'ы, потому что я использовал их много лет до того, как начал работать на App Engine. В последнее время я раздумывал над тем, что должен быть способ заставить их работать. Хранилище данных App Engine гарантирует, что производительность запросов масштабируется с ростом размера результирующего множества, а не всего множества данных. Таким образом запрос, возвращающий 100 записей, должен выполняться за одинаковое время и на тысяче записей и на миллионе. Это означает, что мы не можем вычислить полное декартово произведение во время запроса, потому что это потребует просмотра всех данных в n объединяемых таблицах, и если мы просматриваем все данные, наша производительность оказывается связанной с размером множества данных. Тем не менее похоже, что мы можем что-то с этим сделать.
Что если мы опустим взгляд немного ниже?  Естественно, что n-кратные JOIN'ы с произвольными фильтрацией и порядком сортировки были бы идеальны, но разве хотя бы что-то в данном случае не лучше чем совсем ничего?  Оказывается с некоторыми дополнительными ограничениями возможно выполнять JOIN'ы, при которых производительность масштабируется с ростом размера результирующего множества. В недавно выпущенном SDK 1.3.1 эта возможность, которую я называю "простые JOIN'ы" доступна бета тестерам. Теперь, прежде чем вы начнете предвкушать, дайте мне сообщить вам плохие новости (их немало). Во-первых, простые JOIN'ы доступны только пользователям JPA и JDO. Почему? Потому что у JPA и JDO уже есть устоявшееся API для JOIN'ов, а проектирование API это очень сложная задача. Я стараюсь по возможности избегать этого. Во-вторых, простые JOIN'ы поддерживают фильтры только по равенству. Неприятно, не правда ли? В-третьих, простые JOIN'ы могут сортироваться только по свойству, по которому выполняется объединение. Вдвойне неприятно. В-четвертых, если вы используете зависимые отношения, то для того, чтобы использовать простые JOIN'ы вам придется повысить версию вашего хранилища и перевести существующие данные. Этот пост и так уже достаточно затянулся, поэтому я сосредоточусь на простых JOIN'ах независимых отношений. Я обещаю раскрыть тему версий хранилища, переноса данных и соединений зависимых отношений в своем следующем посте. Я знаю, что плохих новостей было много. Но моя теория заключается в том, что даже со всеми этими недостатками, простые JOIN'ы существенно полезны. Я делаю их доступными, чтобы проверить и доказать эту теорию. Готовы попробовать? Давайте смоделируем колледж. У нас есть Студенты (Students), Курсы (Courses) и Общежития (Dorms). Каждый студент связан с 0 или более общежитиями, и 0 или более курсами.
JPA:
@Entity
public class Student
{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String year;

    @Basic
    private List courses = new ArrayList();

    @OneToMany
    @Column(name = "courses", insertable = false, updatable = false)
    private List coursesAlias;

    @Basic
    private Key dorm;

    @OneToOne(fetch = FetchType.LAZY)
    @Column(name = "dorm", insertable = false, updatable = false)
    private Dorm dormAlias;
}

@Entity
public class Course
{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Key key;

    private String department;
}

@Entity
public class Dorm
{
    @ID
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Key key;

    private String campus;
}
Да, есть кое-что хитрое с полями courseAlias и dormAlias. JPQL компилятор не даст вам сделать объединение по Key или List полям, потому что они не один-к-одному и не один-ко-многим поля. Чтобы обойти это, я создал read-only alias соответствующего типа. Код вашего приложения должен всегда читать (писать) из полей 'courses' и 'dorm', в то время как join запросы должны читать (писать) из полей 'coursesAlias' и 'dormAlias'. Когда появится должная поддержка независисимых отношений, эта бессмыслица прекратится.
JDO:
@PersistenceCapable(detachable = "true")
public class Student
{
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Long id;

    private String year;

    @Element(dependent = "true")
    private List courses = new ArrayList();

    @Persistent
    private Dorm dorm;
}

@PersistenceCapable(detachable = "true")
public class Course
{
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Key key;

    private String department;
}

@PersistenceCapable(detachable = "true")
public class Dorm
{
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Key key;

    private String campus;
}

Вот запрос, который возвращает всех джуниоров, зарегистрированных на курсе биологии.
JPA:
Query q = em.createQuery("SELECT FROM " + Student.class.getName()
+ " s JOIN s.coursesAlias c WHERE "
+ "c.department = 'Biology' AND "
+ "s.year = 'Junior'");

JDO:
Query q = pm.newQuery("SELECT FROM " + Student.class.getName()
+ " WHERE "
+ "courses.contains(c) && c.department == 'Biology' && "
+ "year == 'Junior'");
Это определенно странно ссылаться на несуществующие свойства полей Key и List, но пока правильные независые отношения еще не поддерживаются, я ослабил проверку типов компилятора JDOQL, чтобы это заработало. А этот запрос возвращает всех джуниоров, которые живут в общежитии в северном кампусе.
JPA:
Query q = em.createQuery("SELECT FROM " + Student.class.getName()
  + " s JOIN s.dormAlias d WHERE "
  + "d.campus = 'North' AND "
  + "s.year = 'Junior'");

JDO:
Query q = pm.newQuery("SELECT FROM " + Student.class.getName() + " WHERE "
  + "dorm == d && d.campus == 'North' && "
  + "year == 'Junior'");
Опять я применяю некоторые послабления к стандартному JDOQL здесь. Этих примеров должно быть достаточно вам для того, чтобы начать. На данный момент поддерживаются только 2-хкратные JOIN'ы, но нет препятствий для расширения до n-кратности. Что касается внешних JOIN'ов, произвольных сортировок и фильтрации неравенствами, мои коллеги и я усиленно думаем об этом. Оставайтесь настроенными на следующие посты, в которых я объсню как сделать, чтобы это работало с зависимыми отношениями и лежащий в основе алгоритм. Попробуйте простые JOIN'ы и дайте мне знать как они вам!

Автор: Max Ross Дата: 12 февраля 2010 Оригинал статьи


комментарии [0]  | комментировать

  © 2010-2018 HIGHLOAD