Это 16-й день моего участия в ноябрьском испытании обновлений.Подробности о событии:Вызов последнего обновления 2021 г.
Управление транзакциями в Spring очень простое, достаточно добавить аннотации к методу.@Transactional
, Spring может автоматически помочь нам открывать, фиксировать и откатывать транзакции. Даже многие люди уже связали транзакции Spring с@Transactional
Знак равенства помечен, пока есть операции, связанные с базой данных, напрямую добавьте метод в метод@Transactional
аннотация.
По правде говоря, я всегда был таким раньше, пока не употребил@Transactional
Вызвал производственную аварию, и эта производственная авария также привела к тому, что мое выступление в этом месяце стало D...
@Transactional
производственная авария
В 2019 году я делал внутренний реимбурсный проект в компании, там такая бизнес-логика:
1. Сотрудники, работающие сверхурочно, могут брать такси напрямую через Didi Chuxing Enterprise Edition, а оплата такси на следующий день может быть напрямую синхронизирована с нашей платформой возмещения расходов.
2. Сотрудники могут проверить свои собственные расходы на такси на платформе возмещения и создать форму возмещения для возмещения.При создании формы возмещения будет создан поток утверждения (унифицированная платформа процессов), чтобы руководитель мог утвердить
Код для создания формы возмещения в то время был написан следующим образом:
/**
* 保存报销单并创建工作流
*/
@Transactional(rollbackFor = Exception.class)
public void save(RequestBillDTO requestBillDTO){
//调用流程HTTP接口创建工作流
workflowUtil.createFlow("BILL",requestBillDTO);
//转换DTO对象
RequestBill requestBill = JkMappingUtils.convert(requestBillDTO, RequestBill.class);
requestBillDao.save(requestBill);
//保存明细表
requestDetailDao.save(requestBill.getDetail())
}
Код очень простой и "изящный". Сначала через http-интерфейс вызывается движок рабочего процесса для создания потока утверждения, а затем сохраняется форма возмещения. Чтобы обеспечить транзакцию операции, добавляется весь метод .@Transactional
Аннотации (подумайте об этом, действительно ли это гарантирует транзакции?).
Проект возмещения расходов относится к внутреннему проекту компании, сам по себе он не отличается высоким уровнем параллелизма, и система работает стабильно.
Однажды днем в конце года (несколько дней назад случился сильный снегопад, и было много людей, пользующихся такси), компания отправила уведомление по электронной почте, в котором говорилось, что окно ежегодного возмещения подходит к концу, и невозмещенные расходы должны были быть возмещены как можно скорее, а механизм рабочего процесса в тот день работал.
После получения письма количество получателей возмещения начало постепенно увеличиваться, и оно достигло своего пика, когда он был близок к выходу с работы.В это время система возмещения начала давать сбои: платформа мониторинга базы данных получала тревожные текстовые сообщения. , подключение к БД недостаточно, и происходит большое количество взаимоблокировок, в журнале отображается вызывающий процесс Интерфейс движка имеет большое количество таймаутов, в то же время он постоянно выдает подсказкиCannotGetJdbcConnectionException
, соединение с пулом соединений с базой данных заполнено.
После сбоя мы попытались убить процесс взаимоблокировки и принудительно перезапустить его, но сбой повторился менее чем через 10 минут и получил большое количество телефонных жалоб.
В конце концов, не было другого пути, кроме как рассылать всем сотрудникам электронные письма о техническом обслуживании и отчеты о сбоях, а затем производительность была поставлена на двойку, жалкая...
Анализ причин аварии
Благодаря анализу журнала мы можем легко определить, что причиной сбоя является метод save () сохранения формы возмещения, и виновником является то, что@Transactional
аннотация.
мы знаем@Transactional
Аннотация реализована с помощью АОП, и ее суть заключается в перехвате до и после выполнения целевого метода. Присоединитесь или создайте транзакцию до выполнения целевого метода.После выполнения метода выполнения выберите зафиксировать или откатить транзакцию в соответствии с реальной ситуацией.
Когда Spring встречает эту аннотацию, он автоматически получает соединение из пула соединений с базой данных, запускает транзакцию, а затем привязывает ее к ThreadLocal.Для всего метода, обернутого аннотацией @Transactional, используется одно и то же соединение. Если у нас есть трудоемкие операции, такие как вызовы стороннего интерфейса, сложная бизнес-логика и крупномасштабная обработка данных, это заставит нас занять это соединение в течение длительного времени, а соединение с базой данных было занято и не освобождено. . Когда подобных операций будет слишком много, пул соединений с базой данных будет исчерпан.
Выполнение операций RPC в транзакции приводит к разрыву пула соединений с базой данных, что является типичной проблемой долгосрочных транзакций. Подобные операции включают в себя большое количество запросов данных в транзакции, обработку бизнес-правил и т. д.
Что такое долгий бизнес?
Как следует из названия, это транзакция, которая выполняется в течение длительного времени и долгое время не совершалась, ее также можно назвать крупной транзакцией.
Какие проблемы могут вызвать длинные транзакции?
Общие опасности, вызванные длинными транзакциями:
- Пул соединений с базой данных заполнен, и приложение не может получить ресурсы соединения;
- Легко вызвать взаимоблокировку базы данных;
- Откат базы данных занимает много времени;
- В архитектуре master-slave задержка master-slave будет увеличиваться.
Как избежать длинных транзакций?
Теперь, когда вы знаете об опасностях длинных транзакций, как избежать проблем с длинными транзакциями в разработке?
Очевидно, что целью решения длинной транзакции является разделение метода транзакции, попытка сделать транзакцию меньше и быстрее, а также уменьшить степень детализации транзакции.
Теперь, когда упоминается гранулярность транзакций, давайте рассмотрим, как Spring управляет транзакциями.
декларативная сделка
Сначала нам нужно знать, что с помощью метода@Transactional
Операция аннотации для управления транзакциями называется декларативной транзакцией.
Преимущество использования декларативных транзакций очевидно, то есть они очень просты в использовании и могут автоматически помогать нам открывать, фиксировать и откатывать транзакции. Таким образом, программистам нужно сосредоточиться только на бизнес-логике.
Один из самых больших недостатков декларативных транзакций заключается в том, что гранулярность транзакций — это весь метод, и его нельзя точно контролировать.
Противоположностью декларативным транзакциям являются программные транзакции.
На основе базового API разработчики вручную управляют транзакциями, такими как открытие, фиксация и откат операций в коде. Может использоваться в весенних проектахTransactionTemplate
Объект класса, который вручную управляет транзакцией.
@Autowired
private TransactionTemplate transactionTemplate;
...
public void save(RequestBill requestBill) {
transactionTemplate.execute(transactionStatus -> {
requestBillDao.save(requestBill);
//保存明细表
requestDetailDao.save(requestBill.getDetail());
return Boolean.TRUE;
});
}
Самым большим преимуществом использования программных транзакций является то, что вы можете точно настроить объем транзакции.
Таким образом, самый простой способ избежать длинных транзакций — не использовать декларативные транзакции.@Transactional
Вместо этого вручную управляйте диапазоном транзакций с помощью запрограммированных транзакций.
Некоторые студенты скажут,@Transactional
Это так просто в использовании, есть ли способ использовать оба@Transactional
и избежать длинных транзакций?
Затем вам нужно разделить метод, чтобы отделить логику, не требующую управления транзакциями, от операции транзакции:
@Service
public class OrderService{
public void createOrder(OrderCreateDTO createDTO){
query();
validate();
saveData(createDTO);
}
//事务操作
@Transactional(rollbackFor = Throwable.class)
public void saveData(OrderCreateDTO createDTO){
orderDao.insert(createDTO);
}
}
query()
иvalidate()
Нет необходимости в транзакциях, мы совмещаем это с методом транзакцийsaveData()
открыть.
Конечно, этот раскол ударил бы по использованию@Transactional
Классический сценарий, когда транзакция не вступает в силу после аннотации, очень легко для многих новичков сделать эту ошибку.@Transactional
Декларативная транзакция аннотации работает через spring aop, а spring aop нужно генерировать прокси-объект.Исходный объект по-прежнему используется для вызовов методов непосредственно в том же классе, и транзакция не вступает в силу. Несколько других распространенных сценариев, когда транзакции не вступают в силу:
"
- @Transactional применяется к непублично оформленным методам
- Распространение атрибута аннотации @Transactional задано неправильно
- Откат атрибута аннотации @TransactionalПри ошибке настройки
- Вызовы методов в том же классе приводят к сбою @Transactional
- Исключение, пойманное catch, приводит к сбою @Transactional
"
Правильный метод разделения должен использовать следующие два:
- Вы можете поместить методы в другой класс, например, добавив
manager层
, инжектируемый через пружину, что соответствует условиям вызова между объектами.
@Service
public class OrderService{
@Autowired
private OrderManager orderManager;
public void createOrder(OrderCreateDTO createDTO){
query();
validate();
orderManager.saveData(createDTO);
}
}
@Service
public class OrderManager{
@Autowired
private OrderDao orderDao;
@Transactional(rollbackFor = Throwable.class)
public void saveData(OrderCreateDTO createDTO){
orderDao.saveData(createDTO);
}
}
- Добавить класс запуска
@EnableAspectJAutoProxy(exposeProxy = true)
, используемый в методеAopContext.currentProxy()
Получите класс прокси, используйте транзакцию.
SpringBootApplication.java
@EnableAspectJAutoProxy(exposeProxy = true)
@SpringBootApplication
public class SpringBootApplication {}
OrderService.java
public void createOrder(OrderCreateDTO createDTO){
OrderService orderService = (OrderService)AopContext.currentProxy();
orderService.saveData(createDTO);
}
резюме
использовать@Transactional
Аннотации действительно удобны при разработке, но небольшая небрежность может привести к проблемам с длительными транзакциями. Поэтому для сложной бизнес-логики я рекомендую вам использовать программные транзакции для управления транзакциями.Конечно, если вам нужно использовать@Transactional
, метод можно разделить по двум упомянутым выше схемам.