Сайт Романа ПарпалакаБлогКлючевые словапрограммирование

программирование

Статьи по этой теме:
Программирование


Одновременная вставка уникальных значений в словарные таблицы

Как правильно добавлять данные в словарную таблицу с уникальными строками одновременно из нескольких потоков? В PostgreSQL вот так:

CREATE TABLE words (
  id   SERIAL PRIMARY KEY,
  word TEXT NOT NULL UNIQUE
);

BEGIN;
SELECT id FROM words WHERE word = 'a';
INSERT IGNORE INTO words (word) VALUES ('a');
SELECT id FROM words WHERE word = 'a';
COMMIT;

В видео рассказываю, почему именно так, и показываю, как это работает.

00:25 Пример
01:34 Демонстрация наивной реализации вставки в словарные таблицы
02:32 Недостаток: появление дублей
03:45 Демонстрация уникального индекса
04:47 Недостаток одного только уникального индекса
05:55 Нет поддержки целостности ⇒ нужны транзакции
06:37 Демонстрация параллельной вставки в таблицу с уникальным индексом в транзакции
08:59 Вставка с игнорированием
09:17 Демонстрация вставки с игнорированием в транзакции с уровнем READ COMMITTED
12:15 Демонстрация дедлока при вставке с игнорированием в транзакции с уровнем REPEATABLE READ
13:44 Особенности метода в MySQL

30 августа 2020 года, 23:39     программирование · видео     Комментарии (4)

Кеширование и условие гонки

Одна из важных идей в программировании — кеширование. Если какая-то долгая операция выполняется часто, ее результат запоминается и переиспользуется, пока не изменятся условия, в которых результат был получен.

Кеширование не только экономит ресурсы и делает систему более отзывчивой. Без кеширования долгих операций система в принципе не сможет работать в режиме высокой нагрузки. Если вы разрабатываете кеширование в такой системе, важно помнить об одной частой ошибке проектирования — условии гонки (race condition). Расскажу об этой проблеме на выдуманном примере главной страницы интернет-магазина. А следующий раз поговорим о частном решении на уровне веб-сервера nginx.

Предположим, на главной странице интернет-магазина выводятся карточки наиболее популярных товаров. Товаров и отзывов много, и запрос к базе данных на получение списка выполняется 5 секунд. А остальную часть страницы можно сгенерировать за 100 миллисекунд. Предположим также, что у вас на сервере умеренное количество памяти, и он может одновременно обрабатывать не более 20 запросов (каждый в своем процессе). В таких условиях без кеширования вы в принципе не сможете обработать более 60 / 5,1 · 20 = 235 запросов в минуту к главной странице.

235 запросов в минуту — вроде как не очень мало. Но сайт быстро ляжет, если ссылку разместят на каком-нибудь относительно популярном ресурсе, и на него одновременно перейдут несколько десятков человек. Да и сайт, который не может открыться в течение 5 секунд, в современном вебе никому не составит серьезную конкуренцию.

Если запрос к базе данных выполнить один раз и запомнить список наиболее популярных товаров, скажем, на час, то сервер сможет выдержать до 60 / 0,1 · 20 = 12 000 запросов в минуту. Эта грубая оценка, конечно, уже не отражает реальную возможность сервера. Скорее всего, производительность окажется ниже из-за нехватки ресурсов процессора, сети и т. д. Но оценка показывает, что запрос популярных товаров перестает быть узким бутылочным горлышком в системе.

Однако запомнить результаты выполнения запроса недостаточно. Рано или поздно они устареют, и список популярных товаров надо пересчитать заново. Здесь и кроется та самая ошибка — условие гонки. Если результаты выполнения запроса устареют сразу для всех посетителей, и этих посетителей много, тяжелый запрос начнет выполняться одновременно.

