chronicle map
TRANSCRIPT
Chronicle MapРоман Левентов
Chronicle Software
ПО для
высокопроизводительных
систем на Java
• Финтех, трейдинг
• Ставки
• Туризм
Chronicle Map
Key‐value store в памяти github.com/OpenHFT/Chronicle-Map
LGPL v3
Зачем?
Нужды трейдинга (1)
Задержки = деньги
Обычные реляционные БД
Out-of-process кеши (redis)
Chronicle Map: медианная
задержка (latency)
< 1 микросекунды
Нужды трейдинга (2)
Много источников событий
из разных процессов
Пример: агрегация данных
из нескольких бирж
Зачем еще нужен
доступ
из нескольких
процессов?
Тренды в железе
Сотни ГБ памяти
Несколько сокетов
⇒ NUMA
NUMA
NUMA
Доступ к памяти «чужого»
сокета на ~30% медленнее
NUMA: архитектура 1
NUMA node 1
core core core
NUMA node 2
core core core
Процесс
NUMA: архитектура 2
NUMA node 1
core core core
NUMA node 2
core core core
Процесс 1 Процесс 2
Выигрыш до 30% / 2 = 15%
Зачем несколько JVM?
Сборка мусора
• Меньше heap — меньше
паузы
• Garbage-free подсистемы
– Без пауз
– Цена барьеров
Compressed Oops
Зачем несколько JVM?
Heap = 40 GB
64-битные ссылки
Heap = 15 GB
32-битные ссылки
Heap = 15 GB
32-битные ссылки
Профит
Зачем несколько JVM?
Конфигурация на уровне ОС
• Affinity
• Приоритеты
• cgroups
Нужды трейдинга (2)
Много источников событий
из разных процессов
Hazelcast
Большинство IMDG
MapDB
Много источников
Chronicle Map:
Доступ из нескольких
процессов
Масштабирование записи
ограничено кол-вом ядер в
системе
Нужды трейдинга (3)
Очень большая частота
событий ⇒ обновлений базы
Репликация:
200k RPS
× 3 KB state updates
= 5 Gbit
Нужды трейдинга (3)
Сеть не тянет ⇒ очереди
репликации переполняются
Нужна «фильтрация»
Chronicle Map: репликация
без очередей, гарантия
прогресса
Не серебряная пуля
Multi‐key запросы
изолированы,
но не атомарны
Не durable, только persistent
Только асинхронная
репликация
Как?
Как сделать доступ
из нескольких
процессов?
Loopback, sockets
JVM 1 DB process
JVM 2Kernel
DB memory
Loopback, sockets
Поход в ядро: 100s of ns
× 4
2…∞ лишних копирований
данных
Непредсказуемые задержки:
1..10 микросекунд
Loopback, sockets
• Redis, Memcached
Разделяемая память
БД на C + JNI
БД на Java
• sun.misc.Unsafe
БД на C + JNI
Java code DB code
JNIJVM 1
Kernel
DB shared memory
JVM 2
БД на Java (Unsafe)
Java codeDB code
Unsafe
JVM
Kernel
DB shared memory
JVM 2
Конечно, Unsafe!
Минусы JNI
• 50 ns
• 2 лишних копирования
• putIfAbsent(лямбда)
Минус Unsafe:
геморрой с блокировками
Блокировкине нужны
Блокировки
LockSupport.parkNanos(1000)
8000
Context switches — зло
Spin loops, жжем CPU
Бенчмарк:
Chronicle Map*,
one-nio*,
ConcurrentHashMap
Спасибо Андрею Барюдину
и Андрею Пангину
Ответы на заявки
Запрос:orderId, instrument, price, ± amount
+ покупка, − продажа
Key-value хранилище:instrument, price → orderId, ± leftover
4, 8 → 8, 8 байтов
28 байтов всего
Пример
#1, AAPL, $600, +10
AAPL, $600 → #1, +10
#2, AAPL, $600, −7
AAPL, $600 → #1, +3
#3, AAPL, $600, −5
AAPL, $600 → #3, −2
Реалистичные данные
5000
инструментов
Расп. Ципфа
Цены:
биномиальное
Бенчмарк
10 млн заявок, 8 потоков
~ 160 тыс. различных пар
(instrument, price)
Xeon E5-2650 v2 @ 2.60GHz
8 Cores / 16 HW Threads,
2 NUMA nodes, L3: 20MB
Скорость
174
296
367
Chronicle Map one-nio ConcurrentHashMap
В среднем наносекунд на запрос, меньше — лучше
Память
7,5
10,1
14,4
Chronicle Map one-nio ConcurrentHashMap
Общее потребление памяти, MB, меньше — лучше
Потребление памяти
определяет скорость
Память
7,5
10,1
14,4 15
20
ChronicleMap
one-nio CHM redis 32-bit,approx.
redis 64-bit,approx.
Общее потребление памяти, MB, меньше — лучше
Payload — 4,5 MB
Репликация
Репликация: боль
Сеть не тянет частые
обновления
⇒ Очередь переполняется
⇒ Надо делать сжатие
• Сложно
• Задержки
Репликация без логов
Записи помечаются на
репликацию в bit maps
Потребление памяти
не меняется
Но: только асинхронная,
eventually consistent
репликация
Кратко
По процессу на сокет
Аffinity, приоритеты
syscalls, JNI
Context switches
IPC через shared memory
Меньше памяти