2012-05-05

MySQL сервер ушёл гулять

Иногда на сервере происходят сошибки типа "mysql server has gone away". Ошибка весьма досадная тем, что возникает она не всегда а только иногда. При этом, путем эмпирических действий удалось выяснить, что такое сообщение вызывается если PHP скрипт какое то время чем то сильно был занят и при этом не обращается к базе данных. Когда после некоторго периода бездействия скрипт, наконец хочет что-либо записать в базу данных то обнаруживается, что ссылка на соединение с базой данных уже мертва и все последующие запросы вылетают с ошибкой "mysql server has gone away". Понятно, что соединение с базой данных рвется по некоему таймауту, но вот по какому и как его увеличить?

Оказалось, что дело в переменной wait_timeout которая живет в /etc/my.conf. В моем случае она оказалась установленной в 30 секунд, поэтому, если во время выполнения скрипта между запросами оказывается промежуток более тридцати секунд - соединение рвется и больше не восстанавливается.

Изменить это значение можно либо поправив my.conf,  либо после установления соединения выполнив "SET wait_timeout=1000".

 

Коментариев: 1

 
  2011-10-14

Как найти расстояние между двумя GPS точками

Понадобилось посчитать дистанцию между двумя точками заданными в GPS координатах. В PHP реализация такой фунции выглядит следующим образом:

function calcMiles ($Lat1, $Lon1, $Lat2, $Lon2){
    return 3958.75 * acos(  sin($Lat1/57.2958) * sin($Lat2/57.2958) + cos($Lat1/57.2958) * cos($Lat2/57.2958) * cos($Lon2/57.2958 - $Lon1/57.2958));
}

Ответ получается в милях, для перевода в метры, полученное значение нужно ещё умножить на 1609.344.

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

 

Оставить комментарий

 
  2011-09-29

PCRE поддерживает Posix литералы

Я уже писал, что нужно сделать, чтобы заменить в коде Posix регулярные выражения, которые в PHP 5.3 стали deprecated, на Perl совместимые. Оказывается Rerl совместимые регулярные выражения поддерживают литералы используемые в Posix такие как [[:allnum:]], [[:space:]] и т.д. В одном месте чужёго кода заметил это, и вот дошли руки проверить на практике: конструкция вида preg_match('/[[:space:]]/','df d') работает идентично конструкции вида preg_match('/\s/','df d') т.е. совсем не нужно при замене ereg на preg_match заменять еще и литералы, достаточно добавить слева и справа слеши "/". Кстати кроме слешей можно использовать любой другой символ, например "#" это полезно когда в регулярном выражении уже есть слеши - чтобы их не экранировать и не ухудшать читаемость кода.

 

Оставить комментарий

 
  2011-09-03

Сравнение различных методов вызова функций в PHP

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

Однако, создание синглетона операция довольно ресурсоёмкая — проверить создан ли экземпляр класса, создать экземпляр класса если он не создан, вызвать на нём нужный метод. Насколько это сказывается на бытродействии я и решил сегодня проверить.

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

Вот исходный код скрипта.

Результат работы скрипта представлен в таблице ниже:

generation_time 11.7576429844
php_version 5.3.2-1ubuntu4.9
Case try 1 try 2 try 3 try 4 middle
Same plain function called 10000 times 0.0166 0.0187 0.0183 0.0135 0.0168
Different plain functions called 10000 times 0.0163 0.0221 0.018 0.0163 0.0182
Same static method 10000 times 0.0193 0.0265 0.0206 0.0194 0.0214
Different static methods called 10000 times 0.0221 0.0283 0.0229 0.0229 0.024
Static methods of different classes called 10000 times 0.0236 0.0279 0.0251 0.0242 0.0252
Same dynamic method called 10000 times 0.0166 0.0193 0.0169 0.0169 0.0174
Different class methods called 10000 times 0.02 0.02 0.0202 0.02 0.02
Singleton method called 10000 times 0.0403 0.0446 0.0409 0.041 0.0417
Different singletons called 10000 times 0.0562 0.0631 0.0548 0.0572 0.0578

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

Результаты тестирования показали, что вызов методов синглетона, примерно в два - четыре раза медленнее, чем вызов обычных методов, что, в принципе, меня порадовало — я ожидал, что будет хуже.

