Ближе к Cassandra

Олег Анастасьев
ведущий разработчик,
команда платформы
odnoklassniki.ru
7 M онлайн
40M в день, 80M в месяц
!

~ 300 000 www страниц/сек, 
20 ms на страницу
>280 ГБит/сек
!

> 6 600 серверов в 5 ЦОД
99.9% java
Cassandra @
* Начинали в 2010

- своя версия, основана на 0.6
- целились в:
выживаемость при отказе ЦОД, масштабируемость,
простота эксплуатации, адаптируемость

* Сейчас

- 23 разных кластера
- 418 всего нод
- 240 TB данных
!

- пережили несколько отказов ЦОД
“Раз” - котенок
!

самый быстрый
Класс! 103 927

Вы и 103 927
Виджет Класс!
* Он везде

- На каждой странице по несколько
- В ленте активности
- На сайтах в Интернетах
!

* Он на всем

- Картинки и альбомы
- Видео
- Посты и комментарии
- Ссылки на внешние сайты

Класс! 103 927
Виджет Класс!
* Высоко нагруженный

- 1 000 000 чтений/сек, 3 000 записей/сек
!

* Сложный профиль

Класс! 103 927

- Много чтений 
- Длинный хвост (40% чтений случайны)
- Чувствительна к вариациям скорости
- 3TB данных (9TB с RF) и растет
- ~ 60 млрд класс!ов к ~6 млрд объектов
Простое решение
SQL табличка 

RefId:long

RefType:byte

UserId:long

Created

9999999999

PICTURE(2)

11111111111

11:00

!

для отрисовки

Вы и 4256

!
SELECT TOP 1 WHERE RefId,RefType,UserId=?,?,? 
(98% are NONE)

!
SELECT COUNT (*) WHERE RefId,RefType=?,? 
(80% are 0)

!
SELECT TOP N * RefId,RefType=? WHERE IsFriend(?,UserId)
Простая проблема
SQL табличка 

RefId:long

RefType:byte

UserId:long

Created

9999999999

PICTURE(2)

11111111111

11:00

!

для отрисовки

Вы и 4256

!
SELECT TOP 1 WHERE RefId,RefType,UserId=?,?,? 
(98% are NONE)

= N >=1

!
SELECT COUNT (*) WHERE RefId,RefType=?,? 
(80% are 0)

= M>N

!
SELECT TOP N * RefId,RefType=? WHERE IsFriend(?,UserId) 

