Slide 1

Slide 1 text

@maxmaxmaxmax МАКСИМ КЛИМИШИН CTO zakaz.ua Redis как основная база для Python проекта

Slide 2

Slide 2 text

О чем разговор О чем это ‣ При чем тут Python ‣ Почему Redis ‣ Инструменты в Python ‣ Redis и ORM

Slide 3

Slide 3 text

При чем тут python

Slide 4

Slide 4 text

Типичный проект При чем тут Python ‣ Завязан на SQL базу ‣ Используется Django или Что-то + SQLAlchemy ‣ Реже используют Mongo или Couchdb ‣ Кеш – memcached или redis

Slide 5

Slide 5 text

На zakaz.ua При чем тут Python ‣ Django ‣ CouchDB ‣ Redis ‣ Solr

Slide 6

Slide 6 text

При чем тут Python ‣ Уперлись в скорость создания индекса CouchDB ‣ Постепенно мигрируем с CouchDB в Redis ‣ Активно используем Redis для очередей ‣ Активно используем для Pub/Sub ‣ Акционные предложения целиком в Redis

Slide 7

Slide 7 text

Проблемы При чем тут Python ‣ Реляционные и объектные базы работают с диском, а значит медленнее RAM ‣ Встроены разные механизмы кеширования ‣ Все в конечном итоге упираются в скорость записи или чтения

Slide 8

Slide 8 text

Дешево, но быстро При чем тут Python ‣ Время программистов уходит на оптимизации ‣ Любые тормоза в работе уменьшают конверсию/количество довольных клиентов ‣ Память сейчас стоит намного меньше времени

Slide 9

Slide 9 text

RAM это все еще круто! При чем тут Python SSD

Slide 10

Slide 10 text

Python? При чем тут Python ‣ Часто Redis уже интегрирован как cache backend ‣ Там сессии, кеш вьюх и все стандартизированное фреймворком

Slide 11

Slide 11 text

Python + Redis и продвинутые проекты При чем тут Python ‣ Структуры данных типа set, hash ‣ Cчетчики ‣ Очереди ‣ Pub/Sub

Slide 12

Slide 12 text

Redis & persistency

Slide 13

Slide 13 text

Традиционные базы данных Redis & Persistence ‣ Гарантируют целостность ‣ Размер базы упирается в размер диска ‣ Шардинг ‣ SQL или ORM - абстракция над ним

Slide 14

Slide 14 text

Под капотом Redis & Persistence ‣ По умолчанию данные записываются в Linux раз в 30 секунд (упрощенно) ‣ Но можно настроить базу на сброс на диск сразу после вызова записи ‣ И это будет очень медленно

Slide 15

Slide 15 text

А что такое запись вообще?

Slide 16

Slide 16 text

Redis & Persistence Клиент шлет данные на запись База получает данные на запись (в памяти) База вызывает write (данные в буфере ядра) Ядро сбрасывает буферы на диск (данные попадают в кеш диска) Диск физически записывает данные

Slide 17

Slide 17 text

Redis & Persistence Клиент шлет данные на запись База получает данные на запись (в памяти) База вызывает write (данные в буфере ядра) Ядро сбрасывает буферы на диск (данные попадают в кеш диска) Диск физически записывает данные POWER OUTAGE PROCESS KILL

Slide 18

Slide 18 text

Fsync Redis & Persistence ‣ Можно увеличить надежность с помощью системного вызова fsync ‣ Но это приведет к постоянной записи на диск ‣ Пользователи будут недовольны

Slide 19

Slide 19 text

Снимки Snapshottings ‣ Snapshottings (или RDB) - простейший режим персистентности в редисе ‣ Создает снимок во времени при достижении определенных условий: таймаута и количество измененных ключей. Напр. 100 новых записей и последний снимок создан более, чем 2 минуты назад

Slide 20

Slide 20 text

Ограниченная надежность Redis & Persistence ‣ Надежность ограничена условиями ‣ Гарантирует, что запись будет целостной в конкретный момент времени (в append-only режиме) ‣ Все, что записано до создания следующего снимка будет утеряно

Slide 21

Slide 21 text

AOF – Append Only File Redis & Persistence ‣ Это основная система персистентности в Redis ‣ Суть туповата – если операция записи модифицирует данные, эта операция записывается в AOF ‣ Лог точно в том же формате, что и клиентская команда

Slide 22

Slide 22 text

Интересные возможности Redis & Persistence ‣ Благодаря тупизне можно транслировать команды на другой инстанс с помощью netcat ‣ После рестарта Redis проходится по всем командам из AOF, это может быть долго

Slide 23

Slide 23 text

Always growing file Redis & Persistence ‣ AOF всегда растущий, если он становится слишком большим - Redis пересоздает его целиком ‣ Вместо чтения старого файла, Redis создает минимальный набор команд для воспроизведения данных, которые находятся в памяти

Slide 24

Slide 24 text

Надежность Redis & Persistence ‣ Пока новый AOF файл генерируется, текущие транзакции дописываются в старый файл и в буфер ‣ Когда новый файл готов команды из буфера дописываются в него и старый файл подменяется новым

Slide 25

Slide 25 text

Режимы AOF Redis & Persistence ‣ appendfsync no – fsync не выполняется вовсе, в AOF запись выполняется с помощью write ‣ appendfsync everysec – данные записываются с помощью write и синхронизируются с диском раз в секунду с помощью fsync ‣ appednfsync always – самый меденный способ записи. Fsync + write, но делается group commit

Slide 26

Slide 26 text

Что предлагают другие DB Redis & Persistence ‣ PostgreSQL: fsync, synchronous_commit ‣ MySQL InnoDB: innodb_flush_log_at_trx_commit

Slide 27

Slide 27 text

