Доклад был представлен на официальной российской конференции PG Day'14 Russia, посвященной вопросам разработки и эксплуатации PostgreSQL.
Уникальный семинар от опытного "базиста" Ивана Фролкова призван наглядно пояснить слушателям адекватность применения реляционных СУБД на задачах веба. В рамках доклада Иван рассмотрит типичные "грабли", на которые натыкаются разработчики, и субоптимальные решения, изобретаемые с целью побороть возникшие проблемы. В качестве альтернативы, коллега Фролков наглядно пояснит, как эти же задачи решаются штатными средствами PostgreSQL.
В качестве бонуса Иван — "ветеран" промышленной разработки ПО для реляционных СУБД — проведет краткий ликбез по рекомендуемым практикам построения SQL-запросов и программирования на языке PL/PGSQL.
Докладчик:
Александр Сапронов
Описание:
Мы рассмотрим популярные библиотеки для функционального программирования на Python — fn.py, functools, itertools, funcy, hask, Toolz. Узнаем возможности каждой из библиотеки, а также как в динамическом язык имитировать мощную систему типов. Затронем характеристики функционального программирования и проверим помогают ли библиотеки выполнить.
Хочу знать, сколько уникальных посетителей было на моём сайте за произвольный...Ontico
Что нужно хранить для того, чтобы была возможность ответить на этот вопрос?
Для точного ответа нужно через равные интервалы времени сохранять множество посетителей сайта (пусть это для простоты будут IP-адреса), которых мы за прошедший интервал увидели. Понятное дело, что такой объём информации хранить нереально, а даже, если получится, придётся объединять большое количество множеств и считать элементы в том множестве, которое получилось в итоге. Это очень долго. Не спасает ситуацию даже переход от точных алгоритмов к приблизительным: гарантировать точность либо не получится, либо придётся использовать объём памяти и вычислительные ресурсы, сопоставимые с точным алгоритмом.
В 80-х годах появились первые вероятностные алгоритмы для приблизительной оценки количества элементов в множестве. При большом количестве уникальных элементов эти алгоритмы дают приблизительную оценку, которая отличается от истинного значения в (1±e), e<1>0.5. То есть они могут вернуть оценку, которая сильно отличается от истинного значения с некоторой вероятностью (1-p). Чем больше требуется точность, и чем меньше нужна вероятность ошибки, тем больше ресурсов требуют алгоритмы. Сохраняя внутреннее состояние одного из таких алгоритмов через равные промежутки времени в базе данных, мы можем оценить приблизительное количество уникальных посетителей не только за произвольный интервал времени, но и за произвольное объединение любых интервалов времени, например, мы можем посчитать общее количество уникальных IP, которых мы наблюдали в промежутке времени с 17:00 до 18:00 в течение последней недели.
В 2000-ные в научном сообществе велась активная работа по достижению теоретически оптимальных характеристик (т.е. потребление памяти, сложность добавления нового элемента, сложность запроса) вероятностных приблизительных алгоритмов для оценки кардинальности (количества элементов в множестве), разрабатывался необходимый инструментарий.
Первый такой алгоритм был предложен в 2010 году. О нём-то мы и поговорим.
Докладчик:
Александр Сапронов
Описание:
Мы рассмотрим популярные библиотеки для функционального программирования на Python — fn.py, functools, itertools, funcy, hask, Toolz. Узнаем возможности каждой из библиотеки, а также как в динамическом язык имитировать мощную систему типов. Затронем характеристики функционального программирования и проверим помогают ли библиотеки выполнить.
Хочу знать, сколько уникальных посетителей было на моём сайте за произвольный...Ontico
Что нужно хранить для того, чтобы была возможность ответить на этот вопрос?
Для точного ответа нужно через равные интервалы времени сохранять множество посетителей сайта (пусть это для простоты будут IP-адреса), которых мы за прошедший интервал увидели. Понятное дело, что такой объём информации хранить нереально, а даже, если получится, придётся объединять большое количество множеств и считать элементы в том множестве, которое получилось в итоге. Это очень долго. Не спасает ситуацию даже переход от точных алгоритмов к приблизительным: гарантировать точность либо не получится, либо придётся использовать объём памяти и вычислительные ресурсы, сопоставимые с точным алгоритмом.
В 80-х годах появились первые вероятностные алгоритмы для приблизительной оценки количества элементов в множестве. При большом количестве уникальных элементов эти алгоритмы дают приблизительную оценку, которая отличается от истинного значения в (1±e), e<1>0.5. То есть они могут вернуть оценку, которая сильно отличается от истинного значения с некоторой вероятностью (1-p). Чем больше требуется точность, и чем меньше нужна вероятность ошибки, тем больше ресурсов требуют алгоритмы. Сохраняя внутреннее состояние одного из таких алгоритмов через равные промежутки времени в базе данных, мы можем оценить приблизительное количество уникальных посетителей не только за произвольный интервал времени, но и за произвольное объединение любых интервалов времени, например, мы можем посчитать общее количество уникальных IP, которых мы наблюдали в промежутке времени с 17:00 до 18:00 в течение последней недели.
В 2000-ные в научном сообществе велась активная работа по достижению теоретически оптимальных характеристик (т.е. потребление памяти, сложность добавления нового элемента, сложность запроса) вероятностных приблизительных алгоритмов для оценки кардинальности (количества элементов в множестве), разрабатывался необходимый инструментарий.
Первый такой алгоритм был предложен в 2010 году. О нём-то мы и поговорим.
Slides for my PyCon and DevDay talk about the idea of Underscore spread onto other languages.
Video (in russian) is available here http://www.youtube.com/watch?v=lGAC6ftYUS0#t=12100
Python&Printer / Андрей Пучко / penta.byPython Meetup
Андрей рассказал о личном опыте сражений за печать отчетов из программ на Python. Речь шла о полезных инструментах и форматах документов (PDF, RTF, DOCX, XLS, ODT, HTML) которые можно готовить к печати при помощи Python.
Алексей Куканов — Параллелизм в C++: управляйте приложением, а не потоками!Yandex
Алексей Куканов, Intel.
Последняя версия стандарта С++ добавляет в язык и библиотеку поддержки средства для использования потоков исполнения (threads) и синхронизации между ними. Однако это лишь необходимая низкоуровневая база для внедрения параллелизма. Эффективная разработка параллельных программ требует высокоуровневого API, реализующего типичные шаблоны использования параллелизма в виде, пригодном для применения в широком спектре алгоритмов и приложений. В докладе речь пойдёт о наиболее часто встречающихся параллельных шаблонах, реализованных в программных моделях Intel® Threading Building Blocks и Intel® Cilk Plus, и о примерах их использования.
Slides for my PyCon and DevDay talk about the idea of Underscore spread onto other languages.
Video (in russian) is available here http://www.youtube.com/watch?v=lGAC6ftYUS0#t=12100
Python&Printer / Андрей Пучко / penta.byPython Meetup
Андрей рассказал о личном опыте сражений за печать отчетов из программ на Python. Речь шла о полезных инструментах и форматах документов (PDF, RTF, DOCX, XLS, ODT, HTML) которые можно готовить к печати при помощи Python.
Алексей Куканов — Параллелизм в C++: управляйте приложением, а не потоками!Yandex
Алексей Куканов, Intel.
Последняя версия стандарта С++ добавляет в язык и библиотеку поддержки средства для использования потоков исполнения (threads) и синхронизации между ними. Однако это лишь необходимая низкоуровневая база для внедрения параллелизма. Эффективная разработка параллельных программ требует высокоуровневого API, реализующего типичные шаблоны использования параллелизма в виде, пригодном для применения в широком спектре алгоритмов и приложений. В докладе речь пойдёт о наиболее часто встречающихся параллельных шаблонах, реализованных в программных моделях Intel® Threading Building Blocks и Intel® Cilk Plus, и о примерах их использования.
PG Day'14 Russia, Социальная сеть, которая просто работает, Владислав Ковальpgdayrussia
Доклад был представлен на официальной российской конференции PG Day'14 Russia, посвященной вопросам разработки и эксплуатации PostgreSQL.
Социальная сеть является классическим примером продукта, создаваемого людьми, движет которыми желание использовать как можно больше новомодных технологий. О последствиях, естественно, никто не задумывается. К счастью, в нужный момент было принято решение отказаться от части подобных новшеств и использовать старый-добрый PostgreSQL для создания сервиса.
Казалось бы, нет ничего хуже, чем реляционная база, для разработки социальной сети? Всем ведь хорошо известно, что типичная стратегия расширения подобного продукта заключается в горизонтальном “шардинге”, денормализации, многоуровневых слоях кеширования и прочих вещах, с которым реляционная СУБД не очень дружит.
Тем не менее, в Rubuki.com мы решили пойти наперекор трендам и сделать такую социальную сеть, где используется Rails без ORM, бизнес-логика живет в базе данных, и пользователи каждый день получают корректно отсортированную по дате появления событий новостную ленту (привет, Facebook!).
На примере реализации таких компонентов как полнотекстовый поиск, рекомендательный сервис и сервис премирования, я расскажу о нюансах разработки сети, успехах и проблемах, с которыми мы столкнулись.
This is What Happens When You Automate Stories for the APAutomated Insights
The Associated Press recently broke the news that it's partnering with Automated Insights, an artificial intelligence company, sparking conversations about 'robot' journalists.
Руслан Гроховецкий "Как Python стал делать погоду в Яндексе"Yandex
2 июля 2011, Я.Субботник в Екатеринбурге
Руслан Гроховецкий "Как Python стал делать погоду в Яндексе"
О докладе:
Про Python и Django: зачем нужна красота и простота перфекционистам с дедлайнами, на примере Яндекс.Погоды.
Когда число сервисов, которые делаются в Яндексе, стало возрастать, дедлайны — поджимать, а от процесса разработки требовалось стать более гибким, возникла потребность в свежих решениях. В докладе на примере Яндекс.Погоды рассказывается, как в Яндексе делают сервисы с помощью языка Python и веб-фреймворка Django.
Tech Talks @NSU: Как приручить дракона: введение в LLVMTech Talks @NSU
http://techtalks.nsu.ru
Видеозапись: http://www.youtube.com/watch?v=v7uBLSm6ft8
06 октября 2015. Как приручить дракона: введение в LLVM (Дмитрий Кашицын, HDsoft)
«В этом докладе мы кратко расскажем о таком звере, о котором много кто слышал, но немногие щупали. Что такое компилятор на самом деле? Чем LLVM отличается от других компиляторов? Как в LLVM происходит компиляция программы, как работают оптимизации? Наконец, какой путь проходит программа от разбора исходного текста до генерации исполняемого файла?
Лекция будет обзорной и не потребует от слушателей глубоких знаний теории компиляторов.»
Лекция прочитана в рамках проекта Tech Talks @NSU – серии открытых лекций о разработке ПО и карьере в IT, проводимых в Новосибирском государственном университете.
Подробности: http://techtalks.nsu.ru
10 июня 2015. Дмитрий Кашицын (HDsoft) дает обзор LLVM.
http://techtalks.nsu.ru
Видеозапись: https://plus.google.com/events/ctes98f7uhf19t5jlvlbk24dan4
В этом докладе мы кратко расскажем о таком звере, как LLVM, о котором много кто слышал, но немногие щупали. Что такое компилятор на самом деле? Чем LLVM отличается от других компиляторов? Как в LLVM происходит компиляция программы, как работают оптимизации? Наконец, какой путь проходит программа от разбора исходного текста до генерации исполняемого файла?
Лекция будет обзорной и не потребует от слушателей глубоких знаний теории компиляторов.
Лекция прочитана в рамках проекта Tech Talks @NSU – серии открытых лекций о разработке ПО и карьере в IT, проводимых в Новосибирском государственном университете.
Подробности: http://techtalks.nsu.ru
PG Day'14 Russia, PostgreSQL как платформа для разработки приложений, часть 2...pgdayrussia
Доклад был представлен на официальной российской конференции PG Day'14 Russia, посвященной вопросам разработки и эксплуатации PostgreSQL.
Уникальный семинар от опытного "базиста" Ивана Фролкова призван наглядно пояснить слушателям адекватность применения реляционных СУБД на задачах веба. В рамках доклада Иван рассмотрит типичные "грабли", на которые натыкаются разработчики, и субоптимальные решения, изобретаемые с целью побороть возникшие проблемы. В качестве альтернативы, коллега Фролков наглядно пояснит, как эти же задачи решаются штатными средствами PostgreSQL.
В качестве бонуса Иван — "ветеран" промышленной разработки ПО для реляционных СУБД — проведет краткий ликбез по рекомендуемым практикам построения SQL-запросов и программирования на языке PL/PGSQL.
PostgreSQL: практические примеры оптимизации SQL-запросов / Иван Фролков (Po...Ontico
Довольно часто как адинистраторы, так и разработчики жалуются на низкую производительность приложений, работающих с базой данных, и нередко при этом ищут решения возникших проблем с помощью различных настроек как СУБД, так и операционной системы, пренебрегая при этом самым действенным способом - оптимизацией запросов к собственно БД.
Тому, как понимать, где же узкие места, и как их можно попробовать избежать на примере PostgreSQL и посвящен этот доклад.
Дмитрий Прокопцев, Яндекс
Речь пойдёт о, наверное, одном из самых важных и в то же время сложных нововведений в С++11 — R-ссылках (rvalue references). Мы рассмотрим базовые правила работы с такими ссылками и связанные с ними новые концепции языка: перемещение классов, универсальные ссылки и перенаправление вызовов.
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014Python Meetup
В своем докладе Олег расскажет о замене стандартных функций на более быстрые и об ускорении работы python. Также продемонстрирует несколько примеров быстрых конструкций python.
Функциональное программирование с использованием библиотеки fp-ts | Odessa Fr...OdessaFrontend
Дмитрий Ховрич рассказывает как использовать строгую типизацию TypeScript и писать надёжный код в функциональном стиле. А также делится знаниями как использовать функторы и монады в ежедневной фронтенд разработке.
Similar to PG Day'14 Russia, PostgreSQL как платформа для разработки приложений, часть 3, Иван Фролков (20)
PG Day'14 Russia, Secure PostgreSQL Deployment, Magnus Haganderpgdayrussia
Доклад был представлен на официальной российской конференции PG Day'14 Russia, посвященной вопросам разработки и эксплуатации PostgreSQL.
PostgreSQL supports several options for securing communications when deployed outside of the typical webserver/database combination, or in a high security environments. This talk will go into some details about the features that make this possible. The main areas discussed are:
Securing the PostgreSQL infrastructure and runtime environment.
Securing the channel between client and server using SSL, including an overview of the threats and how to secure against them.
Securing the login process with methods including LDAP, Kerberos or SSL certificates.
PG Day'14 Russia, Работа со слабо-структурированными данными в PostgreSQL, Ол...pgdayrussia
Доклад был представлен на официальной российской конференции PG Day'14 Russia, посвященной вопросам разработки и эксплуатации PostgreSQL.
<сарказм> MongoDB правит бал в мире слабо-структурированных данных. Привлеченные в MongoDB инвестиции часто затмевают разум (особенно начинающих и доверчивых) разработчиков, которые с радостью бросаются в океан возможностей, предоставляемых NoSQL (это же круто!). Энтузиазм затихает после осознания того факта, что бесплатно ничего не бывает и надо писать своими руками то, что десятилетиями хорошо работает в традиционных реляционных базах данных, которые прекрасно справляются с нагрузками и данными 99% проектов, и ваш проект не входит в оставшийся один процент. </сарказм>
Мир баз данных за последние годы существенно изменился. Всеместное проникновение Интернет-технологий привело к необходимости работы с большим количеством разнородных данных в реальном времени, к чему традиционные реляционные СУБД оказались не готовы. Принято считать, что слабая масштабируемость и излишняя “жесткость” модели данных реляционных СУБД и являются основными причинами появляния и роста популярности NoSQL баз данных (далее, NoSQL).
В докладе мы остановимся на концептуальных предпосылках появления NoSQL и их классификации. Одним из “жупелов” NoSQL является поддержка типа данных JSON, который реализует документо-ориентированную модель данных. Документо-ориентированная модель данных является более гибкой и позволяет менять схему данных “на лету”, что сделать очень трудно в реляционных СУБД, особенно в системах, работающих под большой нагрузкой. Несмотря на успех NoSQL (активно распиаренный использованием в некоторых популярных Интернет-проектах), многие пользователи не готовы приносить в жертву целостность данных в угоду масштабируемости, но хотят иметь гибкость схемы данных в проверенных и надежных реляционных СУБД.
Нами была предложена и реализована поддержка документо-ориентированной модели в PostgreSQL (версия 9.4). Уже более 10 лет в PostgreSQL существует возможность работать со schema-less данными, используя наш модуль расширения hstore. Hstore предлагает хранилище вида "ключ-значение" с сохранением всех реляционных возможностей, что сделало его самым используемым
PG Day'14 Russia, PostgreSQL в avito.ru, Михаил Тюринpgdayrussia
Доклад был представлен на официальной российской конференции PG Day'14 Russia, посвященной вопросам разработки и эксплуатации PostgreSQL.
С момента старта проекта на PostgreSQL были возложены серьёзные задачи. Это во многом предопределило успешное развитие всего продукта. Вокруг СУБД выстроены основные компоненты архитектуры, при этом сами базы берут на себя львиную долю обработки пользовательских запросов. Набор фич и расширений, легендарная надёжность PostgreSQL, наличие встроенной репликации, средств резервирования и архивирования — весь потенциал нашел своё воплощение, а наличие открытого профессионального комьюнити не оставляет шансов к неэффективной реализации.
В докладе будет дан обзор развития подсистем, сосредоточенных вокруг PostgreSQL, представлены параметры и режимы функционирования. Будут описаны успешные решения в рамках отдельного PostgreSQL-кластера и при распределенной обработке данных, приведены текущие вызовы, связанные с продолжающимся активным ростом проекта.
PG Day'14 Russia, PostgreSQL как платформа для разработки приложений, часть 1...pgdayrussia
Доклад был представлен на официальной российской конференции PG Day'14 Russia, посвященной вопросам разработки и эксплуатации PostgreSQL.
Уникальный семинар от опытного "базиста" Ивана Фролкова призван наглядно пояснить слушателям адекватность применения реляционных СУБД на задачах веба. В рамках доклада Иван рассмотрит типичные "грабли", на которые натыкаются разработчики, и субоптимальные решения, изобретаемые с целью побороть возникшие проблемы. В качестве альтернативы, коллега Фролков наглядно пояснит, как эти же задачи решаются штатными средствами PostgreSQL.
В качестве бонуса Иван — "ветеран" промышленной разработки ПО для реляционных СУБД — проведет краткий ликбез по рекомендуемым практикам построения SQL-запросов и программирования на языке PL/PGSQL.
PG Day'14 Russia, PostgreSQL System Architecture, Heikki Linnakangaspgdayrussia
Доклад был представлен на официальной российской конференции PG Day'14 Russia, посвященной вопросам разработки и эксплуатации PostgreSQL.
Software architecture means a high-level view of the components of the system and their relationships. Understanding how various components work together is crucial if you want to start hacking on PostgreSQL, but also for understanding performance characteristics and run-time behavior of production systems.
Software architecture is not a single diagram, but consists of multiple views of the same system.
Process view: describing the processes at runtime and how they communicate.
Data flow: how a query passes through various parts of the system, and how the results flow back.
Data structures: how the Heap and Indexes are organized.
Source code: what's in the PostgreSQL source treem, and many more.
In this presentation, I will give an overview of the most important views necessary to understand how PostgreSQL works.
PG Day'14 Russia, PostgreSQL: архитектура, настройка и оптимизация, Илья Косм...pgdayrussia
Доклад был представлен на официальной российской конференции PG Day'14 Russia, посвященной вопросам разработки и эксплуатации PostgreSQL.
Основные принципы устройства PostgreSQL, ключевые принципы правильного конфигурирования и подходы к оптимизации под высокие нагрузки — обо всем этом Илья поведает на специальном мастер-классе, посвященном "внутренностям" СУБД. Курс предназначен как для опытных профессионалов, так и для "молодых бойцов". Полученные в ходе прослушивания семинара знания пригодятся не только на практике. Они также призваны увеличить эффективность восприятия слушателями и, как следствие, полезность докладов, запланированных на второй день конференции.
Как база работает с памятью и файловой системой? Для чего предназначено версионирование? Что такое транзакции, как они устроены и почему полезны? Как строятся индексы и что происходит с запросом во время его выполнения? Что такое репликация и почему ее нельзя применять для backup/recovery? На все эти вопросы, и не только, ответит Илья.
Разобравшись с основами и "матчастью", мы перейдем к самому интересному: "тюнингу" базы в нагруженных системах, начиная с классического "Почему у меня тормозит запрос?" и заканчивая кручением "гаек" системных процессов "посгреса" под объемы данных и транзакционные нагрузки, соизмеримые с "big data".
PG Day'14 Russia, Индексный поиск по регулярным выражениям, Александр Коротковpgdayrussia
Доклад был представлен на официальной российской конференции PG Day'14 Russia, посвященной вопросам разработки и эксплуатации PostgreSQL.
Регулярные выражения — мощный и широко применяемый инструмент для обработки текстовых данных. При поиске по регулярному выражению в большом наборе строк, становится актуальным вопрос о применении индекса. В то же время, использование индексов для поиска по регулярному выражению — нетривиальная задача.
Существует два основных подхода к выполнению поиска по регулярным выражениям с помощью индекса: "FREE indexing engine" [1], основанный на выделении из регулярного выражения непрерывных фрагментов текста, а также метод, разработанный для Google Code Search [2], осуществляющий рекурсивный анализ составных частей регулярного выражения, с целью выявления его атрибутов. В целом же, оба этих подхода используют обратные индексы на основе k-грам (подстрок исходной строки длины k) и различаются методом извлечения k-грам из регулярного выражения для последующего поиска по индексу.
Данный доклад представляет новый метод извлечений k-грам из регулярного выражения, основанный не на анализе исходного регулярного выражения, а на преобразовании соответствующего конечного автомата. Предлагаемый подход позволяет осуществить более полное извлечение k-грам из регулярного выражения, что подтверждается примерами. Данный подход был реализован в модуле pg_trgm СУБД PostgreSQL 9.3 [3].
PG Day'14 Russia, GIN — Stronger than ever in 9.4 and further, Александр Коро...pgdayrussia
Доклад был представлен на официальной российской конференции PG Day'14 Russia, посвященной вопросам разработки и эксплуатации PostgreSQL.
Доклад посвящен улучшениям в GIN-индексах в PostgreSQL 9.4 и далее, которые выводят GIN на новый уровень производительности и расширяемости. Наиболее важные улучшения:
Сжатие постинг-листов. Индексы становятся в среднем в 2 раза компактнее. При это не требуется никаких изменений со стороны opclass'ов. pg_upgrade поддерживается, индексы сжимаются "на лету".
Алгоритм быстрого сканирования GIN-индексов позволяет пропускать части больших постинг-деревьев при сканировании. Этот алгоритм кардинально улучшает скорость поиска для hstore и jsonb операторов, а также случай "частое_слово & редкое_слово" для полнотекстового поиска.
Хранение дополнительной информации в постинг-листах. Содержимое этой дополнительной информации зависит от конкретной разновидности GIN-индекса (определяется opclass'ом). Дополнительная информация может быть полезна при самых разных видах поиска: поиск по фразам, поиск по похожести массивов, обратный полнотекстовый поиск (поиск тех tsquery, которые подходят под tsvector), обратный поиск по регулярным выражением (поиск регулярных выражений, подходящих под строку), поиск по строковой "похожести" с использованием позиционных n-грам.
Ранжирование по индексу. Это улучшение позволяет возвращать результаты из индекса таким образом, как это определяет opclass. Наиболее важное применение — возвращение результатов полнотекстового поиска в порядке релевантности, кардинально снижающее загрузку IO. Но есть также и другие применения, такие как возврат массивов или строк в порядке их "похожести".
В докладе представлены результаты "бенчмарков" полнотектового поиска, использующие реальные наборы данных (6М и 15М документов) и реальные поисковые запросы, которые демонстрируют, что улучшенный полнотекстовый поиск PostgreSQL (со всеми накладными расходами ACID) может превосходить по скорости Sphinx.
PG Day'14 Russia, Нетрадиционный PostgreSQL: хранение бинарных данных в БД, А...pgdayrussia
Доклад был представлен на официальной российской конференции PG Day'14 Russia, посвященной вопросам разработки и эксплуатации PostgreSQL.
Наша небольшая компания занимается консалтингом, а, значит, мы постоянно сталкиваемся с той стороной проектов, о которой обычно не рассказывают на конференциях. Решения, внедряемые в типичном проекте, можно условно разделить на "красивые, но неработоспособные", "плохие", "очень плохие" и "удивительные". То решение, о котором пойдет речь в этом докладе, придумали не мы, и, когда авторы впервые представили его широкой общественности пару лет назад на конференции HighLoad, оно многими было воспринято как "удивительное". Тем не менее, решение работает и поныне, к чему мы приложили руку, поэтому у нас есть о чем рассказать. Да, наш девиз — "от удивительного к плохому!" :)
Речь пойдет о хранении большого количества статического пользовательского контента в PostgreSQL базе — согласитесь, не самая распространенная идея :) Традиционно считается, что СУБД не предназначены для подобных вариантов использования. Тем не менее, свойства СУБД, такие как поддержка транзакций и репликации, очень хорошо помогают для решения этого класса задач. После того как удивление проходит, и оказывается, что решение уже внедрено и работает, через некоторое время возникают новые вызовы — как сделать так, чтобы оно продолжало работать под возрастающей нагрузкой? Оптимизация производительности существующей и работающей 24x7 в продакшн базы — увлекательное действие, чем-то похожее на расследование преступления или на серию "Доктора Хауса". Если вы любите детективы и реляционные базы данных — приходите на мой доклад, и я постараюсь сделать так, чтобы вы не заснули!
Доклад был представлен на официальной российской конференции PG Day'14 Russia, посвященной вопросам разработки и эксплуатации PostgreSQL.
Как известно, PostgreSQL является постреляционной базой данных, что, помимо всего прочего, означает широкое использование массивов как для хранения, так и поиска данных. PostgreSQL обладает достаточно мощными и универсальными средствами оперирования массивами. Многие средства ориентированы на работу с многомерными массивами и, зачастую, избыточно сложны для работы с наиболее часто встречающимися одномерными массивами.
Многие пользователи PostgreSQL успешно используют модуль intarray в своей работе. Модуль предоставляет богатый набор функций и операторов для работы с одномерными целочисленными массивами. Пришло время обобщить этот набор на произвольные типы (текст, 64-битные числа, геометрические точки и т.д.) и добавить некоторые другие методы. Модуль anyarray объединен с модулем smlar, предоставлящим способы определения похожести массивов.
3. Пользователи
l Реально активно обычно 5-15% всех
пользователей.
l Обмен с диском идет постранично.
l Один пользователь — одна страница
l При сохранении в кеше тысячи пользователей
они будут занимать тысячу страниц; конкретно
строка пользователя будет занимать 1/8 — 1/200
страницы.
4. Пользователи
l Таким образом, 1000 пользователей
потребует 8М RAM, из которых будет реально
использоваться только 1000*200байт ≈ 20КБ
RAM.
l Кроме того, RAM будет использоваться еще и под
индекс.
− Кстати, Postgres не работает мимо кеша ОС;
следовательно, надо выделять или разумное
количество памяти, или почти всю.
l Сложим всех активных в секцию usr_active, а
неактивных — в usr_dormant
5. Union all
l select * from usr_active
union all
select * from usr_dormant
l От неактивных пользователей в кеше останется
только индекс.
l Можно еще лучше — создать функцию.
6. create function usr_all(ns integer[]) returns table(n integer) as
$code$
declare
v integer;
begin
foreach v in array ns loop
select a.n into usr_all.n from usr_active a where a.n=v;
if found then
return next;
else
select d.n into usr_all.n from usr_dormant d where d.n=v;
if found then
return next;
end if;
end if;
end loop;
end;
$code$
language plpgsql
7. create function usr_all(p_usr_id int) returns table(n integer) as
$code$
begin
select a.n into usr_all.n from usr_active a where a.n=p_usr_id;
if found then
return next;
else
select d.n into usr_all.n from usr_dormant d where d.n=p_usr_id;
if found then
return next;
end if;
end if;
end;
$code$
language plpgsql
8. Сравнение
l View — удобно; накладные расходы на индекс
l Функция —удобно (относительно); отсутствие
накладных расходов на индекс.
l Функция для LATERAL – удобно, отсутствие
накладных расходов на индекс, наличие
накладных расходов на вызов функции.
l Так ли уж это все надо?
− Скорее всего, нет. Но про страничный обмен забывать
все же не стоит.
− pg_repack, pg_reorg
9. LATERAL
• Что это такое?
select n, t
from generate_series(1,5) as n,
lateral(select max(t) as t
from generate_series(n*2,n*3) as gs1(t)
) as t
n | t
---+----
1 | 3
2 | 6
3 | 9
4 | 12
5 | 15
10. LATERAL
• select n, t
from generate_series(1,2) as n,
lateral(select t as t
from generate_series(n*2,n*3) as gs1(t)
) as t
n | t
---+--
1 | 2
1 | 3
2 | 4
2 | 5
2 | 6
11. LATERAL – зачем это надо?
• VIEW с параметрами
• Функции теперь first-class citizens
• Очевидное применение:
СЕКЦИОНИРОВАНИЕ
• В примере выше – неактивных пользователей
можно хранить в массивах, построив индекс
по range по их id.
• Не ограниченно только встроенными
средствами – можно делать произвольно.
12. View с параметрами
l Часто хотят view с параметрами
l Например, для пользователей показывать,
кто друг просматривающему; параметр — id
просматривающего пользователя
l Что делать?
l Декартово произведение. Да, именно декартово
произведение.
l N*1=N. Таблица параметров должна возвращать
единственную строку.
13. View с параметрами
create or replace view usr_cart as
select p.id as viewer_id,
u.*,
exists(select * from friend f
where f.usr_id=viewer_id
and f.friend_id=u.id)
as isfriend
from usr u, usr p
Используем:
select *
from usr_cart
where id in(...)
and viewer_id=$1
14. View с параметрами
l А что делать, если надо и без viewer_id?
Подставим несуществующий:
l create or replace view usr_cart as
select p.id as viewer_id,
u.*,
exists(select * from friend f
where f.usr_id=viewer_id
and f.friend_id=u.id)
as isfriend
from usr u, (select 0 as id
union all
select id from usr) as p
l Используем:
select *
from usr_cart
where id in(...)
and viewer_id=coalesce($1,0)
В отличие от CTE не создает
временной таблицы и
оптимизатор знает как это
оптимизировать
15. Top N для M
l Например, 10 последних постов из самых
популярных тем. Или 10 последних покупок в
категориях.
l Очевидный запрос
select p.*
from post p, topic t
where p.id in (select p1.id
from post p1
where p1.topic_id=t.id
order by added desc
limit 10
)
16. Top N для M
l К сожалению, не работает. Точнее, работает, но
для больших объемов неприемлемо медленно.
Обманываем оптимизатор
select p.*
from post p, topic t
where p.id=any(array(select p1.id
from post p1
where p1.topic_id=t.id
order by added desc
limit 10)
)
17. Top N для M
l Насколько эффективно это работает?
l Вообще говоря, вполне эффективно
l Если миллионы элементов, то все не так здорово
19. Зачем надо?
l Удобно: эдакое маленькое view в запросе
l Как и всякое view, позволяет отдельно описывать
отдельные логические понятия и не дублировать
код.
l Результаты живут в work_mem (или вытесняются
на диск); по сути — временные таблицы.
l И, наконец, рекурсия.
20. Recursive CTE
with recursive tq as(
select 1 as n --1
union all
select n+1 from tq where n<10 --2
)
select * from tq
23. Способ №1 или линейная
рекурсия
•with recursive
tree(id, parent_id, name) as
(values(1,null,'Адам'),
(2,1,'Авель'),
(3,1,'Каин'),
(4,2,'Енох')
),
with_parent as(
select *, null as parent_name
from tree
where parent_id is null
union all
select tree.*, wp.name
from tree, with_parent wp
where tree.parent_id=wp.id
)
select * from with_parent
id | parent_id | name | parent_name
----+-----------+-------+-------------
1 | | Адам |
3 | 1 | Каин | Адам
2 | 1 | Авель | Адам
4 | 2 | Енох | Авель
(4 строки)
24. Ну и в чем разница?
with recursive tree(id,
parent_id, name) as
(
values(1,null,'Адам'),
(2,1,'Авель'),
(3,1,'Каин'),
(4,2,'Енох')
)
select *,
(select t.name
from tree t
where tree.parent_id=t.id
) as parent_name
from tree
id | parent_id | name | parent_name
----+-----------+-------+-------------
1 | | Адам |
3 | 1 | Каин | Адам
2 | 1 | Авель | Адам
4 | 2 | Енох | Авель
(4 строки)
25. А вот в чем
with recursive tree(id, parent_id, name)
as(
values(1,null,'Адам'),
(2,1,'Авель'),
(3,1,'Каин'),
(4,2,'Енох')),
with_parent as(
select *, 'Бог' as ancestors_names
from tree
where parent_id is null
union all
select tree.*,
wp.ancestors_names||','||wp.name
from tree, with_parent wp
where tree.parent_id=wp.id)
•select * from with_parent
id | parent_id | name | ancestors_names
----+-----------+-------+-----------------
1 | | Адам | Бог
3 | 1 | Каин | Бог,Адам
2 | 1 | Авель | Бог,Адам
4 | 2 | Енох | Бог,Адам,Авель
(4 строки)
26. Нелинейная рекурсия
with recursive tree(id, parent_id, name) as(
values(1,null,'Адам'),
(2,1,'Авель'),(3,1,'Каин'),
(4,2,'Енох'), (5,null,'Ева'),
(6,5,'Дочь Евы'),(7,4,'Ирад')),
expand_tree as(
select id,parent_id,name from tree
union(
with expand_tree as(
select * from expand_tree
)
select e1.id, e1.parent_id, e2.name
from expand_tree e1,
expand_tree e2
where e2.parent_id=e1.id
)
)
select * from expand_tree e where id=1
28. Нелинейная рекурсия-3
Как это, собственно, работает
...where name='Ирад'
id | parent_id | name
----+-----------+------
7 | 4 | Ирад
4 | 2 | Ирад
2 | 1 | Ирад
1 | | Ирад
(4 строки)
29. Путь в циклическом графе
l Создаем граф.
− Создаем вершины:
create table vertex as
select n
from generate_series(1,10000) as gs(n);
alter table vertex add constraint vertex_pk
primary key (n);
− Создаем вершины:
create table edge(f,t) as select distinct (random()
*10000)::integer as from, (random()*10000)::integer as to from
generate_series(1,100000) as gs(n);
create index edge_ft on edge(f,t);
30. Ищем путь
l Весьма просто:
with recursive tq as(
select 1 as pass, n, array[n] as path
from vertex where n=1
union all
(with t as (select * from tq)
select pass+1, v.n, t.path||v.n
from vertex v, t, edge e
where v.n=e.t and t.n=e.f
and not (e.t=any(t.path))
and pass<8
and not exists(select * from t where n=5000)
)
)
select * from tq where n=5000
32. Что дальше
l CTE как конечный автомат. Со стеком! Разбор
JSON'а.
− Как?
l Сделать одним запросом? Реально, но громоздко.
l plpgsql
l C-libraries
− Уже встроенный тип
l All art is quite useless
l Разбираем JSON. JSONPath навыворот
− Пример: $.users.[100].id
33. Разбор JSON
with recursive
tokens as(
select s[1] as token, row_number() over() as n
from regexp_matches($JS$
{
"data": [
{"name": "User1 User1", "id": "768709679"},
{"name": "User2 User2","id": "10604454123"}
]
"paging": {
"next": "https://graph.facebook.com/10000223"
}
}
$JS$,
36. Разбор JSON.parsed as(
select n,
token as token,
array[token] as stack,
array['$']::text[] as path,
'' as jsp,
array[0]::integer[] as poses
from tokens t where n=1
union all
select t.n,
t.token as token,
case when t.token in (']','}') then p.stack[1:array_upper(p.stack,1)-1]
when t.token in ('[','{') then p.stack || t.token
else p.stack
end,
case when t.token in ('[','{') then p.path ||
case when p.stack[array_upper(p.stack,1)]='{'
then regexp_replace(p.token,'^"|"$','','g')
else '[' || (p.poses[array_upper(p.poses,1)]+1)::text || ']'
end
when t.token in (']','}') then p.path[1:array_upper(p.path,1)-1]
else p.path
end,
case when p.stack[array_upper(p.stack,1)]='{' then p.token
when p.stack[array_upper(p.stack,1)]='[' then '[' ||
(p.poses[array_upper(p.poses,1)]+1)::text || ']'
else ''
end,
case when t.token in ('[','{') then p.poses[1:array_upper(p.poses,1)-1]||
(p.poses[array_upper(p.poses,1)]+1)||0
when t.token in (']','}') then p.poses[1:array_upper(p.poses,1)-1]
else p.poses[1:array_upper(p.poses,1)-1]||
(p.poses[array_upper(p.poses,1)]+1)
end
from parsed p, tokens t where t.n=p.n+1),
38. Разбор JSON.
res as(
select *
from parsed
where (stack[array_upper(stack,1)]='['
or (stack[array_upper(stack,1)]='{'
and poses[array_upper(poses,1)]%2=0)
)
and token not in ('{','[','}',']')
)
select array_to_string(path,'.')||'.'||
regexp_replace(jsp,'^"|"$','','g')
as json_path,
regexp_replace(token,'^"|"$','','g')
as json_value
from res
40. А зачем это надо?
l Вообще полезно знать.
l Иногда нет возможности установить что-то
дополнительно
l Не только JSON — что угодно с относительно
несложным синтаксисом.
l Можно предложить еще способ — например,
строить дерево со ссылками на родителя,
рекурсивно же разбирать аналог JSONPath и
добираться до элементов.
− И не только JSON — пути в дереве вообще.
41. with recursive fs(id,pid,name) as(
values
(1,null,null),
(2,1,'usr'),
(3,2,'local'),
(4,3,'postgres'),
(5,4,'bin')
),
path as(
select p, row_number() over() as n
from regexp_split_to_table('/usr/local/postgres/bin','/')
as v(p)
),
parsed as(
select id, name,p,n
from fs, path where fs.pid is null and path.p=''
union all
select fs.id, fs.name, path.p, path.n
from fs, path, parsed
where fs.pid=parsed.id and fs.name=path.p
and path.n=parsed.n+1
)
select * from parsed order by n desc limit 1
42. Что получилось
• id | name | p | n
•----+------+-----+---
• 5 | bin | bin | 5
•(1 строка)
43. Prepared statements
l Увеличивают производительность — не требуется
каждый раз
− разбирать запрос
− Нагружать каталог на предмет существования таблиц,
индексов и т. п.
− Проверять права доступа к объектам
− А в случае широкого использования view все может
оказаться еще интереснее
l prepare <sthname> as <real query>
l execute <sthname>(par1,par2...parN)
44. Prepared statements & plpgsql
l В plpgsql отсутствует аналог пакета DBMS_SQL —
prepared statements недоступны.
− Каждый статический запрос компилируется один
раз при обращении или перекомпилируется при
изменении объектов
l Внимание: раньше была проблема, если объекты, на которые
ссылается statement, в момент начала его выполнения еще
существовали, но до обращения к ним были удалены.
Теперь ее больше нет.
l Типичный пример: переименование таблицы при высокой
OLAP-нагрузке
l Что делать?
45. Advisory locks
l Решение 1: рекомендательные блокировки
− pg_advisory_lock, pg_advisory_xact_lock, _shared,
pg_try_
l Решение 2: обычные блокировки. Таблица
блокировок. select … for share.
46. Prepared statements
l А точно ли недоступны prepared statements в
plpgsql?
l Конечно доступны: execute '….':
execute 'prepare sth1 for select 1';
execute 'execute sth1' into res;
l Execute 'execute' — а вот параметры, увы,
действительно недоступны. quote_literal(),
quote_ident()
47. Prepared statements
l Насколько это быстро?
− Кстати: функции времени: now() возвращает
дату и время начала транзакции. Используйте
clock_timestamp()
− Кстати - анонимные блоки:
l do $$ code $$
− Кстати: $$-quoting:
l $$, $RE$, $Im$, $ANYTHING_ELSE$
− select $Re$^("|[^"])+$Re$
48. Код
do $code$
declare
i integer;
ts1 timestamp with time zone;
ts2 timestamp with time zone;
res integer;
begin
execute 'prepare sth1 as select 1';
ts1:=clock_timestamp();
for i in 1..100000 loop
-- execute 'execute sth1' into res;
-- select 1 into res;
-– execute 'select 1' into res;
–- Реальная таблица: select 1 as n into stht
–- execute 'prepare sth1 as select n from stht';
–- select n into res from stht
–- execute 'select n from stht' into res
end loop;
ts2:=clock_timestamp();
raise notice 'diff=%', (ts2-ts1)::text;
end;
$code$
50. А зачем это, собственно,
надо?
l Разбор
l Построение плана
l Выполнение
l Нередко построение плана > выполнение
l С динамическим выполнением все идет каждый
раз по новой
l С prepared — только один раз
l pg_prepared_statements
51. Prepared statements
l Возвращаем резалтсет
− return query execute
− только обернув в функцию
l Что еще?
− save_record('table_name',
'column1_name','column2_value',
'column2_name','column2_value'...
)
l plpgsql — функции с переменным числом параметров
52. Кстати: upsert и вставка в
несколько таблиц
Подход в лоб:
with
input as
(select n from generate_series(1,10) as gs(n)),
upd as(
update ups set val=i.n from input i
where ups.n=i.n returning i.n
),
ins as(
insert into ups select * from input i
where not exists(select * from upd u
where i.n=u.n)
returning ups.n
)
select (select count(*) from upd) as updates,
(select count(*) from ins) as inserts
53. Upsert
l Подход в лоб не работает — race condition: после
обновления другой процесс может добавить
новые строки, и при вставке возникнет ошибка.
l Как правильно:
− Для каждой вставляемой строки
l Попытаться обновить
l Обновилась — хорошо, не обновилось — добавляем
l При нарушении уникальности повторяем попытку добавления
строки с самого начала
54. Кстати: plpgsql, savepoint и
исключения
l В plpgsql нет явных savepoint
l В plpgsql при использовании исключения в блоке
на входе в блок ставится неявный savepoint
l При возникновении исключения в блоке
происходит откат к нему
l Пример:
55. Кстати: plpgsql, savepoint и
исключения
•do $code$
begin
create temporary table
excdemo as select 1 as n;
begin
update excdemo set n=10;
raise notice
'BEFORE exception n=%',
(select n from excdemo);
raise sqlstate
'EX001';
exception
when sqlstate 'EX001' then
raise notice
'AFTER exception n=%',
(select n from excdemo);
end;
end;
$code$
•ЗАМЕЧАНИЕ: BEFORE exception
n=10
•ЗАМЕЧАНИЕ: AFTER exception
n=1
56. Кстати: plpgsql, savepoint и
исключения
l Позволяет делать логгирование
− Не нужны автономные транзакции (ну, почти не
нужны...)
l Позволяет выполнять потенциально
некорректный код!
− Это хорошо, а не плохо! Можно в живой боевой
сервер на ходу добавлять код, не обваливая все по
ошибке.
l «Можно» - разумеется, не значит «нужно».
57. Курсоры
l Задача — resultset из фунции
− funcname(..) returns table(colname coltype...)
− select * from funcname(...)
l Задача расширилась — несколько resultset'ов из
функции
l Задача еще более расширилась — МНОГО
resultset'ов
l Причем переменное число
l Что делать?
58. Курсоры
l Курсоры существуют только в пределах
транзакции
− Начало транзакции
− Вызов фунции
− Fetch all from <cursor name1>
− Fetch all from <cursor name2>
− …
− Fetch all from <cursor nameN>
− commit
59. Курсоры
l
refcursor
В сущности, курсор — это просто его имя; имя
курсора — строка
declare
cursname1 refcursor;
...
begin
...
open cursname1 for select ...
...
return array[cursname1,cursname2...]
61. Курсоры
l Требуется поддержка на клиенте
l Для курсоров она реализуется несложно — в perl
или php достаточно рекурсивно обойти resultset и
посмотреть типы колонок.
l В Java — используя декоратор, создать класс,
возвращающий ResultSet (как именно его
создавать — отдельная история)
l Для json можно поступить аналогично
− Отличть json от text на клиенте малореально. Хак:
можно сделать domain для text и смотреть длину.
62. generic_cursor
CREATE OR REPLACE FUNCTION generic_cursor(IN sql text, VARIADIC param text[] DEFAULT
ARRAY[]::text[])
RETURNS refcursor AS $BODY$
declare rv refcursor;
sthhash text := 'GENCURS'||md5(sql);
exec text := 'execute ' || sthhash ||
case when array_length(param,1) is null then ''
else '('||(select string_agg(coalesce(quote_literal(pv),'NULL'),',') from
unnest(param) as u(pv)) ||')' end;
begin
begin
open rv for execute exec;
exception
when sqlstate '26000' -- prepared statement does not exist
or sqlstate '0A000' -- table definition had changed
or sqlstate '42703' --- -/-
or sqlstate '42P11'
then
if sqlstate='0A000' or sqlstate='42703' then
execute 'deallocate '||sthhash;
end if;
execute 'prepare '||sthhash|| coalesce((select '('|| string_agg('text',',') |
| ')' from unnest(param) as u(pv)),'') ||' as '||sql;
open rv for execute exec;
end;
return rv;
end; $BODY$ LANGUAGE plpgsql
63. generic_exec
CREATE OR REPLACE FUNCTION generic_exec(
IN sql text,
VARIADIC param text[] DEFAULT
ARRAY[]::text[])
RETURNS SETOF record AS $BODY$
declare
cr refcursor:=
generic_cursor(sql, variadic param);
begin
return query execute 'fetch all from '||
quote_ident(cr::text); end;
$BODY$ LANGUAGE plpgsql
65. А оно надо? Да!
do $code$
declare
rv tt;
s1 timestamp;
s2 timestamp;
s3 timestamp;
i integer;
begin
s1:=clock_timestamp();
for i in 1..1000 loop
execute 'select t1.* from tt t1, tt t2, tt t3, tt t4, tt t5, tt t7, tt t8
where t1.id=t2.id and t2.id=t3.id and t3.id=t4.id and t1.id=t5.id and t5.id=t7.id and
t5.id=t8.id and t8.id=9999
and t1.id between 1 and 10000 and t4.id between 9999 and 200000 and t3.id>9000 and
exists(select * from tt t6 where t6.id=t5.id)
and t4.cnt=49 order by t4.cnt' into rv;
end loop;
s2:=clock_timestamp();
for i in 1..1000 loop
select * into rv from generic_exec('select t1.* from tt t1, tt t2, tt t3, tt t4, tt t5,
tt t7, tt t8
where t1.id=t2.id and t2.id=t3.id and t3.id=t4.id and t1.id=t5.id and t5.id=t7.id and
t5.id=t8.id and t8.id=9999
and t1.id between 1 and 10000 and t4.id between 9999 and 200000 and t3.id>9000 and
exists(select * from tt t6 where t6.id=t5.id)
and t4.cnt=49 order by t4.cnt') as ge(n integer, cnt bigint);
end loop;
s3:=clock_timestamp();
raise notice 'Run 1=%',s2-s1;
raise notice 'Run 2=%',s3-s2;
end; $code$
67. То же самое, но многопоточно
•$ pgbench -M prepared -U postgres -t 500 -n -j 2 -c 10 -f
generic_exec.pgb work
number of clients: 10
number of threads: 2
number of transactions per client: 500
number of transactions actually processed: 5000/5000
tps = 2340.001464 (including connections establishing)
tps = 2508.521577 (excluding connections establishing)
•$ pgbench -M prepared -U postgres -t 500 -n -j 2 -c 10 -f
plain_execute.pgb work
query mode: prepared
number of clients: 10
number of threads: 2
number of transactions per client: 500
number of transactions actually processed: 5000/5000
tps = 146.373869 (including connections establishing)
tps = 147.008750 (excluding connections establishing)
68. Альтернатива
l
JSON — в 9.2 row_to_json, array_to_json
− select
array_to_json(
array(
select row_to_json(c.*)
from pg_class c
)
)
l
В предыдущих версиях легко можно написать и
самостоятельно, используя, например, hstore
l
Можно просто возвращать массив hstore и
разбирать на клиенте
69. Заключение
l И снова: а зачем все это, собственно, надо?
l Производительность
l Переносимость
l Вычурность
l Пробуйте!
l Вопросы?