Движок приложения Google: низкая производительность с JDO + Datastore - PullRequest
2 голосов
/ 17 апреля 2010

У меня есть простая модель данных, которая включает

ПОЛЬЗОВАТЕЛИ: хранить основную информацию (ключ, имя, номер телефона и т. Д.)

ОТНОШЕНИЯ: опишите, например, дружба между двумя пользователями (предоставление тип_отношения + два ключа пользователя)

КОММЕНТАРИИ: опубликовано пользователями (ключ, текст комментария, идентификатор_пользователя)

У меня очень низкая производительность, например, если я пытаюсь напечатать имена всех друзей пользователя. Скажем, у пользователя 500 друзей: я могу очень легко получить список друзей user_ids в одном запросе. Но затем, чтобы вытащить имена, мне нужно совершить 500 поездок назад и вперед в хранилище данных, каждая из которых, кажется, занимает порядка 30 мс. Если бы это был SQL, я бы просто присоединился и быстро получил ответ.

Я понимаю, что существуют элементарные средства для выполнения двусторонних объединений через не принадлежащие ему отношения в упрощенной реализации JDO (как описано в http://gae -java-persistence.blogspot.com ), но они звук экспериментальный и нестандартный (например, мой код не будет работать в любой другой реализации JDO).

Хуже того, что, если я хочу вытащить все комментарии, опубликованные друзьями пользователя. Затем мне нужно получить от User -> Relation -> Comments, то есть трехстороннее соединение, которое даже экспериментально не поддерживается. Излишних накладных расходов 500 взад-вперед, чтобы получить список друзей + еще 500 поездок, чтобы увидеть, есть ли какие-либо комментарии от друзей пользователя, уже достаточно, чтобы продлить время выполнения> 30 секунд.

Как люди решают эти проблемы в реальных приложениях JDO, поддерживаемых хранилищем данных? (Или они?)

Кому-нибудь удалось извлечь удовлетворительную производительность из JDO / Datastore в такой (очень распространенной) ситуации?

-Bosh

Ответы [ 6 ]

3 голосов
/ 17 апреля 2010

Прежде всего, для объектов, к которым часто обращаются (например, пользователи), я полагаюсь на memcache. Это должно немного ускорить ваше приложение.

Если вам нужно пойти в хранилище данных, правильный способ сделать это должен быть через getObjectsById(). К сожалению, похоже, что GAE не оптимизирует этот вызов . Тем не менее, запрос contains() для ключей оптимизирован для извлечения всех объектов за одну поездку в хранилище данных, поэтому вам следует использовать:

List myFriendKeys = fetchFriendKeys();
Query query = pm.newQuery(User.class, ":p.contains(key)");
query.execute(myFriendKeys);

Вы также можете положиться на низкоуровневый API get(), который принимает несколько ключей, или действует как я и использует objectify .

Совершенно другой подход заключается в использовании фильтра равенства для свойства списка. Это будет соответствовать, если любой элемент в списке соответствует. Поэтому, если у вас есть свойство списка friendOf в вашей пользовательской сущности, вы можете выполнить один запрос friendOf == theUser. Вы можете проверить это: http://www.scribd.com/doc/16952419/Building-scalable-complex-apps-on-App-Engine

2 голосов
/ 29 мая 2010

У вас есть , чтобы минимизировать чтение БД. Это должно быть огромное внимание для любого проекта GAE - все остальное будет стоить вам. Для этого предварительно рассчитайте как можно больше, особенно часто читаемой информации. Чтобы решить проблему чтения имен 500 друзей, учтите, что вы, скорее всего, будете изменять список друзей гораздо реже, чем читаете его, поэтому при каждом изменении сохраняйте все имена в структуре, которую вы можете прочитать одним нажатием.

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

Кроме того, скорее оптимизировать по скорости, а не по размеру данных . Используйте дополнительные структуры в качестве индексов, сохраняйте объекты несколькими способами, чтобы вы могли читать их как можно быстрее. Данные дешевы, процессорное время нет.

1 голос
/ 18 апреля 2010

К сожалению, предложение Филиппа

Query query = pm.newQuery(User.class, ":p.contains(key)");

оптимизирован только для выполнения одного запроса при поиске по первичному ключу. Например, передача списка из десяти значений не первичного ключа дает следующую трассировку альтернативный текст http://img293.imageshack.us/img293/7227/slowquery.png

Я бы хотел получать групповые комментарии, например, от всех друзей пользователя. Если я сохраню список для каждого пользователя, этот список не может быть длиннее 1000 элементов (если это индексированное свойство пользователя), как описано в: http://code.google.com/appengine/docs/java/datastore/overview.html

Похоже, я здесь использую не тот набор инструментов.

-B

0 голосов
/ 24 сентября 2010

Предел индексированного свойства теперь увеличен до 5000.

Однако вы можете пойти еще выше, используя метод, описанный в http://www.scribd.com/doc/16952419/Building-scalable-complex-apps-on-App-Engine
По сути, просто есть группа дочерних сущностей для пользователя с именем UserFriends, таким образом, разделив большой список и увеличив предел до n * 5000, где n - количество сущностей UserFriends.

0 голосов
/ 11 мая 2010

Если это часто используемый запрос, вы можете подготовить индексы к нему http://code.google.com/appengine/articles/index_building.html

0 голосов
/ 17 апреля 2010

Facebook имеет 28 терабайт кеша памяти ... Однако, 500 поездок в memcached тоже не очень дешевы. Он не может быть использован для хранения десятка мелких предметов. «Denomalization» является ключом. Такие приложения не должны поддерживать специальные запросы. Вычислите и сохраните результаты напрямую для нескольких поддерживаемых запросов.

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

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

Конечно, есть много проблем с этим подходом. Для гигантских интернет-компаний у них, вероятно, нет выбора, общие механизмы запросов просто не сокращают его. Но для других? Разве вы не были бы счастливее, если бы вы могли просто использовать старую добрую СУБД?

...