Инструменты в Python Инструменты

Slide 28

Slide 28 text

Инструменты Инструменты ‣ redis-py - самый распространенный клиент ‣ tornado-redis - async redis client for Tornado ‣ txredis - async redis client for Twisted ‣ redis-proxy - read/write splitting proxy ‣ nydus - clustering & routing library by Disqus

Slide 29

Slide 29 text

Простые операции Инструменты import redis r = redis.StrictRedis(host='localhost', port=6379, db=0) print r.set('foo', 'bar') print r.get(‘foo’) # True # ‘bar’

Slide 30

Slide 30 text

Pipelines Инструменты r = redis.Redis(...) pipe.set('foo', ‘bar’)\ .sadd('faz', ‘baz’)\ .incr('auto_number').execute() # [True, True, 6]

Slide 31

Slide 31 text

Транзакции Инструменты def client_side_incr(pipe): current_value = pipe.get('OUR-SEQUENCE-KEY') next_value = int(current_value) + 1 pipe.multi() pipe.set('OUR-SEQUENCE-KEY', next_value) r.transaction(client_side_incr, 'OUR-SEQUENCE-KEY')

Slide 32

Slide 32 text

Pub/Sub Инструменты p = r.pubsub() p.subscribe('my-first-channel', 'my-second-channel', …) # Processing messages while True: message = p.get_message() if not message: sleep(1) continue # .. do something with message ### Sending messages r.publish('my-first-channel', 'some data')

Slide 33

Slide 33 text

Server Side Scripting on LUA Инструменты r = redis.StrictRedis() lua = """ local value = redis.call('GET', KEYS[1]) value = tonumber(value) return value * ARGV[1]""" multiply = r.register_script(lua) r.set('foo', 2) multiply(keys=['foo'], args=[5]) # 10

Slide 34

Slide 34 text

Атомарность Инструменты ‣ Скрипты на Lua выполняются атомарно – команды Redis не будут выполнены до конца обработки скрипта. Медленные скрипты нет смысла запускать ‣ Та же история с MULTI/EXEC – команды выполняются атомарно ‣ Если процесс redis убит посреди транзакции, файл AOF может быть поврежден, для починки есть тулза redis-check-aof

Slide 35

Slide 35 text

Типы данных Data types

Slide 36

Slide 36 text

Типы данных в Redis Типы данных ‣ List ‣ Set ‣ SortedSet ‣ Hash ‣ Bitmap ‣ HyperLogLogs

Slide 37

Slide 37 text

ORM Еще инструменты

Slide 38

Slide 38 text

ROM

Slide 39

Slide 39 text

Основные фичи ROM ‣ Декларативно определять модель ‣ JSON поля, datetime ‣ Составные уникальные ключи ‣ ForeignKey, OneToMany, ManyToOne ‣ Выборки по типу Django ORM ‣ Full-word, Prefix, Suffix, Pattern индексы

Slide 40

Slide 40 text

Simple Model ORM import redis import rom rom.util.set_connection_settings(host=‘myhost', db=7) # All models to be handled by rom # must derived from rom.Model class User(rom.Model): email = rom.String( required=True, unique=True, suffix=True) salt = rom.String() hash = rom.String() created_at = rom.Float(default=time.time)

Slide 41

Slide 41 text

Составные ключи ORM class UniquePosition(Model): x = Integer() y = Integer() unique_together = [ ('x', 'y'), ]

Slide 42

Slide 42 text

Отношения ORM class MyModel(Model): col = ManyToOne('OtherModelName') class MyModel(Model): col = ForeignModel(DjangoModel) class MyModel(Model): col = OneToMany('OtherModelName', on_delete='restrict') ocol = OneToMany('ModelName', on_delete='no action')

Slide 43

Slide 43 text

Выборки ORM user = User(email_addrss='[email protected]') user.save() # session.commit() or session.flush() works too user = User.get_by(email_address='[email protected]') user = User.get(5) users = User.get([2, 6, 1, 7]) user = User.get_by(email_address='[email protected]') # gets up to 25 users created in the last 24 hours users = User.get_by( created_at=(time.time()-86400, time.time()), _limit=(0, 25))

Slide 44

Slide 44 text

Выборки ORM at_gmail = User.query.endswith(email='@gmail.com').all() users = User.query.filter( created_at=(time.time()-86400, time.time())).execute()

Slide 45

Slide 45 text

Еще инструменты ORM ‣ redisco – Python Containers and Simple Models for Redis (+1 ORM) ‣ analytics – realtime trackings of different events in the system ‣ restmq - redis queue engine with REST interface

Slide 46

Slide 46 text

Ну хорошо. И что? What the business?

Slide 47

Slide 47 text

Redis - это база данных с предсказуемым результатом выполнения любой команды. Для каждой команды есть конкретная сложность, описанная в документации. Все что нужно – калькулятор и скорость RAM What the business?

Slide 48

Slide 48 text

Кто пошел на это Realworld examples ‣ YouPorn, Pornhub & whole porn holding ‣ Cgraigslist ‣ SkillPages ‣ Disqus – аналитика и метрики ‣ HipChat – XMPP state ‣ Twitter

Slide 49

Slide 49 text

Спасибо. Thanks! @maxmaxmaxmax

Slide 50

Slide 50 text

Data sources 1. http://redis.io/topics/cluster-spec 2. http://redis.io/topics/persistence 3. http://oldblog.antirez.com/post/redis-persistence- demystified.html 4. https://muut.com/blog/technology/redis-as-primary- datastore-wtf.html 5. https://chris-lamb.co.uk/posts/distributing-locking-python- and-redis 6. https://charlesleifer.com/blog/powerful-autocomplete- with-redis-in-under-200-lines-of-python/ 7. http://restmq.com