Yii Framework Forum: Выборка Activerecord По Частям - Yii Framework Forum

Jump to content

Page 1 of 1
  • You cannot start a new topic
  • You cannot reply to this topic

Выборка Activerecord По Частям Rate Topic: -----

#1 User is offline   bFree 

  • Newbie
  • Yii
  • Group: Members
  • Posts: 9
  • Joined: 25-November 12

Posted 12 September 2013 - 03:49 AM

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

Но у нас по крону ночью должно обрабатываться около 100 тысяч записей из БД.
Если делать выборку такого числа записей с помощью AR, то это сжирает больше гигабайта оперативки и вообще как-то не хорошо, ведь findAll() сначала выберет всю инфу, потом нагенерирует все 100 тыс. моделей и все это будет жить в памяти.

В голову приходит несколько вариантов оптимизации.
1. Отказаться в системных операциях от AR, написать запросы вручную, обработать вручную. Будет относительно экономично, но теряется вся прелесть AR.
2. Выбирать по частям (например, с LIMIT или с BETWEEN ID), обрабатывать по частям. Тогда за раз будет создаваться только некоторая часть моделей, которые в цикле будут обработаны и потом убиты сборщиком мусора. В итоге пиковое потребление памяти упадет.

Есть ли в Yii уже какие-либо готовые способы решения задачи?
0

#2 User is offline   Charger 

  • Advanced Member
  • PipPipPip
  • Yii
  • Group: Members
  • Posts: 317
  • Joined: 03-September 11

Posted 12 September 2013 - 03:54 AM

Скорее всего лучше будет обрабатывать по частям используя LIMIT.
А в чем состоит обработка? Можно ведь получать данные в виде массива (без создания AR).
0

#3 User is offline   bFree 

  • Newbie
  • Yii
  • Group: Members
  • Posts: 9
  • Joined: 25-November 12

Posted 12 September 2013 - 04:11 AM

View PostCharger, on 12 September 2013 - 03:54 AM, said:

Скорее всего лучше будет обрабатывать по частям используя LIMIT.
А в чем состоит обработка? Можно ведь получать данные в виде массива (без создания AR).


Там обработка пользователей с выборкой по многим условиям и relations. Причем все условия, фильтры и параметры заданы, как и положено, во всевозможных scope.

Или я не правильно Вас понял?
0

#4 User is offline   ineersa 

  • Standard Member
  • PipPip
  • Yii
  • Group: Members
  • Posts: 282
  • Joined: 15-April 13
  • Location:Ukraine

Posted 12 September 2013 - 04:50 AM

Ну а кто вас заставляет делать findAll()? - это во первых.

Во вторых AR не предназначен для highload, он изначально в разы медленнее и прожорливее. Делайте с помощью DAO или просто в массив queryAll(). Если вы обрабатываете 100к записей за ночь - это значит что нужно было думать хорошенько перед тем как цеплять все к AR.

Тут вступает в силу требования которые вам нужны:
а) скорость и оптимальная нагрузка - DAO или просто выборка данных в массив.
б) меньше скорости и больше памяти - AR и выборка в массив.
в) меньше скорости и минимальные затраты памяти - выборка по 1 записи с помощью DAO.
г) самая маленькая скорости и небольшие затраты памяти - выборка по 1 записи AR.

Сам совершил похожую ошибку в прошлом проекте. Пришлось перековырять половину AR для оптимизаций, с некоторыми таблицами пришлось совсем убрать. В тяжелых крон задачах - AR нет, кроме одной по сбору статистики, которая выполняется 1 раз в день приблизительно за 2 минуты с выборкой findAll() ~80к записей. Почему - пока не дошли руки до нее.

Чем выше уровень абстракции - тем больше затраты ресурсов.

Да и кстати 1гб это очень много, мой скрипт сбора статистики с выборкой на 80к и последующей обработкой с выборкой еще из 2 таблиц укладывается в 150-200 мб.
0

#5 User is offline   bFree 

  • Newbie
  • Yii
  • Group: Members
  • Posts: 9
  • Joined: 25-November 12

Posted 12 September 2013 - 04:58 AM

View Postineersa, on 12 September 2013 - 04:50 AM, said:

Ну а кто вас заставляет делать findAll()? - это во первых.

Во вторых AR не предназначен для highload, он изначально в разы медленнее и прожорливее. Делайте с помощью DAO или просто в массив queryAll(). Если вы обрабатываете 100к записей за ночь - это значит что нужно было думать хорошенько перед тем как цеплять все к AR.

