Какая гадость этот ваш WordPress!
Не спорю, штука прикольная, но уж больно архитектура у него специфическая:
- нет OOP: несмотря на то, что в системе вроде бы присутствуют классы - основная функциональнасть реализована на функциях;
- плохие привычки программирования: широко используются глобальные переменные;
- JavaScript ад: бардак со скриптами - доходит до того что каждый плагин тянет за собой собственный jquery или prototype;
- тем полно, но все кривые: темы сделаны по-идиотски - нет единого соглашения для создания тем, нет четкого разделения навигации и шаблонов - по сути вся навигация и функционал заложены в теме - а это половина движка;
- нет MVC: собственно нет шаблонов вообще - логика перемешана с HTML так не кодируют уже давно;
- SQL: движёк для работы с базой данных неплох - но он негибкий, заточен только под WP и только под MySQL;
- CMS: виджеты можно настраивать для всех страниц сразу, выборочно никак нельзя;
- низкое быстродействие: из-за навороченной системы фильтров и хуков в финале система получается тормозная и прожорливая;
- разработчики не подозревали о существовании других временных зонах кроме UTC: в коде жёстко прописано date_default_timezone_set('UTC');
Ruby on Rails с точки зрения PHP программиста
Изучаю RoR, в принципе нравится. Многие вещи сделаны классно но некоторые вещи вызвали у меня недоуменье:
- обязательный REST: контроллеры генерят код для HTML и для XML хотя их никто об этом не просит. Возможно это круто и обосновано, однако это приводит к избыточности в коде, и созданию функциональности, которая никогда не будет востреботвана.
- обязятельный JavaScript:удаление элементов происходим методом DELETE, хотя браузеры обычно этот метод не используют, поэтому, для вызова DELETE методов используется объект HttpRequest. Как следствие, в браузере обязательно должен быть включен JavaScript. Если JavaScript выключить стандарные методы удаления объектов перестают работать. Это - не гуд.
- избыточность в структуре проекта: методы генерации объектов создают сразу код на все случаи жизни... И что мне после этого удалять вновь созданные файлы если что то не нужно?
Семеро одного ждут, или как работают сессии в 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....
Не все индексы одинаково полезны
Имеется InnoDB табличка, небольшая - меньше миллиона записей, делаем такой запрос (нужно получить top10 доменов):
> select dlvDestinationDomain, count(*) from email_accounting where jobid="11837" group by dlvDestinationDomain order by count(*) desc limit 10
созданы следующие индексы:
| PRIMARY | BTREE | id |
|---|---|---|
| jobId | BTREE | jobId |
| dlvDestinationDomain | BTREE | dlvDestinationDomain |
| dlvDestinationDomain_2 | BTREE | dlvDestinationDomain |
| A | ||
| jobId_3 | BTREE | jobId |
| A |
Обнаружилась забавная вещь: некоторые индексы не только не ускоряют но и замедляют агрегатные запросы:
вообще без использования индексов:
mysql> select dlvDestinationDomain, count(*) from email_accounting IGNORE INDEX(dlvDestinationDomain_2, jobId_3, jobId, dlvDestinationDomain) where jobid="11837" group by dlvDestinationDomain order by count(*) desc limit 10;
3.09 секунды
используя индекс по jobId дает небольшой прирост производительности:
mysql> select dlvDestinationDomain, count(*) from email_accounting FORCE INDEX (jobId) where jobid="11837" group by dlvDestinationDomain order by count(*) desc limit 10;
2.13 секунд;
использование составного индекса по jobID и dlvDestinationDomain сделало все гораздо веселее:
mysql> select dlvDestinationDomain, count(*) from email_accounting FORCE INDEX (jobId_3) where jobid="11837" group by dlvDestinationDomain order by count(*) desc limit 10;
0.39 секунд;
ипользование ключа с полям в обратном порядке (dlvDestinationDomain и jobID) работает гораздо хуже:
mysql> select dlvDestinationDomain, count(*) from email_accounting FORCE INDEX (dlvDestinationDomain_2) where jobid="11837" group by dlvDestinationDomain order by count(*) desc limit 10;
1.36 секунд;
А вот использование ключа только по dlvDestinationDomain заставляет MySQL думать почти две минуты:
mysql> select dlvDestinationDomain, count(*) from email_accounting FORCE INDEX (dlvDestinationDomain) where jobid="11837" group by dlvDestinationDomain order by count(*) desc limit 10;
1 минута 48 секунд;
Кстати, заметил, что использование jobid="11837" (jobid у меня текстовое) заметно прибавляет скорости против jobid=11837. Не зря в strict моде MySQL грязно ругается на такие преобразования, ох не зря...
Upd. Во всем оказались замешаны настройки InnoDB, если увеличить в my.cnf значение переменной innodb_buffer_pool_size то скорость выполнения агрегативного запроса с индексом по группируемому полю заметно возрастает и картина меняется на прямо противоположную.