Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > PHP: Для профи > Высокая нагрузка на работу с БД.


Автор: gogzor 3.3.2010, 18:55
Привет всем.
Есть достаточно нагруженный сайт, меня попросили както переписать/оптимизировать код работы с бд (MySQL).
Что пришло первое в голову это создать класс по шаблону синглтон.
Т.е тогда у каждого юзера будет только 1 инстанс класса на всю сессию и 1 открытое соединение, которое обрабатывает все его запросы.

Примерно так :
Код

class DBManager {
    
    private static $instance;
    public $dblink;
    
    private function __construct(){}
    
    public static function GetInstance() {
        
        if(is_null(self::$instance)) {
            $newInstance = __CLASS__;
            self::$instance = new $newInstance;
            self::$instance->dblink = mysql_connect("host", "user", "pass");
        }
        
        return self::$instance;
    }

    ...

}



Сервер в распоряжении только один, так что возможности распределять всё по серверам нету.
Может вы ещё чтото посоветуете?

Спасибо.

Автор: Vasay 3.3.2010, 19:43
gogzor

Цитата

Что пришло первое в голову это создать класс по шаблону синглтон.
Т.е тогда у каждого юзера будет только 1 инстанс класса на всю сессию и 1 открытое соединение, которое обрабатывает все его запросы.


PHP - не C# smile Тут это работать будет по своему  -  т.к. время жизни синглтона - один запрос (а не сессия). 



Весьма радикальный вариант облегчить жизнь БД - запустить php приложение на http://www.caucho.com/ (желательно коммерческой версии ) Предварительно настроив database connection pool как JNDI ресурс сервера. И переписать всю работу с бд с использованием этого JNDI.

Код


<?php

  // standard PHP
  //mysql_connect($host, $username, $password, $dbname);

  // using JNDI lookup
  mysql_connect("java:comp/env/jdbc/myDatabaseName");

?>


Тогда работа с БД будет идти через пул соединений, что при его грамотной настройке даст серьезное снижение нагрузки с сервера БД.

Автор: gogzor 3.3.2010, 20:07
Vasay, спасибо, посмотрю что это за зверь.

Ипатьев, спасибо, очень информативный ответ от юзера со статусом "эксперт".  smile 

Автор: smartov 3.3.2010, 20:16
gogzor, прежде чем переписывать код, надо найти узкие места.
Разобраться в чем они: тяжелые запросы? большое количетсво запросов?
Исходя из ситуации либо оптимизировать тяжелые запросы, либо структуру базы, чтобы они не были такими тяжелыми, либо консолидировать большое количество запросов в более емкий и оптимальный.

Автор: Ипатьев 3.3.2010, 20:34
Вообще, судя по формулировке задания, непонятна область ответственности топикстартера. Имеется ли у него доступ к нужной информации. 
Но сама, конечно, формулировка, многое говорит о тех, кто ставил задачу. Учитывая, что код доступа к бд никогда не бывает узким метстом.

Автор: IgorIV 3.3.2010, 21:21
Цитата(gogzor @  3.3.2010,  20:07 Найти цитируемый пост)
Ипатьев, спасибо, очень информативный ответ от юзера со статусом "эксперт". 

Раз так, тогда выполняй.
Чтобы не превращать топик во флеймовый, gogzor, в твоих же интересах выдать конкретную информацию. Количество пользователей, размер базы, тяжелые запросы и остальное, в том же духе. Разобраться почему так тяжело базе. 
А пока все думают, что база для тебя черный ящик. Что там внутри ты не знаешь.

Автор: Majesty 23.7.2010, 14:38
Нужен не синглтон, а отложенная инициализация с кеширующей прослойкой. Примерно так:
Код
class DB {
    private $db_host;
    private $db_login;
    private $db_pass;
    private $db_name;
    private $db_link;
    private $cache;

    public function __construct($host, $login, $pass, $name, $cacher) {
        $this->db_host = $host;
        $this->db_login = $login;
        $this->db_pass = $pass;
        $this->db_name = $name;
        $this->cache = $cacher;
    }

    private function connect() {
        if(!is_resource($this->db_link)) {
            $this->db_link = mysql_connect($this->db_host, $this->db_login, $this->db_pass);
            mysql_select_db($this->db_name, $this->db_link);
        }
    }

    public function query($query) {
        if(false === ($result = $this->cache->read($query)) {
            $this->connect();
            $r = mysql_query($query, $this->db_link);
            while(false !== ($row = mysql_fetch_assoc($r))) {
                $result[] = $row;
            }
            $this->cache->write($query, $result);
        }
        return $result;
    }
}
Очень приблизительно так. При запросе спрашиваем в кеше, есть ли там результат такого запроса. Если нет - подключаемся, получаем результат и сохраняем в кеше. Потом результат возвращаем.

Нужно добавить парсинг запроса (чтобы не кешировать INSERT/UPDATE/DELETE запросы) и проверку на возврат false из mysql_query (хотя бы возвращать $result = false, если запрос завершился ошибкой).

Подключение к базе устанавливается только в том случае, если оно реально нужно. Один и тот же запрос выполняется однократно. Кеш можно сохранять в файлах, в sqlite, в memcached, в apc - где угодно (правильный кеш - тема, достойная отдельного топика).

Естественно, все эти телодвижения не заменяют профилирование самих запросов - slow_queries_log и explain в помощь. Если медленные запросы всё-таки нужны, следует вынести их в асинхронный режим (например, выполнять их в кроне, записывая в тот же кеш, чтобы клиенты читали результаты только из кеша). Опять же, профилирование и оптимизация запросов - это отдельная большая тема имеющая мало общего с PHP.

Автор: smartov 23.7.2010, 15:04
Majesty
1) не надо поднимать старые темы
2) когда человек спрашивает совета как правильно отрезать ухо, если у него чешется нос, то не надо подсказывать размер ножа - нужно подсказать почесать нос (на всякий случай: это аллегория)

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)