Вопрос JDO Google App Engine: потребление памяти большим списком в той же группе выборок - PullRequest
2 голосов
/ 28 апреля 2011

Я создаю веб-приложение с помощью GAE и использую JDO для доступа к хранилищу данных.Для простоты, вот описание моей структуры данных:

Класс книги

public class Book 
{

....

public List<Word> getAllWords()
{
return m_lAllWordsInBook;
}

...

@Persistent(mappedBy="m_bPrintedIn", defaultFetchGroup="true")
@Element(dependent = "true")
List<Word> m_lAllWordsInBook;
}

Класс слова

public class Word
{
..
@Persistent
Book m_bPrintedIn;
}

Я не нашел способа автоматически загружать части зависимого списка (в той же группе извлечения) через JDO.Например, это означает, что даже если пользователь просматривает только 4-ю страницу книги, он все равно должен заранее получить все слова из 500-страничной книги.Тестирование на моей локальной машине проходит нормально, и я не замечаю каких-либо проблем с производительностью при выполнении вызовов Datastore для получения «Книг» со многими «Словами», но боюсь, что произойдет в масштабе.

Теперь вот мой вопрос: Что произойдет в гипотетическом сценарии, когда тысячи одновременно работающих пользователей извлекают свои собственные копии различных «Книг» (каждый со многими словами)?Разве это не будет серьезной нагрузкой на память?Лучше ли просто делать прямые запросы из хранилища данных (например, SELECT FROM WORDS WHERE BOOK_ID==BOOK_XYZ) с разумным размером Query.setRange() (например, количество слов на странице)?

Спасибо завперед.

Ответы [ 3 ]

2 голосов
/ 29 апреля 2011

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

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

0 голосов
/ 14 марта 2012

Ваше решение для этого - перейти прямо к источнику и использовать низкоуровневое хранилище данных, по крайней мере, там, где у вас есть узкое место {в данном случае, ваш Список }. Я начинал строго с JDO, потому что имеет смысл сохранять объекты объектно-ориентированными. Однако низкоуровневые DS обладают огромной властью над чистыми решениями JDO {асинхронными, пакетными операциями и запросами, по которым можно выполнять итерации при получении результатов}.

Короче говоря, ваша проблема с производительностью заключается в том, что вы храните индексированный список сущностей Word в своей Книге {фактически, индексированный список ключей, но я отступаю}. Если есть 500 слов, у вас есть 500 пунктов в вашем списке. Мало того, что вы должны загружать их все каждый раз, реальное снижение производительности {если этот список проиндексирован} состоит в том, что когда вы пишете эти объекты, он будет перекрестно индексировать все 500 этих сущностей, и каждая запись в объект Book будет стоить сотни Операции записи {и много времени}. Существует также ограничение {где-то около 1000, я думаю} общего количества операций записи на одну сущность, поэтому очень большая книга может даже не сохранить вообще.

Вместо этого вы хотите сохранить каждый элемент этого списка в отдельном объекте. Преобразуйте поле List в собственную таблицу хранилища данных с указателем на книгу, которой он принадлежит, а также на положение Word в списке, так как я предполагаю, что вам нужны эти данные}. Когда вы загружаете книгу, в ней уже не будет заполнен список Word. Затем заполните его, выполнив запрос. Если вы хотите загрузить все это, не ставьте диапазон в вашем запросе. Если вы хотите ленивую загрузку, захватите только пару сотен слов и отправьте их обратно с помощью курсора из QueryResultIterator, чтобы загрузить остальные. JDO сохраняет список в виде списка , а затем просто получает объекты по ключу и добавляет их в список. Чистые объекты получают быстрее, чем запросы, но массивные объекты List в объектах мучительно медленны и подвержены ошибкам {google: appengine exponential Explo}}.

Поскольку я прочитал в комментариях, что вы кэшируете книги на сессии, вы можете выполнить свою ленивую загрузку в фоновом режиме. Используйте один RPC, чтобы получить книгу, и первые X слов, которые вам нужно показать, затем попросите клиента запустить фоновые RPC, чтобы загрузить остальные результаты в сеанс. Таким образом, первый запрос будет немного медленным {вместо убер-медленного}, и все последующие пользовательские запросы уже будут предварительно загружать и кэшировать свои результаты {uber-fast}.

Может показаться нелогичным хранить больше сущностей для лучшей производительности, но я обещаю вам, этот метод невероятно быстр в appengine. Я могу выполнить 20 быстрых запросов за меньшее время в реальном времени и с меньшими затратами, чем выполнение одного очень медленного запроса. При большем количестве объектов с меньшим числом индексов ваши операции записи и удаления будут выполняться намного быстрее, и если вы будете использовать низкоуровневые DS, вы можете асинхронно загружать и пакетировать свои объекты или даже выполнять пять запросов одновременно {требуется некоторая хакерская атака для обнаружения запросы блокируют и избегают платить часы экземпляров, чтобы ждать запросов ... но это работает и экономит нам кучу денег}.

JDO в любом случае внутренне преобразует ваш список сущностей в список ключей. Я предлагаю удалить этот список ключей из вашей сущности и дать ему собственную таблицу, чтобы вы могли контролировать, сколько загружается. Эта таблица отличается от вашей существующей таблицы Word тем, что она относится к определенному слову в определенной книге. Я не уверен, но я думаю, что JDO будет использовать Книгу в качестве родителя группы сущностей в Pojo Word. Использование группы сущностей для кодирования родительской книги, а не поля книги, означает, что вы можете извлечь ключ книги из запроса только ключей, но тот факт, что вы, вероятно, уже знаете искомый идентификатор книги, означает, что вы можете использовать поле или поле. родительский ключ в вашем запросе. Если в JDO ваши данные уже отформатированы с использованием родительских ключей, используйте их, чтобы избежать переноса каких-либо данных.

Посмотрите на структуру ваших данных, созданных JDO, и спросите себя, можете ли вы загружать и запрашивать эти данные более эффективно. Если да, то используйте низкоуровневую магию, чтобы обойти громоздкую методологию «загрузи все сразу». Если нет, подумайте о реструктуризации ваших данных, чтобы вы могли получить к ним как можно более эффективную информацию. Перемещение списка в собственную таблицу поиска разблокирует ключ appengine к масштабируемости: множество и множество крошечных запросов. Appengine любит маленькие запросы и ненавидит гигантские сгустки памяти, которые долго хранятся. Чем больше вы храните в памяти и чем дольше вы сохраняете ее, тем ниже производительность ваших экземпляров. Я передаю все свои данные в низкоуровневых DS, и мы платим ~ 30 часов в день, чтобы шесть экземпляров работали и работали постоянно. Я не уверен, какое именно колдовство вызвало такое сокращение расходов {threadsafe, async-all или запрос минимизации}, но это экономит нам сотни долларов в месяц.

0 голосов
/ 29 апреля 2011

Я думаю, что вы ищете ленивую загрузку полей контейнера.Загляните в Datanucleus - Контейнерные поля: Ленивая загрузка

...