sergii tsypanov: "tricky enterprise"

44
www.luxoft.com TRICKY ENTERPRISE

Upload: logeeknightukraine

Post on 08-Jan-2017

92 views

Category:

Technology


1 download

TRANSCRIPT

Page 1: Sergii Tsypanov: "Tricky enterprise"

www.luxoft.com

TRICKY ENTERPRISE

Page 2: Sergii Tsypanov: "Tricky enterprise"

www.luxoft.com

Пара слов обо мне

- работаю в «Люксофте» с 2013 г.

- пишу на Java

- люблю разбираться с проблемами производительности

Связаться со мной можно

- написав на корпоративную почту [email protected]

- написав на личную почту [email protected]

- написав ВК https://vk.com/sergei.tsypanov

- написав ФБ https://www.facebook.com/sergei.tsypanov

Page 3: Sergii Tsypanov: "Tricky enterprise"

www.luxoft.com

Вычислительная сложностьКаноническое определение звучит так:

Вычислительная сложность — понятие в информатике и теории алгоритмов, обозначающее функцию зависимости объёма работы, которая выполняется некоторым алгоритмом, от размера входных данных.

https://ru.wikipedia.org/wiki/Вычислительная_сложность

Page 4: Sergii Tsypanov: "Tricky enterprise"

www.luxoft.com

Итак, разминка

Ваши мысли?

Page 5: Sergii Tsypanov: "Tricky enterprise"

www.luxoft.com

А так?

Для пропуска if-блока необходимо и достаточно, чтобы один из операндов был false

Page 6: Sergii Tsypanov: "Tricky enterprise"

www.luxoft.com

Меняем порядок операндов

… а запрос обернём в предикат (можно и в метод, но предикат – это функционально, стильно, модно, молодёжно)

Page 7: Sergii Tsypanov: "Tricky enterprise"

www.luxoft.com

Пример посерьёзнее

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

Page 8: Sergii Tsypanov: "Tricky enterprise"

www.luxoft.com

Почему так? Или пятиминутка скучной теории

В худшем случае мы ничего не теряем

В лучшем случае достаточно вызвать лишь одни метод

Page 9: Sergii Tsypanov: "Tricky enterprise"

www.luxoft.com

Так лучше

Page 10: Sergii Tsypanov: "Tricky enterprise"

www.luxoft.com

1 ПРАВИЛО КЛУБА:

ЕСЛИ МЕДЛЕННОЕ ДЕЙСТВИЕ МОЖНО ОТЛОЖИТЬ – ОТЛОЖИ ЕГО.ВОЗМОЖНО, ДЕЛАТЬ ЕГО ВОВСЕ НЕ ПРИДЁТСЯ.

Действия лучше выполнять в порядке возрастания их сложности.

Page 11: Sergii Tsypanov: "Tricky enterprise"

www.luxoft.com

Едем дальше

«Понабирали по объявлению» (с)

Page 12: Sergii Tsypanov: "Tricky enterprise"

www.luxoft.com

Присмотрись внимательнее

Количество обращений к репозиторию равно количеству DTO в списке

Чтобы отбросить повторяющиеся сущности, используется Set

Множественные запросы к БД?

HashSet там где достаточно ArrayList-а?

Не отчаивайся, %USERNAME% $USER, выход есть

Page 13: Sergii Tsypanov: "Tricky enterprise"

www.luxoft.com

Грузите апельсины бочками

Посчитаем для списка из 10 ID…

Page 14: Sergii Tsypanov: "Tricky enterprise"

www.luxoft.com

Разница почти в 10 раз

Запросы в цикле

Один запрос

Page 15: Sergii Tsypanov: "Tricky enterprise"

www.luxoft.com

Почему так? Или пятиминутка скучной теории

Что происходит под капотом?

1 Получаем сессию2 Открываем транзакцию

3 Работаем

4 Завершаем транзакцию

5 Закрываем сессию

