About |

Быстрый random() select

Я долго думал как без костылей, а только на уровне базы (PostgreSQL) сделать быстрый select * from table order by random(); К сожалению ничего хорошего найти не получилось. Вот explain на базе из около 300 000 записей:

     Limit  (cost=29127.46..29127.71 rows=100 width=358) (actual time=2531.542..2531.900 rows=100 loops=1)

       ->  Sort  (cost=29127.46..29850.98 rows=289406 width=358) (actual time=2531.536..2531.658 rows=100 loops=1)

             Sort Key: (random())

             Sort Method:  top-N heapsort  Memory: 67kB

             ->  Seq Scan on wall_wallpapper  (cost=0.00..18066.58 rows=289406 width=358) (actual time=0.033..1139.509 rows=276459 loops=1)

     Total runtime: 2532.240 ms

Не очень быстро. Очевидным решением было генерировать массив случайных значений на уровне кода и выбирать жанные по ключу. Чтобы выбирать по сгенерированным в коде значениям нам нужен непрерывный интервал значений (чтобы не было “дырок”). Это решение мне не очень нравится, но работает оно очень быстро. Итак для этого я создал таблицу с непрерывным интервалом значений, и ключом на исходную таблицу (в моем случае таблицу с обоями) -

Column  |  Type   |                         Modifiers                          | Storage | Description 

–––+–––+––––––––––––––––––––+–––+––––-

 id      | integer | not null default nextval(‘wall_wallhash_id_seq’::regclass) | plain   | 

 h_id    | integer | not null                                                   | plain   | 

 w_id_id | integer | not null                                                   | plain   | 

Indexes:

    “wall_wallhash_pkey” PRIMARY KEY, btree (id)

    “wall_wallhash_h_id_key” UNIQUE, btree (h_id)

    “wall_wallhash_w_id_id” btree (w_id_id)

Foreign-key constraints:

    “wall_wallhash_w_id_id_fkey” FOREIGN KEY (w_id_id) REFERENCES wall_wallpapper(id) DEFERRABLE INITIALLY DEFERRED

Has OIDs: no

где h_id — значение на непрерывном интервале, w_id_id — ключ на таблицу с обоями (джанга сгенерировала такое название, т.к. я сначала хотел делать триггером и назвать ключ w_id)
Эту таблицу я заполнил непрерывным интервалом с ссылками на таблицу обоев -
    
    CREATE SEQUENCE tmp_hash;
      insert into wall_wallhash (h_id, w_id_id) (SELECT nextval(‘tmp_hash’), id from wall_wallpapper);

 В коде я генерирую случайные значения в интервале от 0 до max() значения таблицы ключей. И выбираю данные примерно следующим запросом - 

    select * from wall_wallpapper where active=TRUE and id in (select w_id_id from wall_wallhash where h_id in (23260,79052,115503,235463,22031,17547,91859,95542,17186,244111,136111, …. 101076,218414,227782,120009,25861))

     Nested Loop  (cost=722.88..1526.57 rows=100 width=358) (actual time=3.687..6.449 rows=100 loops=1)

       ->  HashAggregate  (cost=722.88..723.88 rows=100 width=4) (actual time=3.605..3.740 rows=100 loops=1)

             ->  Bitmap Heap Scan on wall_wallhash  (cost=397.92..722.63 rows=100 width=4) (actual time=2.180..3.390 rows=100 loops=1)

                   Recheck Cond: (h_id = ANY     (‘{23260,79052,115503,235463,22031,17547,……..101076,218414,227782,120009,25861}’::integer[]))

                   ->  Bitmap Index Scan on wall_wallhash_h_id_key  (cost=0.00..397.90 rows=100 width=0) (actual time=2.140..2.140 rows=100 loops=1)

                         Index Cond: (h_id = ANY     (‘{23260,79052,115503,235463,….. 227782,120009,25861}’::integer[]))

       ->  Index Scan using wall_wallpapper_pkey on wall_wallpapper  (cost=0.00..8.01 rows=1 width=358) (actual time=0.021..0.023 rows=1 loops=100)

             Index Cond: (wall_wallpapper.id = wall_wallhash.w_id_id)

             Filter: (wall_wallpapper.active)

     Total runtime: 6.737 ms

Как видно время уменьшилось почти в 400 раз! И чем больше база тем больше будет разница в этих значениях. 

 continue reading

Сравнение производительности Nginx и Lighttpd

Сделали ради интереса небольшой тест производительности двух легких и быстрых вебсерверов (nginx и lighttpd).

Результаты

Результаты по скорости примерно одинаковы. На конфиге по умолчанию лайти немного проигрывал nginx’у и на больших и на маленьких файлах. После отключения лишних модулей и добавления пары опций (см. конфиг вверху страницы) лайти на небольших файлах начал выигрывать.

Пример работы программы ab -c 10 -n 100000 http://127.0.0.1/test_ind.html -

nginx маленький html файл выдал 28488.55 запросов в секунду.

А лайти, — lighttpd маленький html файл выдал 29411.67 запросов в секунду.

После проведения серии тестов (каждый тест мы запускали 3 раза и брали среднее арифметическое значение), были получены следующие результаты:

Как видно из таблицы nginx немного выигрывает на больших файлах а лайти на маленьких. ИМХО, разница в скорости совершенно несущественна. Поэтому выбор веб-сервера для статики (или проксирования) следует делать из других соображений. Мы используем оба веб-сервера и особых нареканий нет ни к тому ни к другому. К плюсам nginx стоит отнести более активную разработку и немного меньшую нагрузку на процессор при проведении тестов.

 continue reading