= N*140
Пробуем Cassandra
LikeByRef (	

refType byte,	

refId bigint,	

userId bigint,	

!
PRIMARY KEY ( (RefType,RefId), UserId)	


для отрисовки

LikeCount (	

refType byte,	

refId bigint,	

likers counter,	

!
PRIMARY KEY ( (RefType,RefId))	


Вы и 4256

!
SELECT FROM LikeCount WHERE RefId,RefType=?,? 
(80% are 0)

!
SELECT * FROM LikeByRef WHERE RefId,RefType,UserId=?,?,? 
(98% are NONE)

= N*20%
>11 M iops
* Пробуем решить по быстрому
LikeByRef (	

! refType byte,	

refId bigint,	

! userId bigint,	

!
! PRIMARY KEY ( (RefType,RefId, UserId) )

!
SELECT TOP N * RefId,RefType=? WHERE IsFriend(?,UserId)

- Нужен Order Pres Partitioner 
(Random не масштабируется)

- Запросы по диапазону ключей (KeyRange)
- Больше сетевого оверхеда
- Партиций >10x, Размер данных > x2
Колоночный блум фильтр

* Что делает

- Хранит пары (PartKey, ColumnKey) в SSTable
*-Filter.db 

!

И это хорошо
- Убрали 98 % чтений с диска
- Меньше ложных попаданий
* Но все таки…
- Они стали очень большими

*

Проблемы с GC Promotion Failures
.. фиксим (CASSANDRA-2466)
Уже готово ?
1. COUNT()

сервер приложений	

> 400	


00

2. EXISTS
cassandra

- 2 похода для отрисовки (COUNT+RR) 	

- THRIFT медленный, когда много клиентов	

- EXISTS() дает 200 Gbit/sec (140*8*1Mps*20%)
Совместить!
get() : LikeSummary

odnoklassniki-like	

Сетевой Бизнес Intf

Кеш счетчиков
cassandra
Кеш соц. графа

- one-nio remoting (быстрее родного nio в java)	

- клиенты знают топологию кластера
Вместе это хорошо
* Быстрый запрос TOP N друзей

1. Берем друзей из кеша соц графа
2. Проверяем по блум фильтру в памяти
3. Читаем с диска пока получим N
!

* Свои кеши

- Специализированные под приложение
!

* Свое объединение реплик данных

- … можно находить и разрешать кофликты
Слушаем мутации
// Implement it	
interface StoreApplyListener {	
boolean preapply(String key, 	
ColumnFamily data); 	
}

// and register with CFS	
store=Table.open(..)	
.getColumnFamilyStore(..);	
store.setListener(myListener);

* Регистрируем слушателя

после применения логов но до госсипа

!

* RowMutation.apply()

и расширяем логику записей
+ На репликах, хинты, ReadRepairs
Счетчики с быстрым чтением
* Кеш счетчиков

LikeCount (	

refType byte,	

refId bigint,	

ip inet,
counter int
PRIMARY KEY ( (RefType,RefId),

- Off heap (sun.misc.Unsafe)
- Компакен (30M in 1G RAM)
- Читаем только кеш локальной ноды
!

* Репликацию кеша
-

проблема холодного кеша реплик
делаем (NOP) записи
меньше чтений
учитывает длинный хвост
Вариации скорости
* Что делает Cassandra

1. Взять 1 ноду с данным и CL-1 - дайджест
2. Ждем данные и дайджест
3. Сравниваем и возвращаем (или RR)

* Ноды притормаживают

- Изза: SEDA, ротируем commit log , внезапно
много IO, заткнулась сеть или пропала,
внезапно много IO мимо кеша диска…

* И что видят клиенты ?

- Пики времени обработки запросов
- Можно только ждать (до таймаута)
Read Latency leveling
* “Параллельная” обработка чтений

1. Запрашиваем данные со всех реплик сразу
2. Ждем сколько надо CL ответов реплик

!

* И это хорошо

- Всегда минимальное время обработки
- Та же нагрузка при отказе ЦОД
!

* что (не так уж) плохо

- “Дополнительная” нагрузка и трафик
More tiny tricks
* Для SSD io

- Deadline IO шедулер
- 64k -> 4k размер запроса на чтение

!

* Хинт Лог

- Коммит лог для хинтов
- Ждем доставки всех хинтов на старте

!

* Селективый компактор

- Чаще читаем из CF - чаще компактим
“Два” - котенок
!

самый пухлый
* Сообщения в чатах

- Читаем свежую страницу на открытии
- длинный хвост (80%) для остальных

- 150 млрд, 100 TB на дисках
- Чтений больше: 120k в сек; 8k записей
Сообщение это структура
Message (	

chatId, msgId, 	

!
created, type,userIndex,deletedBy,...

text	

)	


MessageCF (	

chatId, msgId, 	

!
data blob,
!
PRIMARY KEY ( chatId, msgId )	


- Все сообщения чата в 1 партиции
- Каждое представлено блобом
для уменьшения накладных расходов
!

- Стало плохо
Возможны конфиликтующие изменения 
одного сообщения

(пользователи, антиспам, чистка,…)
Легкое разрешение конфликтов
get
get

(version:ts1, data:d1)

(version:ts1, data:d1)
write( ts1, data2 )
delete(version:ts1)	

insert(version: ts2=now(), data2)
Messages (	

chatId, msgId,	

version timestamp,	

data blob	

PRIMARY KEY ( chatId, msgId, version )	


write( ts1, data3 )
delete(version:ts1)	

insert(version: ts3=now(), data3)
(ts2, data2)	

(ts3, data3)

- merged on read
Специализированный кеш
* Опять. Потому что можем

- Off-heap (Unsafe)
- Кеширует только свежайшие страницы
- Пишет состояние в локальный (system) CF
И ключи, и значения
последовательное чтение на старте

- Компрессия данных в памяти
В 2 раза больше памяти бесплатно
Делим диски
* 4U HDDx24, до 4 TB на сервер

- Size tiered compaction = файл до 4 TB
- RAID10 ? LCS ?

!

* Делим CF на 256 кусочков
* Стало хорошо

- Более частые, но меньшие сбросы мемтаблиц
- Столько же работы для компактора
но более мелкими наборами

- Можно распределять между дисками
Политика распределения
* Из коробки

- “Берем наиболее свободный по месту диск”
* И теперь некоторые из них
- слишком нагружены на ввод-вывод
!

* По поколениям

- На каждом диске хранится одинаковое

количество sstables одного поколения

работает лучше всех для ЖД
“Три” -

Уродливый и страшный
это-ж, братцы, Франкенштейн !
* Список чатов

- мало данных (230GB)
- есть горячий набор, короткий хвост (5%)
- список часто пересортировывается
- 130k чтений/сек, 21k записей/сек
Конфликтные изменения
* List<ЧатOverview> в 1 блоб

.. или мы получим много могилок

!

* Много конфликтов

все меняют 1 колонку
!

* Определен алгоритм слияния 
* Нужно детектирования конфликтов
Векторные часы
* Voldemort

- byte[] ключ -> byte[] значение + ВЧ
- Все клиенты - координаторы
- Заменяемые виды хранилищ
!

* Ну и заменили

- На СС таблицы от CS 0.6 
- Со специализированным кеш
кеши. мы знаем как.
Скорость
* Кластер из 3 нод, RF = 3

- Intel Xeon CPU E5506 2.13GHz 

RAM: 48Gb, 1x HDD, 1x SSD

!

* 8 байт ключ -> 1 KB значение
!

* Дает

- 75 k чтений/сек и 15 k записей
Зачем нам C* ?
* Готовые компоненты РБД

быстрое хранилище, gossip, 
надежные асинхронные сообщения, детектор сбоев,
топология, последовательная обработка, ...

* Данные могут быть структутированы
сложнее, чем byte[] ключ -> byte[] значение

* Выполнила наши цели
* Сделано на java
СПАСИБО
Олег Анастасьев
!
oa@odnoklassniki.ru
odnoklassniki.ru/oa
@m0nstermind

!

github.com/odnoklassniki
shared-memory-cache
кеш для java вне кучи и в
разделяемой памяти

one-nio
ремотинг быстрее, чем java nio; с
автомагической встроенной
сериализацией

Олег Анастасьев "Ближе к Cassandra". Выступление на Cassandra Conf 2013

  • 1.
    Ближе к Cassandra ОлегАнастасьев ведущий разработчик, команда платформы odnoklassniki.ru
  • 2.
    7 M онлайн 40Mв день, 80M в месяц ! ~ 300 000 www страниц/сек, 20 ms на страницу >280 ГБит/сек ! > 6 600 серверов в 5 ЦОД 99.9% java
  • 3.
    Cassandra @ * Начиналив 2010 - своя версия, основана на 0.6 - целились в: выживаемость при отказе ЦОД, масштабируемость, простота эксплуатации, адаптируемость * Сейчас - 23 разных кластера - 418 всего нод - 240 TB данных ! - пережили несколько отказов ЦОД
  • 4.
  • 5.
  • 6.
    Виджет Класс! * Онвезде - На каждой странице по несколько - В ленте активности - На сайтах в Интернетах ! * Он на всем - Картинки и альбомы - Видео - Посты и комментарии - Ссылки на внешние сайты Класс! 103 927
  • 7.
    Виджет Класс! * Высоконагруженный - 1 000 000 чтений/сек, 3 000 записей/сек ! * Сложный профиль Класс! 103 927 - Много чтений - Длинный хвост (40% чтений случайны) - Чувствительна к вариациям скорости - 3TB данных (9TB с RF) и растет - ~ 60 млрд класс!ов к ~6 млрд объектов
  • 8.
    Простое решение SQL табличка
 RefId:long RefType:byte UserId:long Created 9999999999 PICTURE(2) 11111111111 11:00 ! для отрисовки Вы и 4256 ! SELECT TOP 1 WHERE RefId,RefType,UserId=?,?,? (98% are NONE) ! SELECT COUNT (*) WHERE RefId,RefType=?,? (80% are 0) ! SELECT TOP N * RefId,RefType=? WHERE IsFriend(?,UserId)
  • 9.
    Простая проблема SQL табличка
 RefId:long RefType:byte UserId:long Created 9999999999 PICTURE(2) 11111111111 11:00 ! для отрисовки Вы и 4256 ! SELECT TOP 1 WHERE RefId,RefType,UserId=?,?,? (98% are NONE) = N >=1 ! SELECT COUNT (*) WHERE RefId,RefType=?,? (80% are 0) = M>N ! SELECT TOP N * RefId,RefType=? WHERE IsFriend(?,UserId) = N*140
  • 10.
    Пробуем Cassandra LikeByRef ( refTypebyte, refId bigint, userId bigint, ! PRIMARY KEY ( (RefType,RefId), UserId) для отрисовки LikeCount ( refType byte, refId bigint, likers counter, ! PRIMARY KEY ( (RefType,RefId)) Вы и 4256 ! SELECT FROM LikeCount WHERE RefId,RefType=?,? (80% are 0) ! SELECT * FROM LikeByRef WHERE RefId,RefType,UserId=?,?,? (98% are NONE) = N*20%
  • 11.
    >11 M iops *Пробуем решить по быстрому LikeByRef ( ! refType byte, refId bigint, ! userId bigint, ! ! PRIMARY KEY ( (RefType,RefId, UserId) ) ! SELECT TOP N * RefId,RefType=? WHERE IsFriend(?,UserId) - Нужен Order Pres Partitioner (Random не масштабируется) - Запросы по диапазону ключей (KeyRange) - Больше сетевого оверхеда - Партиций >10x, Размер данных > x2
  • 12.
    Колоночный блум фильтр *Что делает - Хранит пары (PartKey, ColumnKey) в SSTable *-Filter.db ! И это хорошо - Убрали 98 % чтений с диска - Меньше ложных попаданий * Но все таки… - Они стали очень большими * Проблемы с GC Promotion Failures .. фиксим (CASSANDRA-2466)
  • 13.
    Уже готово ? 1.COUNT() сервер приложений > 400 00 2. EXISTS cassandra - 2 похода для отрисовки (COUNT+RR) - THRIFT медленный, когда много клиентов - EXISTS() дает 200 Gbit/sec (140*8*1Mps*20%)
  • 14.
    Совместить! get() : LikeSummary odnoklassniki-like СетевойБизнес Intf Кеш счетчиков cassandra Кеш соц. графа - one-nio remoting (быстрее родного nio в java) - клиенты знают топологию кластера
  • 15.
    Вместе это хорошо *Быстрый запрос TOP N друзей 1. Берем друзей из кеша соц графа 2. Проверяем по блум фильтру в памяти 3. Читаем с диска пока получим N ! * Свои кеши - Специализированные под приложение ! * Свое объединение реплик данных - … можно находить и разрешать кофликты
  • 16.
    Слушаем мутации // Implementit interface StoreApplyListener { boolean preapply(String key, ColumnFamily data); } // and register with CFS store=Table.open(..) .getColumnFamilyStore(..); store.setListener(myListener); * Регистрируем слушателя после применения логов но до госсипа ! * RowMutation.apply() и расширяем логику записей + На репликах, хинты, ReadRepairs
  • 17.
    Счетчики с быстрымчтением * Кеш счетчиков LikeCount ( refType byte, refId bigint, ip inet, counter int PRIMARY KEY ( (RefType,RefId), - Off heap (sun.misc.Unsafe) - Компакен (30M in 1G RAM) - Читаем только кеш локальной ноды ! * Репликацию кеша - проблема холодного кеша реплик делаем (NOP) записи меньше чтений учитывает длинный хвост
  • 18.
    Вариации скорости * Чтоделает Cassandra 1. Взять 1 ноду с данным и CL-1 - дайджест 2. Ждем данные и дайджест 3. Сравниваем и возвращаем (или RR) * Ноды притормаживают - Изза: SEDA, ротируем commit log , внезапно много IO, заткнулась сеть или пропала, внезапно много IO мимо кеша диска… * И что видят клиенты ? - Пики времени обработки запросов - Можно только ждать (до таймаута)
  • 19.
    Read Latency leveling *“Параллельная” обработка чтений 1. Запрашиваем данные со всех реплик сразу 2. Ждем сколько надо CL ответов реплик ! * И это хорошо - Всегда минимальное время обработки - Та же нагрузка при отказе ЦОД ! * что (не так уж) плохо - “Дополнительная” нагрузка и трафик
  • 20.
    More tiny tricks *Для SSD io - Deadline IO шедулер - 64k -> 4k размер запроса на чтение ! * Хинт Лог - Коммит лог для хинтов - Ждем доставки всех хинтов на старте ! * Селективый компактор - Чаще читаем из CF - чаще компактим
  • 21.
  • 22.
    * Сообщения вчатах - Читаем свежую страницу на открытии - длинный хвост (80%) для остальных - 150 млрд, 100 TB на дисках - Чтений больше: 120k в сек; 8k записей
  • 23.
    Сообщение это структура Message( chatId, msgId, ! created, type,userIndex,deletedBy,...
 text ) MessageCF ( chatId, msgId, ! data blob, ! PRIMARY KEY ( chatId, msgId ) - Все сообщения чата в 1 партиции - Каждое представлено блобом для уменьшения накладных расходов ! - Стало плохо Возможны конфиликтующие изменения одного сообщения
 (пользователи, антиспам, чистка,…)
  • 24.
    Легкое разрешение конфликтов get get (version:ts1,data:d1) (version:ts1, data:d1) write( ts1, data2 ) delete(version:ts1) insert(version: ts2=now(), data2) Messages ( chatId, msgId, version timestamp, data blob PRIMARY KEY ( chatId, msgId, version ) write( ts1, data3 ) delete(version:ts1) insert(version: ts3=now(), data3) (ts2, data2) (ts3, data3) - merged on read
  • 25.
    Специализированный кеш * Опять.Потому что можем - Off-heap (Unsafe) - Кеширует только свежайшие страницы - Пишет состояние в локальный (system) CF И ключи, и значения последовательное чтение на старте - Компрессия данных в памяти В 2 раза больше памяти бесплатно
  • 26.
    Делим диски * 4UHDDx24, до 4 TB на сервер - Size tiered compaction = файл до 4 TB - RAID10 ? LCS ? ! * Делим CF на 256 кусочков * Стало хорошо - Более частые, но меньшие сбросы мемтаблиц - Столько же работы для компактора но более мелкими наборами - Можно распределять между дисками
  • 27.
    Политика распределения * Изкоробки - “Берем наиболее свободный по месту диск” * И теперь некоторые из них - слишком нагружены на ввод-вывод ! * По поколениям - На каждом диске хранится одинаковое количество sstables одного поколения работает лучше всех для ЖД
  • 28.
    “Три” -
 Уродливый истрашный это-ж, братцы, Франкенштейн !
  • 29.
    * Список чатов -мало данных (230GB) - есть горячий набор, короткий хвост (5%) - список часто пересортировывается - 130k чтений/сек, 21k записей/сек
  • 30.
    Конфликтные изменения * List<ЧатOverview>в 1 блоб .. или мы получим много могилок ! * Много конфликтов все меняют 1 колонку ! * Определен алгоритм слияния * Нужно детектирования конфликтов
  • 31.
    Векторные часы * Voldemort -byte[] ключ -> byte[] значение + ВЧ - Все клиенты - координаторы - Заменяемые виды хранилищ ! * Ну и заменили - На СС таблицы от CS 0.6 - Со специализированным кеш кеши. мы знаем как.
  • 32.
    Скорость * Кластер из3 нод, RF = 3 - Intel Xeon CPU E5506 2.13GHz 
 RAM: 48Gb, 1x HDD, 1x SSD ! * 8 байт ключ -> 1 KB значение ! * Дает - 75 k чтений/сек и 15 k записей
  • 33.
    Зачем нам C*? * Готовые компоненты РБД быстрое хранилище, gossip, надежные асинхронные сообщения, детектор сбоев, топология, последовательная обработка, ... * Данные могут быть структутированы сложнее, чем byte[] ключ -> byte[] значение * Выполнила наши цели * Сделано на java
  • 34.
    СПАСИБО Олег Анастасьев ! [email protected] odnoklassniki.ru/oa @m0nstermind ! github.com/odnoklassniki shared-memory-cache кеш дляjava вне кучи и в разделяемой памяти one-nio ремотинг быстрее, чем java nio; с автомагической встроенной сериализацией