Статьи

Распространенные проблемы с приложениями и способы их устранения: проблема выбора N + 1

В AppDynamics мы получаем возможность увидеть внутреннюю работу многих приложений. Хотя все эти приложения кажутся довольно разными для своих конечных пользователей, то, что находится под капотом, обычно не сильно отличается (извините, ваше приложение не уникальная снежинка в конце концов). Все они имеют похожие сервис-ориентированные архитектуры, использующие различные базы данных, кэши и очереди. Это относится и к проблемам производительности, с которыми сталкиваются эти приложения, и к анти-шаблонам, которые их вызывают. Те же проблемы обнаруживаются в приложениях для высокоскоростной торговли, на сайтах электронной коммерции, в мобильных приложениях и онлайн-играх — поэтому мы подумали, что соберем некоторые из наиболее распространенных проблем с производительностью в серии блогов, чтобы показать вам, как найти, исправить и предотвратить их. В этом посте мы рассмотрим довольно распространенную проблему, которую сложно обнаружить в большом приложении:проблема выбора N + 1.

Что это?

Проблема N + 1 — это анти-шаблон производительности, в котором приложение выполняет N + 1 вызовов базы данных (где N — количество выбранных объектов). Как и большинство анти-паттернов, это не обязательно является проблемой само по себе, но при определенных обстоятельствах (например, когда N велико) это приведет к снижению производительности, делая сотни или даже тысячи обращений к базе данных за одну бизнес-транзакцию.

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

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

SELECT id FROM Parent

и затем выполните запрос для каждой записи:

SELECT * FROM Child WHERE parent_id = ?

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

Как найти это с AppDynamics

Like I said, this isn’t always a problem, but when it is it can be hard to find. The database will probably look normal to the DBA – they won’t see any long-running queries, and the CPU may look fine under normal load. The best way to find a problem like this is to use a performance monitoring solution that allows you to drill into a particular business transaction (user request) and see what code is executed and how it is accessing the database.

Here’s an example from an AppDynamics customer:

The best part about AppDynamics is its request snapshots. They not only allow us to troubleshoot performance problems faster – they also allow us to perfect this code. “AppDynamics allows us to see what is going on and identify the issues that could be refactored and made faster,” he said. “It lets us bridge the gap between anecdotes from users and actual, actionable information.” – Cornell University

How to Fix It

This problem happens most often when you’re using a persistence engine or an object/relational mapper (ORM) like Hibernate and you’re using lazy loading. Be sure to understand the defaults for any object-relational mapper before you begin using them.

If you’re just writing raw SQL, you may want to fetch all your data at once and then join the two sets of records, like this:

SELECT p.id, p.name FROM Parent p
LEFT OUTER JOIN child c ON p.id = c.parent_id
INNER JOIN grandchild g ON c.id = g.parent_id

Optimizing Database Access

Many times, how an application accesses the database will be a focus of optimization. Stay tuned for our next blog post in the series, where we will discuss the importance of caching database access. If you enjoyed this blog post, check out our e-book on Java performance problems.

Find out more about AppDynamics Pro and get started optimizing your application with a free 15 day trial.

As always, please feel free to comment if you think I have missed something or if you have a request for content in an upcoming post.

Thoughts? Let us know on Twitter @AppDynamics!