Оптимизация сортировки в таблицах с текстовыми полями

16.11.2010

Проблема с сабжем в том, что mysql сортирует сначала все выбранные данные, а потом отдает кусочек, который ты указал в limit. Соответстветственно, даже при наличии индекса по полю сортировки он выпадает в filesort если в таблице есть крупные поля.

Обычный подход состоит в том, чтобы сделать задуманое двумя запросами: первым (легким) выгрести только id отсортированных записей и избежать filesort, а вторым уже получить полные записи для этих id.

Ну, например, вот так:

class FoobarManager(models.Manager):
    def latest(self, limit=10):
        """ more effecient version of query to avoid filesort """
        ids = list(self.values_list('id', flat=True).order_by('-modified')[:limit])

        return self.filter(id__in=ids).extra(
                select={'manual': 'FIELD(id,%s)' % ','.join(map(str, ids))},
                order_by=['manual'])

Идея насчет реализации order_by(‘manual’) взята отсюда, а сама необходимость в нем описана вот тут.
Можно не делать order_by(‘manual’), а сделать повторный order_by(‘-modified’) при желании. По идее, производительность не пострадает.

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


Цветной logger

30.10.2010

Тут.

Сейчас и опробую.


hg externals

11.04.2010

hg externals — удобное маленькое расширение для scm mercurial. Позволяет примапить папки внутри рабочей копии к другим репозитариям (аналог svn externals). Помимо hg поддерживает еще и svn.

Имеет смысл, допустим, если вы используете какие–то внешние библиотеки в рамках проекта и хотите постоянно поддерживать их в максимально свежем состоянии, или у вас несколько проектов, с общими кусками, которые целесообразно вынести в отдельные репозитарии.

Молча ставится из исходников на любой меркуриал, но работает нормально только с 1.5 :)

Кстати, selenic наконец обновил сайт для mercurial, куда катится этот мир?


Удаление всех записей из таблицы.

23.11.2009

Удивительное рядом:

           db.delete(Score.all())

исполняется на полторы секунды ДОЛЬШЕ чем лобовой способ:


             for score in Score.all():
                 score.delete()

На датасете из 1000 элементов в локальном варианте GAE. Я бы подумал, что это флюктуация, но результат воспроизводится с завидным постоянством.


Поиск строк по префиксу в BigTable

23.11.2009

Нативный GAE дэйтастор не умеет фильтровать строки по префиксу, как это делает, например mysql

SELECT * FROM foo WHERE bar LIKE 'prefix%';

Взамен этого в мануале предлагается следущий трюк:

db.GqlQuery("SELECT * FROM foo WHERE bar >= :1 AND bar < :2",
 "prefix", u"prefix" + u"\ufffd")

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

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

Трюк, конечно, из разряда удаления гланд через анус (кроме того, непонятно, как матчить строки по постфиксу, или просто по вхождению), с другой стороны, надо попробовать такое в MySQL, может побыстрее нэйтивного лайка будет. Хотя, не исключено, что он именно так и реализован внутренне :).

Stay tuned, сегодня–завтра будет пост по расчетам медиан на крупных выборках с профайлингом и т.п.


Typhoon App Engine

09.11.2009

Хотите использовать Google App Engine на своем собственном сервере? Никаких проблем, для этого есть Typhoon App Engine.

(за исключением того, что оно еще альфа и разработчики ни за что не ручаются).

На закуску — подсказка, как запустить GAE локально под python2.6 (ну если кто сам не нашел).

P.S. Спасибо Диме Харламову за наводки.


Немного javascript: измерение времени загрузки скрипта

03.11.2009

По ходу разработки проекта столкнулся с необходимостью замерять чистое время загрузки скрипта с CDN средствами javascript.

Загрузка и исполнение скрипта меряется довольно легко:

function loadAndExecuteScript(url, callback){
      var script = document.getElementsByTagName('script')[0];

      script.type = "text/javascript";

      if (script.readyState){  //IE
          script.onreadystatechange = function(){
              if (script.readyState == "loaded" ||
                      script.readyState == "complete"){
                  script.onreadystatechange = null;
                  callback(Number(new Date()) - t);
              }
          };
      } else {  //Others
          script.onload = function(){
              callback(Number(new Date()) - t);
          };
      }

      var t = Number(new Date());
      script.src = url;
  }