Одновременное выполнение тяжелого запроса — неприятная ситуация по многим причинам:

  1. Несколько конкурентных запросов могут нагружать базу данных и выполняться медленнее, чем один запрос.
  2. Процессы приложения вместо полезной работы будут ждать окончания выполнения долгих запросов (если, конечно, у вас не асинхронная архитектура; хотя я сомневаюсь, что в этом случае вы бы стали сейчас читать в интернете об условии гонки). При достаточном количестве посетителей 20 процессов израсходуются очень быстро, и сайт перестанет открываться, пока, наконец, не завершится выполнение долгих запросов.
  3. Кроме того, если вы еще и допустили ошибку при программировании самого кеша, и система записывает данные в него неатомарно (например, в файл с помощью fopen/fwrite, file_put_contents и т. д.), вы с большой вероятностью получите испорченные данные (записанные в случайном порядке байты из разных процессов). Если система не готова к некорректным данным в кеше, она может вообще перестать работать, пока не посчитает, что данные в кеше устарели. А если готова — продолжит пытаться выполнить тяжелый запрос в конкурентном режиме и не восстановится до тех пор, пока не посчастливится записать корректные данные в кеш, или пока не упадет нагрузка.

Как избежать условия гонки? Есть два способа.

Синхронизировать параллельные процессы. Один из процессов «прогревает кеш» (выполняет долгую операцию). Остальные понимают, что процесс прогрева идет, и всё еще используют устаревшие данные из кеша. Способ не требует глубокой переработки приложения и подходит в простых случаях. Но универсальных методов синхронизации процессов не существует. Придется подбирать подходящий: блокировка файлов (flock), блокировки в базе данных, редисе и т. д.

Прогревать кеш в фоне. Если вы хотите обновлять список популярных товаров каждый час, вычисляете его по расписанию и складываете в кеш из отдельного процесса, который не имеет отношения к обработке http-запросов. Способ универсальный и хорошо показывает себя при росте нагрузки. Но может потребовать доработку приложения, если архитектура не приспособлена к выполнению фоновых задач по расписанию.

20 августа 2020 года, 10:57     программирование     Оставить комментарий

Очередь на основе PHP-FPM

Применил на практике прием, когда асинхронная очередь обработки сообщений реализовывается через PHP-FPM по протоколу fastcgi. На удивление, всё заработало сразу, никакой наладки не потребовалось.

Обычно PHP-FPM обрабатывает запросы от веб-сервера, например, nginx. Но никто не запрещает обращаться к PHP-FPM напрямую. Если все доступные рабочие процессы заняты, сообщения в нем как раз и ждут своей очереди на обработку.

Положительные стороны:
  • Не нужны дополнительные компоненты в системе.
  • PHP-FPM сам заботится о запуске рабочих процессов, достаточно отредактировать конфиг.
Отрицательные стороны:
  • Нет надежного хранения сообщений. Если процесс PHP-FPM упадет, сообщения потеряются.
  • Нет мониторинга. Если нужен — придется делать самостоятельно.

Чтобы сделать такую очередь, возьмите готовые библиотеки для общения по протоколу fastcgi, например, hollodotme/fast-cgi-client.

27 июня 2020 года, 13:13     программирование · PHP     Оставить комментарий

Спагетти-код

Большой перевод на Хабре про спагетти-код. Считаю статью важной, особенно в разговорах с людьми с ООП головного мозга, и не понимаю, почему у нее рейтинг +6.

Автор рассказывает о спагетти-коде как об одном из самых худших видов «плохого кода». Повествование закономерно начинается с оператора goto. Затем утверждается, что и объектно-ориентированное программирование может порождать спагетти.

Объектно-ориентированное программирование, первоначально разработанное для предотвращения спагетти-кода, стало (из-за использования без полного понимания «паттернов проектирования») одним из худших его источников. «Объект» может спокойно совмещать в себе код и данные, имея при этом любое количество интерфейсов, в то же время класс может свободно порождать подклассы по всей программе. Объектно-ориентированное программирование таит в себе большую мощь, и при дисциплинированном использовании, оно может быть очень эффективно. Но большинство программистов не могут с этим справиться, и со временем их код превращается в спагетти.

Потом рассматривается «Большой код» вообще. Автор заявляет, что у нас вообще нет хороших методов разработки «Большого кода», и противопоставляет его философии Unix — сделай одну вещь, и сделай ее хорошо.

20 июля 2013 года, 13:40     программирование · что почитать     Оставить комментарий

Современный рынок труда

Сегодня за обедом разговаривали с коллегой:
— Я чувствую спрос на себя как на программиста, а как на физика — не чувствую.
— И это в стране, первой запустившей человека в космос.

