вторник, 25 декабря 2012 г.

Firefox: пишем расширение для браузера

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

Внедерение своего скрипта в тело страницы
//защищенная часть скрипта - оборачиваем в анонимную функцию, чтобы не было повторов имен переменных
(function() {
    var YT_global_doc = undefined;

    var YT_loader = new function(){};
    YT_loader.run = function(e) {
        //проверяем адрес сайта
	var unsafeWin = e.target.defaultView;
	var unsafeLoc = new XPCNativeWrapper(unsafeWin, 'location').location;
	var href = new XPCNativeWrapper(unsafeLoc, 'href').href;
	if (!href.match(/^http:\/\/www\.xxxx\.com\/watch(.*)?$/i) )
	      return;

        //обращаемся к dom сайта
	YT_global_doc = e.target.defaultView.document;

	if(YT_global_doc) {

          //подгружаем наш скрипт
	  var script = YT_global_doc.createElement( 'script' );
	  script.type = 'text/javascript';
	  script.src = 'chrome://xxx/content/yyy.js';
	  YT_global_doc.body.appendChild(script);

	}
    };

    var yt_load = function() {
        //событие загрузки вкладки с сайтом, выполняется каждый раз при загрузке страницы
        window.document.addEventListener("DOMContentLoaded", YT_loader.run, true);
    };
    //событие загрузки приватной части - выполняется один раз
    window.addEventListener("load", yt_load, false);

})();


Правильное создание новых узлов в DOM
Внутренними правилами firefox запрещено обращаться и работать с dom через innerHtml. Взамен этого необходимо пользоваться шаблонами. В своих аддонах я пользуюсь JQuery Templating.
Вот пример:
$("#pv_actions").prepend (
      $("<a>", {
	href: 	"#",
	target: "_blank",
	text:	"Загрузить оригинал",
	id:	"pv_down_link"
      })
      .css("font-weight", "bold")
      .bind("click", function (o) {
	//
      })
);


Взаимодействие с защищенной частью скрипта
Аддон состоит из 2 частей: защищенной и внедренной (небезопасной части). В безопасной части можно работать с апи браузера: сохранять файл, делать ajax запросы и т.д.
Взаимодействовать эти две изолированные части могут только по средствам событий.
Рассмотрим пример: узнать размер файла.
В незащищённой части создаем событие с нашими параметрами.
  //yt_getFileSizeEvent - название события
  var element = document.createElement("yt_getFileSizeEvent");
  element.setAttribute("attribute_url", "адрес до файла" );
  element.setAttribute("attribute_id",  "индекс файла" );
  document.documentElement.appendChild(element);
  var evt = document.createEvent("Events");
  evt.initEvent("yt_getFileSizeEvent", true, false);
  element.dispatchEvent(evt);
В защищенной части ловим событие:
document.addEventListener("yt_getFileSizeEvent", function(e) { YT_secureEvent.getFileSize(e); }, false, true);
После этого обрабатываем и повторяем все в обратном порядке, чтобы передать данные в незащищённую часть скрипта.

суббота, 1 декабря 2012 г.

Советы по оптимизации SQL запросов

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

Другие статьи по оптимизации SQL

1. Ни каких подзапросов, только JOIN
Как я уже писал ранее, если выборка 1 к 1 или надо что-то просуммировать, то ни каких подзапросов, только join.
Стоит заметить, что в большинстве случаев оптимизатор сможет развернуть подзапрос в join, но это может случиться не всегда.

2. Выбор IN или EXISTS ?
На самом деле это сложный выбор и правильное решение можно получить только опытным путем.
Я дам только несколько советов:
* Если в основной выборке много строк, а в подзапросе мало, то ваш выбор IN. Т.к. в этом случае запрос в in выполнится один раз и сразу ограничит большую основную таблицу.
* Если в подзапросе сложный запрос, а в основной выборке относительно мало строк, то ваш выбор EXISTS. В этом случае сложный запрос выполнится не так часто.
* Если и там и там сложно, то это повод изменить логику на джойны.

3. Не забывайте про индексы
Совет для совсем новичков: вешайте индексы на столбцы по которым джойните таблицы.

