Модераторы: skyboy, MoLeX, Aliance, ksnk
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Большой IN, Оптимизация 
V
    Опции темы
Onis
  Дата 16.5.2012, 17:50 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 241
Регистрация: 18.6.2005
Где: Украина, Николаев

Репутация: нет
Всего: 3



Доброго времени суток Винград
Сейчас занимаюсь оптимизацией одного проекта, и нарвался на довольно сомнительный момент.
Есть скрипт импортирующий из файла имейлы в табличку mysql, каждые 100 итераций он обращается к базе чтобы проверить какие из импортируемых имейлов уже имеют дубликаты в таблице (данные нужны для статистики), делает он это так:
Код

mysql_query('SELECT email FROM queue WHERE email IN ("'.implode('","', array_keys($queue)).'")');

$queue Содержит последние 100 имейлов, а в таблице queue их уже несколько миллионов. Выходит за один проход скрипт может сделать порядка 100 обращений к базе данных с условием IN на 100 элементов.
Как лучше оптимизировать этот момент? Я думал скачать в начале скрипта все имейлы из очереди и сравнивать массивы на стороне php, но оправдает ли себя загрузка такого объемного количества данных? Есть ли другие способы, или стоит этот момент оставить без изменений?

Табличка в innoDB, и сейчас всё переписываю на PDO.

Это сообщение отредактировал(а) Onis - 16.5.2012, 17:52
PM MAIL   Вверх
Fortop
Дата 16.5.2012, 18:21 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2200
Регистрация: 13.11.2007
Где: Донецк

Репутация: 3
Всего: 42



Результаты профайлинга выполнения этого вызова функции приведите.
Плюс explain для самого запроса.

Тогда можно будет о чем-то говорить.


--------------------
Мир это Я.
Живее всех живых.
PM MAIL   Вверх
Onis
Дата 17.5.2012, 10:14 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 241
Регистрация: 18.6.2005
Где: Украина, Николаев

Репутация: нет
Всего: 3



Ok, пардон, думал уже есть стандартные методики для избежания подобных выборок. Я сейчас переписываю код у себя на локальной машине, когда будет доступ к продукт серверу и базе, выложу тесты.
PM MAIL   Вверх
ksnk
Дата 17.5.2012, 10:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


прохожий
****


Профиль
Группа: Комодератор
Сообщений: 6855
Регистрация: 13.4.2007
Где: СПб

Репутация: 14
Всего: 386



Цитата(Onis @  16.5.2012,  17:50 Найти цитируемый пост)
Есть скрипт импортирующий из файла имейлы в табличку mysql

А сделать email уникальным ключем не проще? Тогда можно вставлять insert... on duplicate key update..., чтобы не наталкиваться на ошибки, в крайнем случае просто их игнорировать при вставке.


--------------------
Человеку свойственно ошибаться, программисту свойственно ошибаться профессионально ! user posted image
PM MAIL WWW Skype   Вверх
Onis
Дата 17.5.2012, 11:21 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 241
Регистрация: 18.6.2005
Где: Украина, Николаев

Репутация: нет
Всего: 3



ksnk
Это уже сделано, я специально дописал то что эти данные нужны для статистики. Правда перед инсертом дубликаты всё равно убираются
На данный момент в коде это выглядит примерно так:
Код

...
//$this->stack; массив входящих имейлов, обычно за одну итерацию обрабатываем ~100 имейлов

$unsubscribed  = $this->selectAlreadyUnsubscribed($this->stack); // внутри происходить запрос SELECT email FROM unsubscribe WHERE email IN ("'.implode('","', $stack).'")
$bounced  = $this->selectAlreadyBounced($this->stack); //SELECT email FROM bounce WHERE email IN ("'.implode('","', $stack).'")
$dublicate = $this->selectAlreadyInQueue($this->stack); //SELECT email FROM queue WHERE email IN ("'.implode('","', $stack).'")
$this->udpateStatistic(compact($unsubscribed, $bounced, $dublicate));
$this->addToQueue(array_diff($this->stack, $unsubscribed, $bounced, $dublicate)); // INSERT INTO queue ...
...

