А кто ты такой ?
● 5 лет опыта в web (кроме фриланса)
● 3 года в highload (3k+ request/min)
● 5k requests/min
● >1Tb raw data
● Real time analytic
Проект
«Big Data как секс у подростков: - все говорят об этом, - никто
реально не знает что это такое и что с этим делать, - каждый думает,
что другие уже успешно это делают и - каждый заявляет что
тоже успешно это делает.» (с) просторы интернета
“Данные становятся большими тогда, когда их
размер превращается в самостоятельную
проблему” (с) Роджер Магулас
Для нас Big Data началась с 10 млн. записей
И что ? Сколько данных это Big Data ?
SELECT COUNT(id) FROM tbl WHERE {...}
SELECT COUNT(DISTINCT token) FROM tbl WHERE {...}
Суммарное количество ивентов по фильтрам
Уникальное количество пользователей по фильтрам
400 msg/second = 24000 msg/minute
1440000 msg/hour = 34560000 msg/day
1 036 800 000 msg/month ( 1 млрд )
Да сколько там тех ивентов !
Мы используем Postgres.
Структура таблиц (изначальная)
Distinct ?
SELECT COUNT(DISTINCT token) FROM tbl WHERE {...}
in 40220 ms -> 11194 unique from 3489541 records
in 340220 ms -> 27616 unique from 9658465 records
SELECT COUNT(*) FROM (
SELECT token,COUNT(token) FROM tbl WHERE {...}
GROUP BY token
) q1
11194 ms -> 27616 unique from 9658465 records
Оптимизация запроса через группировку
Как жить дальше ?
MapReduce !!!!
Проблемы работы с исспользованием MapReduce
● Пользователь должен получить результат за допустимый промежуток времени (5-10
секунд)
● Если использовать препроцессинг (подсчитать все возможные варианты которые
может выбрать пользователь) то это будет очень долго
Postgres + HLL
--- Make a dummy table
CREATE TABLE helloworld (
id integer,
set hll
);
--- Insert an empty HLL
INSERT INTO helloworld(id, set) VALUES (1,
hll_empty());
--- Add a hashed integer to the HLL
UPDATE helloworld SET set = hll_add(set,
hll_hash_integer(12345)) WHERE id = 1;
--- Or add a hashed string to the HLL
UPDATE helloworld SET set = hll_add(set, hll_hash_text
('hello world')) WHERE id = 1;
--- Get the cardinality of the HLL
SELECT hll_cardinality(set) FROM helloworld WHERE id =
1;
Справимся и так !
Amazon EC2, c1.xlarge (4 CPU, 420Gb)
SELECT COUNT(id) FROM event WHERE {...}
Да, да, да. Индексы стоят, работают. Да, partitioning тоже включен и работает.
55 ms -> 342282 records
1714 ms -> 4014783 records
7524 ms -> 20109099 records
Для связывания этих таблиц исспользуется тригер OnInsert который делает +1 в поле cnt,
если совпадают event_type,campaign_id,item_id,date.
Но есть нюанс...
Batch insert
2014-02-10 00:20:39 UTC [21166]: [3-1] user=****,db=**** ERROR: deadlock detected
2014-02-10 00:20:39 UTC [21166]: [4-1] user=****,db=**** DETAIL: Process 21166 waits for ShareLock on
transaction 1623260082; blocked by process 21162.
Process 21162 waits for ShareLock on transaction 1623260084; blocked by process 21164.
Process 21164 waits for ExclusiveLock on tuple (16351,53) of relation 87300951 of database 16422;
blocked by process 21166.
INSERT INTO tbl VALUES (...),(...),(...),(...)
Выход ?
1. Переписать код на UPDATE и убрать тригерры
2. Игнорировать ошибку, уменьшив уровень изолированости транзакций
3. Ваш вариант
Postgres partitioning
CREATE TABLE events_01_2014 (
CHECK ( event_date >= DATE ‘2014-01-01’ AND event_date < DATE ‘2014-02-01’ )
) INHERITS (events);
+ триггер, окончание которого:
RETURN NULL;
Что делать если нет таблицы ?
Добавляем в триггер
IF NOT EXISTS(
SELECT * FROM information_schema.tables WHERE table_name = tableName
) THEN
EXECUTE 'CREATE TABLE '||tableName||' (...) INHERITS (‘||TG_TABLE_NAME||’);';
END IF;