Движок приложений Google: низкая производительность с JDO + Datastore

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

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

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

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

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

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

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

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

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

-Боша


person Bosh    schedule 17.04.2010    source источник


Ответы (6)


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

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

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

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

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

person Community    schedule 17.04.2010
comment
Если AppEngine не поддерживает вызовы contains () в полях Collection, похоже, что у вас есть лучшее решение. - person Gunslinger47; 17.04.2010

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

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

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

person Richard Watson    schedule 29.05.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

person Bosh    schedule 18.04.2010

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

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

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

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

person irreputable    schedule 17.04.2010

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

person Gaurav    schedule 11.05.2010

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

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

person Strom    schedule 24.09.2010