Ну вот меня мучают сомнения что этот момент сделан оптимально.
PM MAIL   Вверх
Aliance
Дата 17.5.2012, 11:49 (ссылка)    | (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


I ♥ <script>
****


Профиль
Группа: Модератор
Сообщений: 6418
Регистрация: 2.8.2004
Где: spb

Репутация: 3
Всего: 137



Не проще ли выгрузить email`ы в массив вида:
Код

$emails = array('[email protected]', '[email protected]');

а дальше вместо запроса проверять наличие так:
Код

if (in_array($email, $emails))
    print 'Email '.$email.' exists in DB!';


Это сообщение отредактировал(а) Aliance - 17.5.2012, 11:50
PM MAIL WWW ICQ Skype   Вверх
Onis
Дата 17.5.2012, 13:19 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 241
Регистрация: 18.6.2005
Где: Украина, Николаев

Репутация: нет
Всего: 3



Цитата(Aliance @  17.5.2012,  10:49 Найти цитируемый пост)
Не проще ли выгрузить email`ы в массив вида:

Собственно это была часть моего вопроса, у меня вообще была мысль загрузить массивы $unsubscribed,  $bounced и $dublicate в мемкэш и считывать их с базы только если их нет в кэше. Но дело в том что только за последние несколько месяцев каждый из этих массивов уже составляет по несколько миллионов имейлов. Вот и думаю оправдает ли себя в данном случае хранение и манипуляция с многомиллионными массивами.
Был бы у меня сейчас доступ к продукт серверам и данным я бы просто методом перебора подобрал бы оптимальный вариант, но пока в моем распоряжении только скрипт который уже нужно оптимизировать. Вот решил у умных людей спросить.
PM MAIL   Вверх
Onis
  Дата 17.5.2012, 14:02 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 241
Регистрация: 18.6.2005
Где: Украина, Николаев

Репутация: нет
Всего: 3



Ладно, решил создать синглтон который будет выступать в роли хранилища и при вызове будет пытаться подгрузить себя с memcache, в нем реализую Lazy Initialization с погрузкой этих массивов. Надеюсь это будет правильное решение. 
PM MAIL   Вверх
Aliance
Дата 17.5.2012, 14:49 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


I ♥ <script>
****


Профиль
Группа: Модератор
Сообщений: 6418
Регистрация: 2.8.2004
Где: spb

Репутация: 3
Всего: 137



Проведите замеры скорости и того, и того решения и сравните. А заодно выложите результаты сюда, всем интересно  smile 
PM MAIL WWW ICQ Skype   Вверх
Onis
  Дата 17.5.2012, 15:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 241
Регистрация: 18.6.2005
Где: Украина, Николаев

Репутация: нет
Всего: 3



Пардон если кто-то прочел превое не отредактированное сообщение, я после теста кэша не вынес кэширование в деструктор. Дурья моя бошка smile еще раз извините.
Вот адекватный  предварительный тест загрузки с mysql и memcahe.
Draft storage class:
Код

<?php
namespace Queue;
require('config.class.php');
require('db.class.php');
require('qmc.class.php');
require('errorhandler.class.php');

class Storage{
    private $recipients = array();

    private function getUnsubscribed(){
        if(!isset($this->recipients['unsubscribed'])){
            $this->recipients['unsubscribed'] = Db::getInstance()->sel_all_unsubscribed()->fetchAll(\PDO::FETCH_COLUMN, 0);
        }
        return $this->recipients['unsubscribed'];
    }

    /***Magic***/
    public function __get($name){
        $method = 'get' . ucfirst($name);
        if(method_exists($this, $method))
            return $this->$method();
        ErrorHandler::trigger('Undefined property requested "' . $name . '"', __FILE__, __LINE__);
        return null;
    }
    public function __destruct(){
          echo 'To cache', "\n";
          QMC::getInstance()->add('add2queue_storage', self::$instance, MEMCACHE_COMPRESSED, 60*10);
    }
    /***Singleton stuff***/
    protected static $instance;
    private function __contruct(){}
    private function __clone(){}
    public static function getInstance(){
        if(!isset(self::$instance)){
            if(!(self::$instance = QMC::getInstance()->get('add2queue_storage'))){
                echo 'Initilized:', "\n";
                self::$instance = new self;
            }
            else
                echo 'From cache:', "\n";
        }
        return self::$instance;
    }
}
//var_dump(QMC::getInstance->get(123));
$start = microtime(true);
echo count(Storage::getInstance()->unsubscribed), ' recipients loaded', "\n";
$end =  microtime(true);
var_dump($end - $start);



Код

[ivan@*** classes]$ ../clear.php
Done.
[ivan@*** classes]$ php storage.class.php
Initilized:
38042 recipients loaded
float(0.038810014724731)
To cache
[ivan@*** classes]$ php storage.class.php
From cache:
38042 recipients loaded
float(0.029272079467773)
To cache
[ivan@*** classes]$ ../clear.php
Done.
[ivan@*** classes]$ php storage.class.php
Initilized:
38042 recipients loaded
float(0.043804168701172)
To cache
[ivan@*** classes]$ php storage.class.php
From cache:
38042 recipients loaded
float(0.029371976852417)
To cache
[ivan@*** classes]$



т.е. выигрыш есть даже на подгрузке данных, для того чтобы крон имел более менее актуальный данные, кэш хранится всего 10 минут. 
Но мы хотели выигарть в сравнении а не в подгрузке, завтра закончу весь скрипт и выложу тесты по сравнению.

Это сообщение отредактировал(а) Onis - 17.5.2012, 15:25
PM MAIL   Вверх
Fortop
Дата 17.5.2012, 20:27 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2200
Регистрация: 13.11.2007
Где: Донецк

Репутация: 3
Всего: 42



Цитата(Aliance @  17.5.2012,  11:49 Найти цитируемый пост)
Не проще ли выгрузить email`ы в массив вида:

зависит от конкретной БД.
Если пару тысяч записей, то кладите в кеш или в банальный php файл, который при включенном опкодкеше будет подгружаться моментально.
При числе записей в несколько миллионов, БД до определенного момента предпочтительнее, потом при росте нагрузки вы всеравно вынесете это в память.


--------------------
Мир это Я.
Живее всех живых.
PM MAIL   Вверх
Onis
Дата 18.5.2012, 09:17 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 241
Регистрация: 18.6.2005
Где: Украина, Николаев

Репутация: нет
Всего: 3



Fortop
Вы правы, сегодня удалось протестировать storage класс на продакшен сервере, и на данный момент загруженные данные занимают почти гигабайт оперативной памяти, а их объем будет быстро расти, так что я отказался от идеи выгрузки всех данных с базы.
PM MAIL   Вверх
Fortop
Дата 18.5.2012, 14:05 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2200
Регистрация: 13.11.2007
Где: Донецк

Репутация: 3
Всего: 42



Цитата(Onis @  18.5.2012,  09:17 Найти цитируемый пост)
 и на данный момент загруженные данные занимают почти гигабайт оперативной памяти, а их объем будет быстро расти, так что я отказался от идеи выгрузки всех данных с базы. 

Ну это как бы не показатель smile у нас только на скриптовом сервере съедается до 100Гб  на пике smile


Onis, давайте вернемся все же к исходным баранам smile
Цитата(Fortop @  16.5.2012,  18:21 Найти цитируемый пост)
Результаты профайлинга выполнения этого вызова функции приведите.
Плюс explain для самого запроса.




--------------------
Мир это Я.
Живее всех живых.
PM MAIL   Вверх
Onis
  Дата 22.5.2012, 18:15 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 241
Регистрация: 18.6.2005
Где: Украина, Николаев

Репутация: нет
Всего: 3



Цитата(Fortop @  18.5.2012,  13:05 Найти цитируемый пост)
давайте вернемся все же к исходным баранам 

 smile 
Честно говоря, просто переписав тот код, и поправив логику я уменьшил время выполнения скрипта более чем в шесть (!) раз. Причем этот скрипт, писал мой тимлид. Дальше, с этим, возиться не собираюсь.
Я уже написал заявление об уходе из этой конторы, никому не пожелаю столкнуться с таким апофеозом непрофессионализма на уровне целой компании.

Это сообщение отредактировал(а) Onis - 22.5.2012, 19:02
PM MAIL   Вверх
Fortop
Дата 23.5.2012, 06:29 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2200
Регистрация: 13.11.2007
Где: Донецк

Репутация: 3
Всего: 42



Цитата(Onis @  22.5.2012,  18:15 Найти цитируемый пост)
Честно говоря, просто переписав тот код, и поправив логику я уменьшил время выполнения скрипта более чем в шесть (!) раз. Причем этот скрипт, писал мой тимлид. Дальше, с этим, возиться не собираюсь.

Это странный подход.

Никто не пишет идеальный код сразу.
А первые прототипы могут быть более чем непроизводительны.
Поэтому если вы выполняли эту работу на итерации профилирования и оптимизации написанного кода, то на мой взгляд это вполне нормальная ситуация.

Сначала функционал - потом производительность (иногда даже ценой "переписать все")


--------------------
Мир это Я.
Живее всех живых.
PM MAIL   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | PHP: Базы Данных | Следующая тема »


 




[ Время генерации скрипта: 0.1726 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.