Успеть за 100 миллисекунд: контекстная реклама на SphinxДмитрий Хасанов Avito
Контекст
Требования• 50 млн рекламных объявлений • 300 тыс запросов в минуту • 100 мс на ответ
Принцип работы• отбор
• текстовый запрос
• регион
• категория
• время суток
• сортировка: bid * CTR
Модели• десятки параметров • миллионы коэффициентов • обратная связь • эксперименты, АБ-тесты
Варианты решения• реляционная БД • in-memory БД • самописный инструмент • внешний поисковый движок
Поиск в Sphinx'е• индексация объявлений • поиск • ранжирование
Индексация• БД -> TSV -> индекс • один мастер • разливка rsync'ом
Поиск• запрос • параметры окружения • параметры пользователя
Ранжирование• UDF для предсказания CTR • компиляция на одной ноде • разливка rsync'ом
UDF — user defined function#include "sphinxudf.h"
double myudf ( SPH_UDF_INIT * init, SPH_UDF_ARGS * args, char * error_flag ) { double res = 0; // ... return res; }
int myudf_init (SPH_UDF_INIT * init, SPH_UDF_ARGS * args, char * error_message) { return 0; }
void myudf_deinit ( SPH_UDF_INIT * init ) { }
int myudf_ver () { return SPH_UDF_VERSION; }
UDF: коэффициенты внутриpostgres -> coeffs.h
#include "sphinxudf.h" #include coeffs.h
double myudf ( SPH_UDF_INIT * init, SPH_UDF_ARGS * args, char * error_flag ) { double result = f(coeffs); // ... return result; }
...
UDF: компиляция, деплойgcc -fPIC -shared -o ./myudf.so ./myudf.c
mysql> create function myudf returns float soname 'myudf.so';
mysql> show plugins;+------+--------------------+------------------+-------+-------+| Type | Name | Library | Users | Extra |+------+--------------------+------------------+-------+-------+| udf | myudf | myudf.so | 0 | FLOAT |+------+--------------------+------------------+-------+-------+
UDF: вызовmysql> select myudf(4);+----------------+| myudf(4) |+----------------+| 0.38 |+----------------+1 row in set (0.00 sec)
Инфраструктура
Хитрости: распределённый индексindex adverts_distr { type = distributed agent = server1:9312:adverts agent = server2:9312:adverts }
update adverts_distr set bid=100 where id=1;
Хитрости: подзапросыselect myudf() as sort_slowfrom advertsorder by sort_fast desc, sort_slow desclimit 100
select * from (select myudf() as sort_slowfrom advertsorder by sort_fast desclimit 100
) order by sort_slow desc
Хитрости: мультизапросыselect myudf() from adverts where foo=bar order by sort_1 asc limit 100;select myudf() from adverts where foo=bar order by sort_2 asc limit 100;
Хитрости: индекс для атрибутов
Медленно:select * from adverts where category_id=109;
Быстро:select * from adverts where match('cat_109');
Хитрости: постфильтрация• часто изменяемое • зависит от пользователя • не селективное
Хитрости: tmpfs
mount -t tmpfs -o size=10g tmpfs /home/sphinx/sources
Результат
Дмитрий Хасанов[email protected] github.com/pik4ez