2015-10-21 2 views
2

Мы постоянно обрабатываем тысячи субъектов времени, и у нас есть проблемы с производительностью, которые считывают много данных из хранилища данных, процессы являются вычислительными и не вызывают проблем. Мы создали синтетический тест, имитирующий реальный серверный трафик, где мы тестируем 25 тыс. Элит.GAE Datastore read performance

Мы используем среду выполнения Java и Objectify (5.1.1 и 5.1.8) для доступа к хранилищу данных.

Субъект

@Entity(name="logs") 
@Cache 
public class Log { 
    @Id 
    public Long id; 

    @Index 
    public Ref<User> user; 

    public String deviceId; 
    public String nonce; 
    public String version; 

    public String data; 

    @Index 
    public Date timestamp; 

    @OnSave 
    private void prePersist() { 
     if (timestamp == null) { 
      timestamp = new Date(); 
     } 
    } 
} 

Запрос

query = ofy().load().type(Log.class). 
     filter("timestamp >", startDate). 
     order("timestamp"). 
     limit(25000); 

Мы пытались различную нагрузку субъектов. Сначала query.list(), затем ofy().load().keys(query.keys()), поэтому поиск будет проходить через memcache GAE, но результаты будут одинаковыми. Получение объектов размером 25 тыс. Занимает около 8 секунд (измеряется через System.nanoTime()). В случае query.list() этот вызов выполняется быстро, но итерация по объектам происходит медленно. Похоже, что объект извлекается из хранилища данных в тот самый момент, а не в query.list(). Все это простой сервлет на внешнем экземпляре F4 с выделенным memcache, без задачи.

Чтение объектов 25k - это просто тест, чтобы получить некоторые цифры о производительности нашей серверной реализации. В реальном мире мы ожидаем читать до 500 тыс. Сущностей одновременно, можно ли это сделать до 30-60 секунд с хранилищем данных GAE и выделенным memcache? Через 2 года это могут быть миллионы юридических лиц.

Другой проблемой является ограниченная оперативная память, но она разрешима через управляемые виртуальные машины GAE или GCE.

Вопросы являются тем, что является самым быстрым способом получения временного объекта из выделенного хранилища данных + выделенного memcache с Objectify. Похоже, memcache не помогает Objectify в нашем случае. Memcache содержит десятки тысяч объектов Objectify внутри, но время загрузки такое же, как и с пустой memcache. Лучшая практика Objectify's/Datastore заключается в том, чтобы сделать пакетные операции, как это сделать? Является ли Objectify делать это под капотом с нашей сущностью и запросом или мы должны что-то изменить? Может ли API-интерфейс Datastore низкого уровня помочь нам улучшить производительность чтения? Спасибо.

EDIT Мы уже работаем над объединением журналов, поэтому каждый объект журнала будет содержать несколько текущих журналов. Это даст нам примерно 10-кратное усовершенствование тростника, которого все еще недостаточно для сотен тысяч записей.

+2

Я пытаюсь представить сценарий, в котором вам нужно сразу же прочитать все свои ключи. Что именно вы делаете? –

+0

Нам нужно прочитать журналы за последние несколько минут, затем загрузить активных пользователей на основе этих журналов, а затем выполнить итерацию по журналам на пользователя и обновить сущность пользователя на основе данных в журналах. Обработка выполняется быстро (задачи + потоки), но загрузка журналов и пользователей происходит медленно. Мы уже работаем над объединением журналов, поэтому каждый объект журнала будет содержать несколько текущих журналов. – shelll

+1

Ну, я действительно не понимаю ваш случай использования, но по какой-либо причине вы не можете обновлять объект непосредственно в действии, а не хранить журнал и затем разбирать его? Дело в том, что, как вы обнаружили, GAE не подходит для такого использования; массовые операции лучше всего делать медленно, автономно, с помощью чего-то вроде map-reduce. –

ответ

1

Это решение вряд ли будет масштабироваться так, как вы хотите.

Querying для объектов @Cache по умолчанию используется «гибридный» запрос только на ключ (который быстро разгорается), за которым следует пакет (который сравнительно медленный). Если кеш теплый, это может работать довольно хорошо, но, вероятно, не в масштабе, о котором вы говорите. И в конце концов, даже с выделенной memcache, кеш будет сброшен - тогда ваши действия, вероятно, будут тайминга и не будут выполняться несколько раз, пока кеш не будет снова разогреваться.

Вы можете отключить эту гибридную функцию: ofy().load().hybrid(false) или просто удалить аннотацию @Cache. Обычный запрос будет работать значительно лучше с холодным кешем. Вы также можете попробовать изменить размер chunk() на нечто большее. Значение по умолчанию - что-то маленькое, как 20.

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

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

+0

Мы попробуем 'chunk()', но похоже, что это не будет масштабироваться, как вы сказали в начале. Вероятно, нам понадобится обработка в режиме реального времени по мере создания журналов. – shelll

+0

Вы не показали, как вы повторяете результаты. Если вы просто делаете foreach, вы найдете гораздо больший размер куска, который сильно изменит ситуацию. Потенциально вы также можете использовать проекционный запрос для поиска пользователей, а затем поставить очередь на задание для обработки каждого пользователя для вашего временного окна независимо. – Nick

+0

Установка большего фрагмента, даже максимально возможное значение ничего не изменило. В итоге мы делаем вычисления в режиме реального времени для стоимости дополнительного мультииндекса и более длинного запроса. Как вы сказали, хранилище данных недостаточно для массового чтения. – shelll