Чтобы замерять только загрузку, приходится выкручиваться примерно вот так:

function loadScript(url, callback) {
      if (navigator.userAgent.toLowerCase().indexOf('msie') !== -1) {
          var xhr = window.ActiveXObject ? new ActiveXObject("Microsoft.XMLHTTP") : new XMLHttpRequest(),
              time = false;

          xhr.onreadystatechange = function() {
            if (xhr.readyState==1 && !time)
              time = Number(new Date());

            if (xhr.readyState==4)
              callback(Number(new Date()) - time);
          }

          xhr.open("GET", url, true);
          xhr.send(null);
      }
      else
      {
        var iframe = document.getElementsByTagName('iframe')[0],
            time = Number(new Date());

        iframe.onload = function(){
            callback(Number(new Date()) - time);
        };

        iframe.src = url;
      }
  }

Если в нормальных браузерах можно просто загрузить файл js в iframe и померять время его загрузки, то у IE, как обычно, свой путь. Увидев непривычный mime IE предложит пользователю сохранить его. Попытки загрузить JS в картинку (а также FRAME, OBJECT, и еще полдесятка тэгов) к позитивному результату также не приводит — он либо видит неожиданный mime в неожиданном месте либо не дает замерять, как в случае с BGSOUND (помните такой? :)).
Я выкрутился через XHR–запрос. При попытке выполнить XHR–запрос к другому домену все браузеры обычно делают вид, как–будто ничего и не случилось (и не выполняют его), ИЕ же выдает confirmation на обращение к внешнему ресурсу. Не очень секьюрно с его стороны, но, по–крайней мере, позволяет решить мою задачу хоть как–то (без привлечения Flash через Flex).

Не исключено, впрочем, что проблема эта уже тысячу раз была кем–то решена, а я просто не смог найти решение. Впрочем, еще поищу :)

P.S.
А посоветуйте хороший HTTP сниффер под Mac OS X, который умеет замерять время выполнения запросов? Хочу проверить точность результатов.


Используем expect для установки фикстур

01.11.2009

Пару постов назад я жаловался на неудобство установки фикстур. У меня в проекте пять табличек, при перезапуске приложения GAE чистит датастор (хотя галочка отключена :-\), поэтому приходится периодически запускать пять скриптов чтобы данные залить заново. Все бы хорошо, но appcfg.py требует каждый раз вводить логин/пароль (admin:admin для локалхоста) вручную. Указать их в параметрах нельзя (по–крайней мере я не нашел).

Помучавшись немного, решил написать expect–скрипт для автоматизации этой операции (спасибо, дядя Вова!). Не знаю, как у виндовс-ребят, но под маком и никсами она есть :). Для тех, кто (как и я, до сегодняшнего вечера)  не знаком с этой чудесной программой — expect позволяет запускать различные приложения командной строки и эмулировать ввод пользователя по срабатыванию определенных триггеров (например, появления в выводе какого–нибудь слова, в нашем случае — приглашения ввести пароль). На самом деле expect умеет гораздо больше, но нам вполне хватит и этого базового функционала:

Вот что у меня получилось:

spawn appcfg.py upload_data -q --config_file=[lindex $argv 0] --filename=[lindex $argv 1] --log_file=/d
ev/null --kind=[lindex $argv 2] --url=http://localhost:[lindex $argv 3]/remote_api ./

expect {
    Password { send admin\r; exp_continue }
    Email { send admin\r; exp_continue }
}

Вызывается это счастье следущим образом:

expect ./test.exp cdn_loader.py cdn_data.csv CDN 8081

Где параметры означают:

  1. скрипт–загрузчик
  2. CSV с данными
  3. kind датастора
  4. Порт вашего приложения

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


Новый адрес

28.10.2009

Этот сайт теперь доступен по человеческому адресу appengine.com.ua. Обновите ваши закладки :)

Вордпресс, оказывается, берет целых 10 баксов за привязку домена, зато умеет включать для него Google MX.


Пара ссылок

26.10.2009

Здесь немного общей информации.

Здесь хулят книгу по GAE от Apress O’Reilly Media. Печально, но похоже все так и есть.


Follow

Get every new post delivered to your Inbox.