Тут вступает в силу требования которые вам нужны:
а) скорость и оптимальная нагрузка - DAO или просто выборка данных в массив.
б) меньше скорости и больше памяти - AR и выборка в массив.
в) меньше скорости и минимальные затраты памяти - выборка по 1 записи с помощью DAO.
г) самая маленькая скорости и небольшие затраты памяти - выборка по 1 записи AR.

Сам совершил похожую ошибку в прошлом проекте. Пришлось перековырять половину AR для оптимизаций, с некоторыми таблицами пришлось совсем убрать. В тяжелых крон задачах - AR нет, кроме одной по сбору статистики, которая выполняется 1 раз в день приблизительно за 2 минуты с выборкой findAll() ~80к записей. Почему - пока не дошли руки до нее.

Чем выше уровень абстракции - тем больше затраты ресурсов.


Оно и понятно. Я с Вами не спорю.
Задача выполняется по крону раз в сутки и время ее выполнения не очень критично. Нужно просто, что бы она не клала сервер на время своего выполнения.

И если я обрабатываю 100к записей за ночь - это не значит, что нужн оотказаться от AR в проекте. 99% операций в системе происходят 1-2 записями и их связями, что явно укладывается в AR.

А есть ли способ на основе Scopes в AR создать кастомную выборку?
Самое большое, чего я опасаюсь - это дублирования кусков SQL по всему проекту. В этом случае правка бизнес-логики превращается в ад и именно решения этой проблемы ждешь от AR.

По поводу 1Гб - там просто много колонок нужно выбирать в каждой таблице, которая связана с моделью.
0

#6 User is offline   ineersa 

  • Standard Member
  • PipPip
  • Yii
  • Group: Members
  • Posts: 282
  • Joined: 15-April 13
  • Location:Ukraine

Posted 12 September 2013 - 06:13 AM

Дробите на куски - уменьшите потребление памяти.
Проведите эксперимент с выборкой по 1 - через find(). В этом случае нагрузка перейдет к БД, посмотрите устроит ли вас это.

По поводу scopes - что значит кастомная выборка? Вы записываете туда то что часто используете - например статус или роль, и потом делаете выборку по типу:
$users=User::model()->status()->role()->findAll();

Для меня это и есть уже кастомной выборкой. Тут вы действительно избегаете дублирования, например если изменятся константы статуса - не нужно их менять везде.

Если честно я предпочитаю немного другие методы для избежания таких проблем. Я делаю мелкие функции в моделях для выборок из БД. Но это личное мнение.

Да и еще по личному опыту - избегайте join-ов, особенно на больших таблицах - они часто и есть причина непомерного расходования памяти и времени.

Бд сейчас на старом проэкте выросла до 20 гб, и 50 миллионов записей в 1 таблице. Это сравнительно немного, но когда нативный AR уже на 30м начинает захлебываться и делать выборки по 5 минут - заставляет задуматься об его уместности.
1

#7 User is offline   bFree 

  • Newbie
  • Yii
  • Group: Members
  • Posts: 9
  • Joined: 25-November 12

Posted 12 September 2013 - 06:56 AM

View Postineersa, on 12 September 2013 - 06:13 AM, said:

Дробите на куски - уменьшите потребление памяти.
Проведите эксперимент с выборкой по 1 - через find(). В этом случае нагрузка перейдет к БД, посмотрите устроит ли вас это.

По поводу scopes - что значит кастомная выборка? Вы записываете туда то что часто используете - например статус или роль, и потом делаете выборку по типу:
$users=User::model()->status()->role()->findAll();

Для меня это и есть уже кастомной выборкой. Тут вы действительно избегаете дублирования, например если изменятся константы статуса - не нужно их менять везде.

Если честно я предпочитаю немного другие методы для избежания таких проблем. Я делаю мелкие функции в моделях для выборок из БД. Но это личное мнение.

Да и еще по личному опыту - избегайте join-ов, особенно на больших таблицах - они часто и есть причина непомерного расходования памяти и времени.

Бд сейчас на старом проэкте выросла до 20 гб, и 50 миллионов записей в 1 таблице. Это сравнительно немного, но когда нативный AR уже на 30м начинает захлебываться и делать выборки по 5 минут - заставляет задуматься об его уместности.


Да, я измеряю сейчас производительность и думаю, что найду золотую середину для конкретной задачи.
Собственно я спрашивал, есть ли в yii, например что-то готовое для реализации, например "постраничной" выборки. Как-то так:
$ModelIterator = Model::model()->mySuperScopes()->findAllLimited(200); // вернет итератор, возвращающий по 200 моделей
foreach($ModelIterator as $models) {
    // Получили новый кусок моделей и работаем с ними
}


