Ваше решение для этого - перейти прямо к источнику и использовать низкоуровневое хранилище данных, по крайней мере, там, где у вас есть узкое место {в данном случае, ваш Список }. Я начинал строго с 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 или запрос минимизации}, но это экономит нам сотни долларов в месяц.