5 июля 2013 года, 00:05     по жизни · физика · программирование     Оставить комментарий

История программирования в СССР

История программирования в СССР (первая часть, вторая часть). Написано интересно и легко читается. Вот, например, про блок-схемы:

Давным давно, еще в докомпьютерную эру (с двадцатых годов) применялись для изображения последовательных процессов или алгоритмов блок-схемы (flowcharts). На них отдельные элементарные (на данном уровне абстракции) шаги изображались прямоугольничками, последовательность шагов — стрелочками, а ветвления (проверки условий) ромбиками. В самом-самом начале, когда языков программирования еще не было, а программы непосредственно кодировались числовыми кодами или, в лучшем случае, писались в «содержательных обозначениях», как рекомендовал патриарх нашего ремесла Александр Львович Брудно, блок-схемы были важным подспорьем. В таковом качестве во время оно их и застандартизировали.

Прошли десятилетия, то есть минули целые эпохи. А от программистов по-прежнему требовали чертить эти чертовы стрелочки и ромбики. Смысла в этом было аж никакого. Во-первых, теоретически доказано, что любой алгоритм, записанный на языке высокого уровня (на любом языке) имеет эквивалентное графическое представление в виде блок-схемы и почти наоборот, любая правильная блок-схема (фишка тут в слове «правильная») эквивалентна некоторому тексту на том или ином языке программирования. Но текст программы завсегда лучше блок-схемы, хотя бы потому, что последней можно только любоваться, а первый — это реальный кусок программы, который компилируется и выполняется на машине. Есть разница? Во-вторых, блок-схема может показать только синхронный, строго последовательный процесс вычислений, а в жизни такое наблюдается разве что в небольших несложных программах. Реальные же системы — это не однопоточные алгоритмы, а целые искусственные миры, где множество населяющих их объектов-персонажей (как программных, так и аппаратных) взаимодействуют друг с другом, посылая в непредсказуемые моменты времени сигналы и возбуждая прерывания, и где множество потоков вычислений исполняются одновременно и асинхронно, порой еще и на множестве процессоров и машин. Получается, что блок-схемами можно проиллюстрировать только маленькие кирпичики, но никак не всю систему, но зачем дополнительно иллюстрировать то, что и так внятно и понятно (с комментариями) записывается в текстовом виде?

Казалось бы, не нужны, так не пользуйтесь. А действительно не нужны — любой программист, хоть разработчик, хоть представитель заказчика предпочтет посмотреть исходный текст программы, а не эти картинки. Непрограммисту они — тем более до лампочки. И только ГОСТу, в лице его полномочного представителя — нормоконтролера, они нужны. Дороги как произведения изобразительного искусства. Он их проверяет на соответствие требованием оформления — такая-то ширина линий, столько-то миллиметров длина стрелочки, такой-то отступ квадратика от ромбика... Смысл схемы контролеру совершенно недоступен. Можете себе представить, какая халтура там процветала? В нашей конторе (как и в сотнях и тысячах таких же контор по всему Союзу) сидели тетки-чертежницы и тушью на кальках рисовали никому не нужные стрелочки и ромбики. Зато безработицы не было! Уже Союз загибался, но в девяностом году, если не ошибаюсь, успели под занавес выпустить новый ГОСТ все на ту же тему рисования блок-схем. Какая-то навязчивая, неотвратимая мания. Ну да ладно...

3 октября 2011 года, 23:23     программирование · что почитать     Оставить комментарий

Почему я никогда не стану настоящим программистом

Я не смогу стать настоящим программистом. Это было понятно давно. Но только что я смог сформулировать, почему. Потому, что я слишком консервативен и ленив для изучения новых технологий, если старые успешно работают.

Я заинтересовался Паскалем еще до того, как мы его начали проходить в школе, потому что это позволило мне кое-что вычислить. Я разобрался в Ассемблере, потому что замена часто выполняемого кода ассемблерными вставками сильно ускоряет программу. А вот разобраться с Си я заставлял себя долго (правда, Си мне приходилось использовать и я бы рано или поздно с ним разобрался).