При этом выяснилось несколько дополнительных вещей: cтатические вызовы функций объявленных без модификатора "static" примерно в два раза медленнее, чем с ним, cтатические методы немного медленнее, чем динамические.

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

 

Оставить комментарий

 
  2011-09-01

Еще один странный глюк в PHP

Два дня бился со странным глюком  - в скрипте в определенном месте отваливается соединение с базой данных - выдает "MySQL server is gone away". При этом отваливается всегда в одном и том же месте после куска кода с большим объемом вычислений, но без единого вызова базы данных. И только на сервере - на локальном компьютере всё тип-топ. Путем небольших, почти шаманских манипуляций с кодом удалось проблему купировать, правда неизвестно, где она может всплыть в дальнейшем. Налицо проблема в самом интерпретаторе PHP.

Upd: Нашел источник проблемы

Такое себе позволял только PHP4. Похоже, PHP5 вступил в ту стадию зрелости за которой уже наступает старческий маразм...

 

Коментариев: 3

 
  2011-07-13

Странный глюк в PHP

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

Предыстория такая - работаю над проектом, над которым потрудились индийские программисты. Сам проект сделан на движке Symphony - добротный такой движок, вполне приятный. В качестве библиотеки работы с базой данных используется Creole - тоже довольно приятная штука. В качестве ORM используется Propel — штука громоздкая, но тем не менее тоже весьма мощная и простая. Вообще Symphony показался мне весьма неплохим движком, весьма простым и удобным. Наверно, потому, что он почти один в один копирует Ruby on Rails - те же хелперы, тот же yml в конфигах, такой же подход к MVC - в общем очень похоже.

Так вот, доблестные индийские программисты, вместо того, чтобы использовать оснастку из Symphony навставляли везде mysql_connect и mysql_query. И все бы ничего, но mysql_connect у них вставляется ВЕЗДЕ перед mysql_query — в одном скрипте может быть создано несколько десятков соединений, а каждый mysql_query дополняется проверкой "or die(mysql_error())", поэтому любая ошибка в sql ведет к скоропостижной кончине всего скрипта.

Чтобы хоть как то исправить это безобразие я сначала удалил все mysql_connect. Затем, все mysql_query поиском и заменой заменил на собственную функцию, использующую оснастку creole, поместил её в глобально видимый класс myTools::query():

static function query($sql){
         $connection = Propel::getConnection();
         $r = $connection->executeQuery($sql);
         return $r->getResource();
}

Странное дело код ведь тривиальный, но ресурс возвращаемый этой функцией оказался невалидным! Причем внутри функции ресурс еще остаётся рабочим а возвращённый во вне оказывается сломанным. Тот же код ВНЕ функции идеально работает, но как только выношу его в функцию — перестаёт работать.

Что это — глюк самого PHP или глюк Simphony я так и не понял, пришлось немного модифицировать класс MySQLConnection и добавить туда метод возвращающий MySQL ресурс непосредственно, минуя всякие PHP обёртки, но осадочек все же остался...

 

Оставить комментарий

 
  2011-07-05

Какая гадость этот ваш WordPress!

Не спорю, штука прикольная, но уж больно архитектура у него специфическая:

  1. нет OOP: несмотря на то, что в системе вроде бы присутствуют классы - основная функциональнасть реализована на функциях;
  2. плохие привычки программирования: широко используются глобальные переменные;
  3. JavaScript ад: бардак со скриптами - доходит до того что каждый плагин тянет за собой собственный jquery или prototype;
  4. тем полно, но все кривые: темы сделаны по-идиотски - нет единого соглашения для создания тем, нет четкого разделения навигации и шаблонов - по сути вся навигация и функционал заложены в теме - а это половина движка;
  5. нет MVC: собственно нет шаблонов вообще - логика перемешана с HTML так не кодируют уже давно;
  6. SQL: движёк для работы с базой данных неплох - но он негибкий, заточен только под WP и только под MySQL;
  7. CMS: виджеты можно настраивать для всех страниц сразу, выборочно никак нельзя;
  8. низкое быстродействие: из-за навороченной системы фильтров и хуков в финале система получается тормозная и прожорливая;
  9. разработчики не подозревали о существовании других временных зонах кроме UTC: в коде жёстко прописано date_default_timezone_set('UTC');
 

Оставить комментарий

 
  2011-04-08

Семеро одного ждут, или как работают сессии в PHP

