В мире финансовых технологий, или FinTech, работа с реальными деньгами требует максимальной точности, аккуратности и безопасности. Как разработчик, вы должны убедиться, что транзакции вашей базы данных безопасны и не содержат несоответствий данных. В этом блоге мы обсудим некоторые ключевые выводы, которые я извлек из работы с реальными деньгами и MySQL, уделяя особое внимание использованию транзакций, уровней изоляции, блокировки строк и хранимых процедур.

  1. Всегда использовать транзакции

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

В MySQL вы можете использовать следующие команды для работы с транзакциями:

  • START TRANSACTION;: инициирует новую транзакцию.
  • COMMIT;: Фиксирует транзакцию, делая все изменения постоянными.
  • ROLLBACK;: откатывает транзакцию, отменяя все изменения.

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

2. Уровень изоляции имеет большое значение

Уровни изоляции определяют степень изоляции транзакций друг от друга. Другими словами, они контролируют видимость изменений данных, сделанных одной транзакцией, для других параллельных транзакций. MySQL поддерживает четыре уровня изоляции:

  • ЧИТАТЬ БЕЗ ЗАЯВЛЕНИЙ
  • ПРОЧИТАТЬ СОВЕРШЕНО
  • ПОВТОРЯЕМОЕ ЧТЕНИЕ (по умолчанию)
  • СЕРИАЛИЗУЕМЫЙ

Более высокие уровни изоляции обеспечивают более высокие гарантии согласованности, но могут снижать параллелизм и производительность. Выберите подходящий уровень изоляции для своего приложения с учетом таких факторов, как требования к целостности данных, производительность и потенциальные сценарии взаимоблокировок.

3. Используйте FOR UPDATE для блокировки строк

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

Например:

START TRANSACTION;
SELECT amount FROM accounts WHERE account_id = 1 FOR UPDATE;
-- Perform necessary calculations and updates here
COMMIT;

4. Если строки не возвращаются, блокировок нет

Если SELECT ... FOR UPDATE, запрос не возвращает строк, что означает отсутствие блокировок строк. Это полезно для предотвращения взаимоблокировок и повышения общей производительности приложения. Вы можете использовать это поведение в своих интересах, проверяя отсутствие заблокированных строк, прежде чем продолжить транзакцию.

5. Для длинных транзакций/запросов используйте хранимые процедуры

Хранимые процедуры — это предварительно скомпилированные подпрограммы, хранящиеся в базе данных, которые можно выполнить с помощью одного вызова. Они предлагают несколько преимуществ в финансовых приложениях:

  • Улучшенная производительность: хранимые процедуры компилируются один раз и кэшируются, что снижает нагрузку на повторный синтаксический анализ и компиляцию операторов SQL.
  • Сокращение сетевого трафика: поскольку процедура хранится в базе данных, по сети отправляется только вызов для ее выполнения, а не весь оператор SQL.
  • Повышенная безопасность. Инкапсулируя сложную логику в хранимые процедуры, вы можете ограничить прямой доступ к базовым таблицам и применить более строгие меры контроля доступа.

Для длинных транзакций или сложных запросов использование хранимых процедур может повысить производительность и безопасность, делая ваше приложение более эффективным и надежным при работе с реальными деньгами.

6. Используйте SKIP LOCKED и NOWAIT для лучшего контроля параллелизма

В дополнение к предложению FOR UPDATE MySQL предоставляет еще две опции для лучшего контроля параллелизма: SKIP LOCKED и NOWAIT. Эти параметры могут помочь вам справиться с ситуациями, когда ожидание заблокированной строки может привести к неэффективности или взаимоблокировкам.

  • SKIP LOCKED: этот параметр позволяет пропустить строки, которые в данный момент заблокированы другими транзакциями. Это может быть полезно в тех случаях, когда вы хотите обработать несколько записей, не дожидаясь освобождения блокировок, удерживаемых другими транзакциями. Например, если вы обрабатываете очередь финансовых транзакций, вы можете использовать ее SKIP LOCKED для обработки незаблокированных записей, не дожидаясь, пока заблокированные записи станут доступными:
SELECT * FROM transactions WHERE status = 'pending' ORDER BY created_at LIMIT 1 FOR UPDATE SKIP LOCKED;
  • NOWAIT: Параметр NOWAIT приводит к немедленному сбою запроса, если он сталкивается с заблокированной строкой, вместо ожидания снятия блокировки. Это может быть полезно в тех случаях, когда вы хотите избежать ожидания блокировки, позволяя реализовать пользовательскую обработку ошибок или логику повторных попыток. Например:
START TRANSACTION;
SELECT amount FROM accounts WHERE account_id = 1 FOR UPDATE NOWAIT;
-- If the row is locked, an error will be raised, and you can handle it accordingly
COMMIT;

Заключение

Работа с реальными деньгами в среде баз данных, такой как MySQL, требует глубокого понимания транзакций, уровней изоляции, блокировки строк и хранимых процедур. Используя эти концепции и методы, вы можете создавать более надежные, безопасные и эффективные финансовые приложения, обеспечивая