Postgresql полнотекстовый поиск и Django

 Postgresql с версии 8.3 поддерживает full text search из коробки. А вещь эта очень полезная и мощная.

В этом посте я приведу пример для английского языка. Но тоже можно сделать и для русского.

Допустим у нас есть следующая модель:

from django.db import models
class Item(models.Model): title = models.CharField(max_length=200) body = models.TextField() 
 continue reading

Создание Postgresql trigger на PL/Python

PostgreSQL отличная и функциональная база данных. Одна из замечательных вещей, которых она поддерживает это создание функций на разных языках программирования ( PL/pgSQL, PL/Tcl, PL/Perl, PL/Python), а не только PL/SQL как большинство СУБД. В этом посте я приведу пример функции для триггера на PL/Python.

 continue reading

Деплоинг Django приложения на Nginx или разворачивание связки Nginx+uWsgi

Итак, начнем:

нам понадобится установленный пакет virtualenv

virtualenv $DOC_ROOT
cd $DOC_ROOT/bin
./easy_install django
cp /var/www/wsgi_app.py $DOC_ROOT/$pypath/
nano /$DOC_ROOT/$pypath/wsgi_app.py #edit PATH_PROJECT to fit your needs

wsgi_app.py listing:

import os, sys                                                                                                                                        
PATH_BASE = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
PATH_PROJECT = os.path.join(PATH_BASE, 'pyprojectname')
activate_this = os.path.join(PATH_BASE, 'bin/activate_this.py')
execfile(activate_this, dict(__file__=activate_this))

sys.path.append(PATH_PROJECT)
sys.path.append(PATH_BASE)

os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'

import django.core.handlers.wsgi
application = django.core.handlers.wsgi.WSGIHandler()

nginx sample config:

server {                                                                                                                                              
listen 80;
server_name sitename.name www.sitename.name;
location / {
uwsgi_pass unix:///var/run/uwsgi.socket;
include uwsgi_params;
uwsgi_param UWSGI_CHDIR /var/www/sitename.name/pypath;
uwsgi_param UWSGI_PYHOME /var/www/sitename.name/;
uwsgi_param UWSGI_SCRIPT wsgi_app;
}
location ~* \.(jpg|gif|png|css|js|swf)$ {
root /var/www/sitename.name/pypath/media/;
}
}

Бот генератор ругательств

For fun, написали бота генератора ругательств в твиттере — Генератор Ругательств.

Каждые 10 минут постит в твиттер какую нибудь гадость =). Если у вас не хватает цензурных слов или вас кто-то обозвал и вы хотите хорошенько ответить — то этот бот для вас!

Парочка примеров его работы:  continue reading

Анонимные ссылки

Сделали проект для своих нужд, может быть еще кому-то пригодится:
Анонимные ссылки

Зачем это нужно?

Это нужно, чтобы скрыть с какого сайта пришел посетитель от администратора сайта на который стоит ссылка. Т.е. владелец сайта, на которого вы поставили анонимную ссылку не увидет в логах веб-сервера referer вашего сайта.

Пример использования:
http://anonym-web.com/?http://aaa.b000.ru/

Исходники можно выгрузить из репозитория Mercurial:

hg clone https://pi11@bitbucket.org/pi11/anonym-web

Добавили интеграцию с memori.ru в Wallp Project

Во-первых, УРА!

Wallp Project обзавелся интеграцией с memori.ru. Теперь при добавлении обоев создается закладка на сервисе закладок memori.ru.

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

И в-третьих, готовый кусок кода на питоне для работы с API memori.ru можно сказать с нашего pastebin сервиса — Memori.ru API Python example.

 continue reading

Автоматический following в twitter’e на Python

Т.к. наш Твиттер-бот описанный в предыдущем посте не имел фолловеров, мы решили написать небольшой скрипт для фолловинга за друзьями пользователя твиттера. Тяжелое предложение для восприятия получилось =).

Цель — получить фолловеров в твиттере.
Решение: Прошерстив API твиттера вы обнаружите, что проспамить твиттер не так просто — ограничение на количество запросов, регистрация через капчу, и т.д. Единственный вариант автоматизации который можно заюзать — это фолловинг за пользователями (которые в ответ тоже начинают “дружить” с ботом).

Итак вот скрипт на питоне: http://p.py6.ru/view/7/, готовый пример для интеграции в ваши проекты: python example — эта функция например используется для хостинга картинок. Запускается из консоли. Зависимость — python-twitter. (Для Debian достаточно выполнить — apt-get install python-twitter) Если вы используете Windows — то сами разбирайтесь, что вам нужно поставить.

Help:
Usage: twit-follow.py [options]

Options:
  -h, --help            show this help message and exit
  -u USERNAME, --username=USERNAME
                        Ваш логин в твиттере
  -p PASSWORD, --password=PASSWORD
                        Ваш пароль в твиттере
  -f FOLLOW_USER, --follow=FOLLOW_USER
                        Username (или id) Твиттер пользователя, для анализа и
                        фоловинга за его пользователями
  -d, --debug           Отображать подробно ход процесса выполнения,
                        диагностика

Пример запуска — 
python twit-follow.py -u ваш_логин -p 'Ваш_пароль' -f имя_пользователя_чьих_друзей_мы_будем_добавлять

Твиттер-бот.

Прикрутили к своему jabber-боту постинг анекдотов в твиттер. Заценить — http://twitter.com/anekbot Анекдоты более 140 символов обрезаются и дается ссылка на продолжение анекдота — an7.ru. Ссылки само собой рубятся py6анком.

Вот сусок кода на питоне, который собственно постит анекдоты в твиттер — http://p.py6.ru/view/12/.