4. По возможности не используйте OR.
Проведите тесты, возможно UNION выглядит не так элегантно, за то запрос может выполнится значительно быстрей. Причина в том, что в случае OR индексы почти не используются в join.

5. По возможности не используйте WITH в oracle.
Значительно облегчает жизнь, если запрос в with необходимо использовать несколько раз ( с хинтом materialize ) в основной выборке или если число строк в подзапросе не значительно.
Во всех других случаях необходимо использовать прямые подзапросы в from или взаранее подготовленную таблицу с нужными индексами и данными из WITH.
Причина плохой работы WITH в том, что при его джойне не используются ни какие индексы и если данных в нем много, то все встанет. Вторая причина в том, что оптимизатору сложно определить сколько данных нам вернет with и оптимизатор не может построить правильный план запроса.
В большинстве случаев WITH без +materialize все равно будет развернут в основной запрос.


6. Не делайте километровых запросов
Часто в web обратная проблема - это много мелких запросов в цикле и их советуют объединить в один большой. Но тут есть свои ограничения, если у вас запрос множество раз обернутый в from, то внутреннюю(ие) части надо вынести в отдельную выборку, заполнить временную таблицу, навесить индексы, а потом использовать ее в основной выборке. Скорость работы будет значительно выше (в первую очередь из-за сложности построения оптимального плана на большом числе сочетаний таблиц)

7. Используйте KEEP взамен корреляционных подзапросов.
В ORACLE есть очень полезные аналитические функции, которые упростят ваши запросы. Один из них - это KEEP.
KEEP позволит сделать вам сортировку или группировку основной выборки без дополнительно запроса.
Пример: отобрать контрагента для номенклатуры, который раньше остальных был к ней подвязан. У одной номенклатуры может быть несколько поставщиков.
SELECT n.ID, MIN(c.ID) KEEP (DENSE_RANK FIRST ORDER BY c.date ASC) as cnt_id
FROM nmcl n, cnt c
WHERE n.cnt_id = c.id
GROUP BY n.ID
При обычном бы подходе пришлось бы делать корреляционный подзапрос для каждой номенклатуры с выбором минимальной даты.
Но не злоупотребляйте большим числом аналитических функций, особенно если они имеют разные сортировки. Каждая разная сортировка - это новое сканирование окна.

8. Гуляние по выборке вверх-вниз
Менее популярная функция, но не менее полезная. Позволяет смещать текущую строку выборки на N элементов вверх или вниз. Бывает полезно, если необходимо сравнить показатели рядом стоящих строк.
Следующий пример отбирает продажи департаментов отсортированных по дате. К основной выборке добавляются столбцы со следующим и предыдущим значением выручки. Второй параметр - это на сколько строк сместиться, третьи - параметр по-умолчанию, если данные соседа не нашлись.
SELECT deptno, empno, sal,
LEAD(sal, 1, 0) OVER (PARTITION BY dept ORDER BY date) NEXT_LOWER_SAL,
LAG(sal, 1, 0) OVER (PARTITION BY dept ORDER BY date) PREV_HIGHER_SAL
FROM emp;
ORDER BY deptno, date DESC;
При обычном подходе бы пришлось это делать через логику приложения.

9. Direct Path Read
Установка этой настройки (настройкой или параллельным запросом) - чтение данных напрямую в PGA, минуя буферный кэш. Что укоряет последующие этапы запроса, т.к. не используется UNDO и защелки совместного доступа.

10. Direct IO
Использование прямой записи/чтения с диска без использования буфера файловой системы (файловая система конкретно для СУБД).
* В случае чтения преимущество в использовании буферного кэша БД, замен кэша ФС (кэш бд лучше заточен на работу с sql)
* В случае записи, прямая запись гарантирует, что данные не потеряются в буфере ФС в случае выключения электричества (для redolog всегда использует fsync, в не зависимости от типа ФС)

11. Оптимизация параллельных запросов
12. Оценка стоимости запроса и построение правильного плана
13. Оптимизация работы секционированных таблиц
14. Индексный поиск
15. Оптимизация запросов вставки
16. Ускорение pl/sql циклов
17. И другое...