Сессии в PHP - вещь замечательная, она позволяет ускорить многие вещи на сайте, например, при выполнении долгих запросов к базе данных их можно записать в сессию и затем полученные значения сохранять в сессию и использовать по мере необходимости, однако, у сессий есть одно свойство, которое может гарантированно свести на нет все попытки увеличить быстродействие вашего сайта. Дело в том, что сессии в PHP блокируют ВСЕ страницы загружаемые для текущего пользователя до тех пор, пока страница, которая перавая успела заблокировть сессию не будет выгружена из памяти сервера. Т.е. если имеем одну медленную страницу с открытой сессией, которая выполняется, скажем, десять секунд - то в течении этих десяти секунд ВСЕ параллельно загружаемые странички будут блокированы и будут ждать пока медленная страница не будет сгенерирована до конца и выгружена и памяти.

В качестве примера, создадим две странички:

slow.php

<?php
print 'START....';

sleep(10);

print 'DONE';

fast.php:

<?php

print 'START....';
print 'DONE';
 

как видим обе странички работают вместе просто прекрасно, пока одна страничка генерируется десять секунд вторая загружается почти мгновенно, теперь добавим в обе страницы работу с сессиям:

slow.php

<?php
session_start();
print 'START....';

if (!isset($_SESSION['counter'])){
    $_SESSION['counter'] = 0;
}
else {
    $_SESSION['counter']++;
}
sleep(10);

print 'DONE';

fast.php:

<?php
session_start();
print 'START....';

print isset($_SESSION['counter'])?$_SESSION['counter']:'none';

print 'DONE';
 

Все попались, теперь, пока медленная страничка не завершиться, быстрая страничка будет ее ждать, т.к. файл с сессией остался заблокирован медленной страницей.

Одним из вариантов для решения подобной проблемы можно вызвать session_write_close(); перед заведомо медленной процедурой. В этом случае сессия будет закрыта досрочно и блокировка с файла будет снята, правда изменять какие либо значения в сессионных переменных уже не получится.

Финальный вариант slow.php:

<?php
session_start();
print 'START....';

if (!isset($_SESSION['counter'])){
    $_SESSION['counter'] = 0;
}
else {
    $_SESSION['counter']++;
}

session_write_close();
sleep(10);

print 'DONE';

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

Upd: Кроме этого, если после долгой операции нужно еще что нибудь поменять можно перед долгой операцией сначала закрыть сессию при помощи session_write_close() а потом её снова открыть при помощи session_start(). Однако, делать это можно только если вы ничего ещё не начали выводить в поток, иначе будет известный Warning: Headers already sent....

 

Коментариев: 1

 
  2011-01-19

Функция преобразования даты из ISO 8601

Потребовалось обработать логи в которых дата хранилась примерно в таком формате 2010-12-03T18:34:33-0500. К сожалению в PHP нет толковой функции для преобразования строкового представления даты в timestamp. Как показала практика strtodate совершенно не учитывает последние четыре знака в строке которые она просто отбрасывет.

Пришлось парсерить строчку ручками и вот что получилось:

function covertdate($date){
	preg_match('/(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})([-+])(\d{2})(\d{2})/', $date,$res);
	list($d,$year,$month,$day,$hour,$minute,$second,$digit,$zonehours,$zoneminutes) = $res;
	
	$offset = ((int)($digit."1"))*($zonehours*3600+$zoneminutes*60);
	
	$timestamp = gmmktime($hour,$minute,$second,$month,$day,$year)-$offset;
	return $timestamp;
}

 

 

Коментариев: 1

 
  2010-12-03

Полезное применение static переменных

Иногда бывает необходимо в какой либо функции находить какую либо информацию, сохраненную в MySQL табличке по её ID. При этом количество элементов в табличке может быть небольшим, а вот количество вызовов такой функции, наоборот большим. Каждый раз вызывать "select ... from" будет расточительно, вставить справочную табличку через JOIN не всегда возможно. Было бы логично сохранить где-либо все значения справочной таблички а потом брать по ключу (табличка как я уже сказал небольшая). Самое лучшее решение, которое я придумал на сегодня — это воспользоваться static переменным (их можно применять не в только для синглитонов)

static function getDocumentTypeName($type_id){
    static $cache;
    if (!isset($cache)){
        $cache = Db::selectAssoc("select type_id, type_name from doc_types");
    }
    return $cache[$type_id];
}

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

 

Оставить комментарий