Я начал программировать на Delphi, потому что это почти что Паскаль :) И еще потому, что на нем легко делать интерфейсы. Но вот делать что-либо в Visual Studio меня не тянет.

Я принялся за изучение PHP, потому что он был на моем хостинге и мне хотелось «оживить» свой сайт. Но браться за Python или Ruby я не вижу смысла.

То же самое и с объектно-ориентированным программированием. Я до сих пор в программах не создаю свои классы (или объекты, как там правильно?). Процедурного программирования пока хватает.

Для меня Java, .NET, MVC, XML, x64 и остальное X — пустой звук. Уж лучше играться с shell-скриптами.

25 февраля 2010 года, 01:24     программирование     Комментарии (6)

Delphi 2009 и не только

Я давно уже собираюсь выпустить новую версию The Game of Life, 3.6. Внес ряд полезных изменений. Проблема, которую осталось решить, как ни странно, связана с Вистой :)

Кажется, микрософты сделали так, что GDI в Висте эмулируется через DirectX (я не специалист в этом вопросе, точно утверждать не берусь). Из-за этого прорисовка через Canvas.Pixels стала в Висте жутко тормозить (по сравнению с XP). Я прекрасно понимаю, что Canvas.Pixels — сам по себе не очень быстрый способ вывода на экран. Но когда на каждом шаге меняется несколько точек, проще вызвать именно Canvas.Pixels, а не создавать отдельный буфер.

Версию 3.6 я задумал как переходную — в ней я хотел разобраться с графикой, а переписывание алгоритма расчета оставить для следующих версий. Мой приятель, просивший не упоминать его имя, помог с OpenGL. Теперь некритичная к быстродействию часть кода выводит графику через Canvas, а критичная — через OpenGL.

В ходе тестирования выяснилось, что в Висте есть еще одна проблема, которая связана, вероятно, с тем, что Виста запоминает содержимое PaintBox'а. Виста считает себя умнее всех остальных систем, в которых посылается сообщение wmpaint, и без спроса восстанавливает то, что было PaintBox'е, хотя оно уже перезаписано более свежей картинкой.

Логично предположить, что всё дело в Делфи 7. Она вообще не подозревает о существовании Висты :) Вполне возможно, что подобные ошибки уже исправлены в последней версии Делфи. Не долго думая, установил Делфи 2009. После небольших изменений кода программа скомпилировалась, но заработать отказалась из-за непонятной ошибки «Stream read error» при запуске. Более того, похожая ошибка стала возникать и в Делфи 7, правда, сообщение там было другое: «List capacity out of bounds (%d)». Эта же ошибка стала возникать и при запуске ранее скомпилированных экзешников. Я не знаю, что Делфи 2009 сделала с Вистой, что отказались работать экзешники, скомпилированные до ее установки!

Делфи 2009 — какое-то жуткое говно. Она, наконец, научилась понимать картинки PNG. Но вот с альфа-прозрачностью у нее до сих пор проблемы. Алё, Embarcadero, 2009 год на дворе. Как можно не поддерживать альфа-прозрачность? Отладчик по сравнению с седьмой версией сильно не улучшился. В программе возникла ошибка, так покажите, где она! Почему Visual Studio это умеет, а Делфи — нет?

К счастью, после удаления Делфи 2009 проблема с ошибкой «List capacity out of bounds (%d)» исчезла. Баг, связанный с обновлением PaintBox'а, исправлю каким-нибудь другим способом.

Да, и еще. Вот объясните мне, как можно пользоваться последними версиями программных продуктов и технологий, если они настолько кривые?

Добавлено: Обычно я не склонен к поспешным выводам, однако Делфи 2009 произвела на меня совсем уж удручающее впечатление :) По всей видимости, проблема с ошибкой при старте программы не связана с Делфи 2009. И, может быть, в ней можно заставить PNG работать нормально. В общем, нужно еще раз ее поставить, чтобы разобраться до конца.

28 марта 2009 года, 22:37     программирование     Комментарии (6)

Стиль оформления кода

Как заставить неправильный код выглядеть неправильно. Описывается один из вариантов оформления кода. Даже если вы — опытный программист, статья будет полезна для вас.

16 марта 2007 года, 19:06     программирование     Оставить комментарий
Поделиться
Записи