Это шаблон session-per-request (https://developer.jboss.org/wiki/Sessionsandtransactions)

Page 16: Sergii Tsypanov: "Tricky enterprise"

www.luxoft.com

Почувствуйте разницу…

Пять указанных шагов выполняются при каждом вызове repository.findOne(),

т. е. вычислительная сложность равна О(n)

При вызове repository.findAll(Iterable<ID>)

пять указанных шагов выполняются всегда один раз,

т. е. вычислительная сложность равна О(1)

Page 17: Sergii Tsypanov: "Tricky enterprise"

www.luxoft.com

2 ПРАВИЛО КЛУБА:

ГРАБЬ, ВОРУЙ, …

СОБИРАЙ И ВЛАСТВУЙ.

Однотипные действия, которые выполняются в цикле, часто можно выполнить за один проход, особенно когда речь идёт о запросах в БД.

Page 18: Sergii Tsypanov: "Tricky enterprise"

www.luxoft.com

Внезапно вернёмся к первому примеру

Таким мы его запомнили:

Page 19: Sergii Tsypanov: "Tricky enterprise"

www.luxoft.com

А что если…

…его можно сделать ещё чуточку лучше?

Самую малость…

Page 20: Sergii Tsypanov: "Tricky enterprise"

www.luxoft.com

А что это у нас в третьей строке?

У свеженайденной сущности мы берём сущность-дочку, а потом – сущность-внучку.

…дочка за внучку, внучка за Жучку, и вытащили они – нет, не репку, – а полновесный OutOfMemoryError…

Page 21: Sergii Tsypanov: "Tricky enterprise"

www.luxoft.com

Шутка,

OutOfMemoryError на таком примере не выловить, однако это вполне жизненный пример анти-паттерна под замысловатым названием Circuitous Treasure Hunt.

Его определение: Occurs when an object must look in several places to find the information that it needs. If a large amount of processing is required for each look (i. e. a database access), performance will suffer.

Способ борьбы прост: не размазывайте логику.

Page 22: Sergii Tsypanov: "Tricky enterprise"

www.luxoft.com

Выход есть

Достанем рейтинг напрямую, минуя дочку и внучку

Подправим предикат

Page 23: Sergii Tsypanov: "Tricky enterprise"

www.luxoft.com

Измерим

До

После

Page 24: Sergii Tsypanov: "Tricky enterprise"

www.luxoft.com

Чё, 90 миллисекунд? Серьёзно?

Page 25: Sergii Tsypanov: "Tricky enterprise"

www.luxoft.com

Пятиминутка арифметической объективности

1 проход = 90 мс ≈ 1 фигня

10 проходов= 900 мс ≈ 1 с

100 проходов= 9000 мс = 9 с

1000 проходов= 90000 мс = 90 с = 1,5 минуты

С уважением,

ваш Кэп

Page 26: Sergii Tsypanov: "Tricky enterprise"

www.luxoft.com

3 ПРАВИЛО КЛУБА:

КОСИ И ЗАБИВАЙ …

НЕ ДЕЛАЙ ЛИШНЮЮ РАБОТУ ТАМ, ГДЕ ЕЁ МОЖНО НЕ ДЕЛАТЬ.

ДЕЛАЙ ВСЁ В ОДНОМ МЕСТЕ.

Page 27: Sergii Tsypanov: "Tricky enterprise"

www.luxoft.com

Несложный, но важный пример

Page 28: Sergii Tsypanov: "Tricky enterprise"

www.luxoft.com

Что скажете?

отсеиваем лишнее

и ещё раз отсеиваем лишнее

А теперь, внимание, вопрос.

Что мешает выбрать нужные данные сразу?

Можете ли вы с первого взгляда понять, что делают последние 3 строки?

Page 29: Sergii Tsypanov: "Tricky enterprise"

www.luxoft.com

Переложим логику на мощные плечи БД

Обратите внимание, насколько лаконичнее и выразительнее стал код

Page 30: Sergii Tsypanov: "Tricky enterprise"

www.luxoft.com

4 ПРАВИЛО КЛУБА:

НЕ ДЕЛАЙТЕ ТОГО, ЧТО МОЖЕТ ЗА ВАС СДЕЛАТЬ БД.

Если задача состоит в поиске данных и отсеивании лишнего – делайте это с помощью базы. Она специально заточена под это.

Page 31: Sergii Tsypanov: "Tricky enterprise"

www.luxoft.com

На мой взгляд, самый необычный пример

Page 32: Sergii Tsypanov: "Tricky enterprise"

www.luxoft.com

Внезапно

Транзакция открывается как для «хороших», так и для «плохих» данных.Если данные плохие, то транзакция тут же откатывается.

А зачем нам в таком случае транзакция?

Achtung !!!

Page 33: Sergii Tsypanov: "Tricky enterprise"

www.luxoft.com

Что можно с этим сделать?Испечём «Наполеон»: всю транзакционную логику перенесём во внутренний сервис, а всю логику проверки оставим во внешнем сервисе:

Транзакция открывается только тогда, когда она необходима

Page 34: Sergii Tsypanov: "Tricky enterprise"

www.luxoft.com

Считаем

До

После

Page 35: Sergii Tsypanov: "Tricky enterprise"

www.luxoft.com

«Наполеон», он же шаблон Service Layers

https://en.wikipedia.org/wiki/Service_layers_pattern

• проще писать

• легче читать

• проще тестировать

• легче поддерживать

• проще рефакторить

И это просто вкусно

Page 36: Sergii Tsypanov: "Tricky enterprise"

www.luxoft.com

5 ПРАВИЛО КЛУБА:

РАЗДЕЛЯЙ И ВЛАСТВУЙ.

УВЕЛИЧИВ КОЛИЧЕСТВО СЛОЁВ, ПРИЛОЖЕНИЕ ЛЕГЧЕ ТЕСТИРОВАТЬ И ОНО (В НЕКОТОРЫХ СЛУЧАЯХ) СТАНОВИТСЯ БОЛЕЕ ОТЗЫВЧИВЫМ.

Page 37: Sergii Tsypanov: "Tricky enterprise"

www.luxoft.com

Лирическо-логическое отступление[режим-капитана-очевидности-вкл]

В JVM существует сборщик мусора (СМ), который включается, когда приложению не хватает памяти.

Иногда работа СМ приводит к stop-the-world задержкам в работе приложения.СМ освобождает память и не даёт приложению голодать, но всё имеет свою цену.В обмен на помощь в освобождении памяти СМ берёт дань процессорным временем.

Таким образом, если доступная вычислительная мощность равна 1, а СМ отнимает у нас х, то приложению остаётся лишь 1 - х вычислительной мощности.

Отсюда вывод: производительность можно повысить, если уменьшить дань, забираемую СМ

[режим-капитана-очевидности-выкл]

Page 38: Sergii Tsypanov: "Tricky enterprise"

www.luxoft.com

Немного отвлечёмся от вычислительной сложностиНебольшое пояснение: это общий предок для около 50 сущностей одного из наших приложений.Метод includeInAudit() вызывается при изменении значений некоторых полей, и вызывается он очень часто

Page 39: Sergii Tsypanov: "Tricky enterprise"

www.luxoft.com

Вернёмся к нашим баранамНа основе реальных событий: в результате 1 действия 1 пользователя данный метод вызывается 3,5 тыс. раз

И при каждом вызове метода создаётся HashSet…

Page 40: Sergii Tsypanov: "Tricky enterprise"

www.luxoft.com

Пятиминутка арифметики:

Пустой HashSet в 64-разрядной JVM «весит» 240 байт*.

Путём сложнейших вычислений определим, что 3,5 тыс. пустых (берём по нижней границе) HashSet-ов «весят» 820 кБ.

И это 1 действие 1 пользователя…

* Накладные расходы памяти у коллекций https://habrahabr.ru/post/159557/

Page 41: Sergii Tsypanov: "Tricky enterprise"

www.luxoft.com

Присмотримся внимательнее…и обнаружим, что по завершении цикла переменная drafts может иметь только 4 состояния:

• пустая коллекция

• коллекция, содержащая OLD_DRAFT

• коллекция, содержащая NEW_DRAFT

• коллекция, содержащая обе записи

Page 42: Sergii Tsypanov: "Tricky enterprise"

www.luxoft.com

Перепишем определим все

возможные состояния

вместо добавления объектов

используем уже созданные коллекции

Page 43: Sergii Tsypanov: "Tricky enterprise"

www.luxoft.com

6 ПРАВИЛО КЛУБА:

ЧИСТО НЕ ТАМ, ГДЕ УБИРАЮТ,А ТАМ, ГДЕ НЕ МУСОРЯТ.

Кэшируйте всё, что можно и не превращайте приложение в помойку.

Page 44: Sergii Tsypanov: "Tricky enterprise"

www.luxoft.com

СПАСИБО ЗА ВНИМАНИЕ

И ДЕРЖИТЕ ПОРОХ СУХИМ БАЗУ ПРОГРЕТОЙ