-
Notifications
You must be signed in to change notification settings - Fork 115
Optimize rake task + optimize index page loading #122
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
# frozen_string_literal: true | ||
|
||
require 'benchmark' | ||
|
||
time = Benchmark.realtime do | ||
system 'bin/rake utils:reload_json[fixtures/medium.json]' | ||
end | ||
|
||
puts "Finish in #{time.round(2)}" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
# Case-study оптимизации | ||
|
||
## Case 1 | ||
Метрика: | ||
|
||
`ruby benchmark/benchmark.rb` | ||
|
||
Время работы rake таски на medium.json | ||
Finish in 69.99 | ||
|
||
## Бюджет | ||
Снизить время выполнения rake задачи до 10 секунд на файле large.json | ||
|
||
## Фиксация текущей метрики | ||
`bundle exec rspec spec/task/utils_performace_spec.rb:5` | ||
|
||
## Профилировщик | ||
Воспользовался ruby-profiler | ||
|
||
`ruby profiling/ruby_prof.rb` | ||
|
||
**Точки роста:** | ||
``` | ||
99.66% 0.10% 24.91 0.02 0.00 24.89 7421 *ActiveRecord::ConnectionAdapters::DatabaseStatements#transaction | ||
15.19 0.00 0.00 15.19 4265/4265 ActiveRecord::Validations#save | ||
``` | ||
|
||
Создается множество отдельных операций к базе. Выполняются валидации, запуск транзакций и персист записей. | ||
|
||
**Решение** | ||
|
||
Пробую воспользоваться bulk_insert | ||
|
||
После оптимизации: | ||
Результат снизился до 8 секунд при загрузки данных из файла large.json | ||
|
||
## Case 2 | ||
Метрика: | ||
Загрузка страницы( /автобусы/Самара/Москва) происходит ~ 6.8 секунд, посмотрел в логи + rack-mini-profiler | ||
|
||
## Бюджет | ||
Снизить загрузку страницы до 0.4 секунд | ||
|
||
**Точки роста** | ||
N+1 в логах | ||
Много раз рендеринг паршалов. | ||
|
||
**Решение** | ||
```ruby | ||
@trips = Trip.includes(bus: :services).where(from: @from, to: @to).order(:start_time) | ||
``` | ||
|
||
Для каждого объекта Trip подгружаю объекты Bus и для каждого объекта Bus подгружаю объекты Service | ||
Также пока решил убрать паршелы и перенести во вьюху | ||
|
||
После оптимизации: | ||
Скорость загрузки страницы снизилась до 330ms ~ 0.3 секунды | ||
|
||
## Case 3 | ||
|
||
Бюджет снизизить загрузку страницы до 0.1 секунды | ||
|
||
## Профилировщик | ||
Установил PGHero | ||
В дашборде на вкладке Overview не показал что нужно создать индексы | ||
|
||
` | ||
No long running queries | ||
Connections healthy 7 | ||
Vacuuming healthy | ||
No columns near integer overflow | ||
No invalid indexes or constraints | ||
No duplicate indexes | ||
No suggested indexes | ||
No slow queries | ||
` | ||
|
||
На вкладке Queries отображает два запроса: | ||
|
||
1. Выполняется 30% от общего времени | ||
|
||
```ruby | ||
SELECT COUNT(*) FROM "trips" WHERE "trips"."from_id" = $1 AND "trips"."to_id" = $2 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. count по итогу нам вообще не нужен, тк мы грузим все данные и можем просто взять size |
||
``` | ||
|
||
2. Выполняется 16% от общего времени | ||
|
||
```ruby | ||
SELECT "trips".* FROM "trips" WHERE "trips"."from_id" = $1 AND "trips"."to_id" = $2 ORDER BY "trips"."start_time" ASC | ||
``` | ||
Добавил в explain | ||
```ruby | ||
SELECT "trips".* FROM "trips" WHERE "trips"."from_id" = 4 AND "trips"."to_id" = 2 ORDER BY "trips"."start_time" ASC | ||
``` | ||
|
||
**Решение** | ||
Добавил композитный индекс по двум полям | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
В результате: | ||
для запроса: | ||
|
||
```ruby | ||
SELECT "trips".* FROM "trips" WHERE "trips"."from_id" = 4 AND "trips"."to_id" = 2 ORDER BY "trips"."start_time" ASC | ||
``` | ||
|
||
``` | ||
Sort (cost=953.40..955.84 rows=977 width=34) | ||
Sort Key: start_time | ||
-> Bitmap Heap Scan on trips (cost=14.31..904.88 rows=977 width=34) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. лайк за explain |
||
Recheck Cond: ((from_id = 4) AND (to_id = 2)) | ||
-> Bitmap Index Scan on index_trips_on_from_id_and_to (cost=0.00..14.06 rows=977 width=0) | ||
Index Cond: ((from_id = 4) AND (to_id = 2)) | ||
``` | ||
|
||
для запроса: | ||
```ruby | ||
SELECT COUNT(*) FROM "trips" WHERE "trips"."from_id" = 4 AND "trips"."to_id" = 2 | ||
``` | ||
``` | ||
Aggregate (cost=26.27..26.29 rows=1 width=8) | ||
-> Index Only Scan using index_trips_on_from_id_and_to on trips (cost=0.29..23.83 rows=977 width=0) | ||
Index Cond: ((from_id = 4) AND (to_id = 2)) | ||
``` | ||
|
||
Скорость закрузки страницы снизилась от 144ms до 290 ms | ||
Думаю на большом объеме данных результат будет более наглядным | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. да, вообще тут на время рендеринга страницы эти запросы не так уж влияют на фоне паршлов но если бы мы подходили с точки зрения оптимизации БД - это были бы топовые запросы и нам индексы помогли бы их из этого топа скинуть |
||
|
||
## Case 4 | ||
PGHero | ||
|
||
Беру запрос который на первом месте в разделе Queries | ||
|
||
37% от общего времени | ||
```ruby | ||
SELECT "buses_services".* FROM "buses_services" WHERE "buses_services"."bus_id" IN ($1, $2,......) | ||
``` | ||
|
||
Explain отдал | ||
``` | ||
CREATE INDEX CONCURRENTLY ON buses_services (bus_id) | ||
Rows: 4534 | ||
Row progression: 4534, 5 | ||
|
||
Row estimates | ||
- bus_id (=): 5 | ||
|
||
Existing indexes | ||
- id PRIMARY | ||
``` | ||
|
||
Добавляю индекс по bus_id | ||
|
||
Скорость загрузки стариницы осталась примерно на том же уровне иногда опускаясь до 129 ms | ||
Хотя PGHero показывает по прежнему 37% от общего времени выполнение запроса на получение сервисов, решил остановиться. | ||
В бюджет почти уложился(хотел до 0.1 сек) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
https://guides.rubyonrails.org/layouts_and_rendering.html#spacer-templates
можно использовать рендеринг коллекций и даже там задать delimiter параметром
так можно и сохранить удобство паршлов и не так сильно проиграть в производительности