Да, Scopes примерно так используются, только внтури более сложные условия и scope с параметрами.
Получается практически тоже самое, что делать статические методы вида User::getNeededForMeUsers();
Только у scope преимущество - позволяет использовать себя в качестве параметров при выборке relations, что очень гибко


Quote

Да и еще по личному опыту - избегайте join-ов

Я всегда проверяю все запросы, которые генерирует AR вручную с помощью EXPLAIN и слежу, что бы везде джойны были по индексам. Так что это не слишком большая проблема. И я всегда представляю, какой запрос сгенерирует AR ;)

Ну что касается 50 миллионов записей.. выбирать 30 миллионов с помощью AR точно не следует =)
0

#8 User is offline   ORey 

  • Elite Member
  • PipPipPipPipPip
  • Yii
  • Group: Members
  • Posts: 1,694
  • Joined: 20-April 09
  • Location:Moscow, Russia

Posted 12 September 2013 - 07:17 AM

Пользуясь случаем, попеарю Yii2, в котором можно так:
$list = MyModel::find()->select(array('id', 'name')->where(..)->limit(...)->asArray()->all();

И никакого AR! :)
God is real unless declared as integer
1

#9 User is offline   bFree 

  • Newbie
  • Yii
  • Group: Members
  • Posts: 9
  • Joined: 25-November 12

Posted 12 September 2013 - 07:57 AM

View PostORey, on 12 September 2013 - 07:17 AM, said:

Пользуясь случаем, попеарю Yii2, в котором можно так:
$list = MyModel::find()->select(array('id', 'name')->where(..)->limit(...)->asArray()->all();

И никакого AR! :)

К великому моему сожалению, Yii2, как говорится, сейчас not for production use ;(
0

#10 User is offline   bFree 

  • Newbie
  • Yii
  • Group: Members
  • Posts: 9
  • Joined: 25-November 12

Posted 12 September 2013 - 07:59 AM

Кстати, заметил, что если я выберу через голое PDO те же 100к записей и , например, сохраню все строки в массив, то это займет ~600 Mb. Отсюда вывод, что большую часть оперативки жрет PDO, который хранит в себе полный результат выборки из мускуля.
0

#11 User is offline   ineersa 

  • Standard Member
  • PipPip
  • Yii
  • Group: Members
  • Posts: 282
  • Joined: 15-April 13
  • Location:Ukraine

Posted 12 September 2013 - 08:27 AM

Ну да. Это значит что ваше полотно с джоинами немного больше структур activerecord :rolleyes:

Если вам скорость неважна делайте выборку по 1. Нагрузка на БД должна быть приемлима, а затраты памяти минимальны.

Yii 2 действительно сыроват.
0

#12 User is offline   bFree 

  • Newbie
  • Yii
  • Group: Members
  • Posts: 9
  • Joined: 25-November 12

Posted 19 September 2013 - 05:40 AM

Ранее я писал, что хорошо бы, что б был итератор по моделям:

View PostbFree, on 12 September 2013 - 06:56 AM, said:

Да, я измеряю сейчас производительность и думаю, что найду золотую середину для конкретной задачи.
Собственно я спрашивал, есть ли в yii, например что-то готовое для реализации, например "постраничной" выборки. Как-то так:
$ModelIterator = Model::model()->mySuperScopes()->findAllLimited(200); // вернет итератор, возвращающий по 200 моделей
foreach($ModelIterator as $models) {
    // Получили новый кусок моделей и работаем с ними
}



О боже мой! Оказывается в версии 1.13 это было сделано. Разработчики yii опередили меня =(
Кто столкнется с подобной задачей, велкам: http://yiiframework....ovider.iterator
0

#13 User is offline   yan 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 22
  • Joined: 29-March 11
  • Location:Russia, Ufa

Posted 22 September 2013 - 01:03 PM

Плюс к итераторам - если в запросах не используются джоины их можно легко превратить в sql и получить результат в виде простого массива, что конечно быстрее и ест меньше памяти.
0

#14 User is offline   NickCool 

  • Newbie
  • Yii
  • Group: Members
  • Posts: 1
  • Joined: 13-March 14

Posted 13 March 2014 - 01:34 AM

Я надеюсь, что все понимают разницу между "... LIMIT 100" и "... LIMIT 99900, 100" для MySQL.
0

Share this topic:


Page 1 of 1
  • You cannot start a new topic
  • You cannot reply to this topic

1 User(s) are reading this topic
0 members, 1 guests, 0 anonymous users