Upload
krivoy-rog-it-community
View
1.509
Download
4
Embed Size (px)
DESCRIPTION
Citation preview
Шардинг - что это и как правильно его готовить.
автор: Дмитрий Фурзенко, Sandstorm NV
О себе:● в сети известен под ником - Dimonji● python developer over 3+ year expirience● pythonic way в разработке● highload системы● Мои инструменты:
○ Pycharm or Emacs ○ Python○ Brain○ И очень много разных python библиотек :)
Где работал/работаю:c 08.2011 по текущее время - Sandstorm NVс 05.2009 по 08.2011 ООО Любимое такси, разработчик программного обеспечения, системный администратор
О чем будет доклад:● Теория построения шардинга● SQLAlchemy● gevent
Ингредиенты для шардинга:● python 2.7● SQLAlchemy >= 0.7.9● MySQL or PostgreSQL● четкое понимание архитектуры приложения● немного магии
Зачем нужен шардинг:● практически бесконечное горизонтальное
масштабирование production платформы● отказоустойчивость● более дешевый production
Плюсы:● горизонтальное масштабирование● балансировка нагрузки на БД● более высокая отказоустойчивость● более низкий downtime
Минусы:● выборка и обработка всех данных становиться
самым узким местом● требуется более тщательное проектирование
приложения● в некоторых моментах приходится пренебрегать т.н
нормальной формой БД
Обобщенные принципы построения:● все данные которые будут расшардены должны
иметь общий ключ т.н сегмент● отдельная структура которая будет знать какой
сегмент какому шарду принадлежит● единый алгоритм распределения сегментов по
шардам
SQLAlchemy● мощная орм, которая при минимуме усилий
позволяет абстрагировать разработчика от БД и работать на уровне объектов
● если алхимию неправильно использовать она может принести много головной боли разработчику
Установка соединения с БД и pool соединений:● umysqldb● sqlalchemy.create_engine● sqlalchemy.orm.sessionmaker
Пример организации соединения с одной БД
import umysqldbfrom sqlalchemy import create_enginefrom sqlalchemy.orm import sessionmaker
umysqldb.install_as_MySQLdb()SHARD_ENGINE = { 'engine': 'mysql', 'host': 'localhost', 'name': 'kranonit_shards', 'login': 'root', 'password': ''}_DSN = '%(engine)s://%(login)s:%(password)s@%(host)s/%(name)s?charset=utf8'
SHARD_CONNECTION = create_engine( _DSN % SHARD_ENGINE, echo=False, pool_recycle=3600, pool_size=5, max_overflow=0, isolation_level="READ COMMITTED")
SESSION = sessionmaker(bind=SHARD_CONNECTION, autocommit=True)S_SESSION = SESSION()
Организация соединения с БД для шардовых БД:● pool соединений к шардовым базам● shard class
○ shard_chooser○ query_chooser○ id_chooser
Пример организации соединения с шардовыми БД
CONNECTIONS = list()_DSN = '%(engine)s://%(login)s:%(password)s@%(host)s/%(name)s?charset=utf8'for shard in S_SESSION.query(Shard).all(): engine = create_engine( _DSN % {'engine': shard.engine, 'host': shard.host, 'name': shard.name, 'login': shard.login, 'password': shard.password}, echo=False, pool_recycle=3600, pool_size=shard.pool_size, max_overflow=shard.max_overflow, isolation_level=shard.isolation_level) CONNECTION[shard.id] = engine
createfunc = sessionmaker(class_=DeepShardedSession, autoflush=False)scoped = scoped_session(createfunc, scopefunc=getcurrent)scoped.configure(shards=CONNECTIONS)session = scoped()
Логика распределения shard● uuid● балансировка● выбор емкости сегментов
○ 1 разряд uuid дает 16 записей на сегмент○ минимальный ключ для сегмента два разряда
uuid○ максимальноя количество записей в сегменте
Пример модели для шардclass Shard(SEBase): __tablename__ = 'shards'
id = Column(Integer, primary_key=True) engine = Column(String(50), default='mysql') host = Column(String(255)) login = Column(String(100)) password = Column(String(100)) name = Column(String(100)) pool_size = Column(Integer(3), default=-1) max_overflow = Column(Integer(3), default=-1) isolation_level = Column(String(100), default='READ COMMITED') desc = Column(Text(255)) is_default = Column(Boolean, default=False)
__table_args__ = ( UniqueConstraint('host', 'name', name='_shard_host_name_uc'), {'mysql_engine': 'InnoDB', 'extend_existing': True, }, )
Пример модели сегментовclass Segment(SEBase): __tablename__ = 'segments'
id = Column(String(5), primary_key=True) shard_id = Column(Integer, ForeignKey("shards.id")) shard = relationship( Shard, primaryjoin='Shard.id==Segment.shard_id', lazy='joined')
__table_args__ = ({ 'mysql_engine': 'InnoDB', 'extend_existing': True, })
DeepShardedSession примерclass DeepShardedSession(ShardedSession): def __init__(self, query_cls=DeepShardedQuery, *args, **kwargs): self.__binds = {} super(DeepShardedSession, self).__init__( shard_chooser=self.shard_chooser, query_chooser=self.query_chooser, id_chooser=self.id_chooser, query_cls=query_cls, *args, **kwargs)
def shard_chooser(self, mapper, instance, clause=None, *args, **kwargs): “””Здесь описываем по каким критериям мы выбираем необходимый шард””” pass
def query_chooser(self, query, ident=None): """Здесь опираясь на условия запроса определяем на каких шардах его выполнять””” pass
Почему одна БД плохо
Наша одна БД
Поток данных
Почему шардинг хорошо
БД 1
БД 2
БД 3
БД 4
БД 5
БД n
Поток данных
Немножко про geventGevent - green event, микропотоки в pythonПлюсы
● Псевдо многопоточность● Забываем про ужасы callback и deffered из Twisted● пишем код в обычном стиле и двумя строчками делаем его асинхронным
Минусы● сложно профилировать● не все библиотеки можно им пропатчить● требует определенных навыков в использовании
Применение● Практически везде где уместна работа с сетью, потоками и очередями