Проблема увеличения счетчика базы данных вручную
Я разрабатываю REST API с использованием Java и Spring Boot для управления покупками и клиентами. В моей базе данных MySQL у меня есть таблица Purchase
с колонкой, которая хранит уникальный ticketId
, Это не первичный ключ.
Когда новая покупка добавляется (делая PUT
запрос), я создаю новую покупку из данных, указанных в запросе, получить максимум ticketId
, увеличить его на единицу и сохранить в базе данных. Первичный ключ автоматически увеличивается.
Это мой код:
@Transactional
public boolean saveNewPurchase(PurchaseDTO data) {
Purchase p = createPurchaseFromData(data);
Long idTicket = purchaseDao.getMaxIdTicket();
p.setIdTicket(idTicket + 1);
save(p);
}
Есть ли здесь проблемы с параллелизмом? Скажем два PUT
запросы выполняют этот метод параллельно, они могут получить тот же максимум idTicket
так что нарушать уникальный idTicket
Ограничение, когда сохранить вторую покупку?
Если так, как я мог решить это? Будет делать метод synchronized
решать проблему?
Благодарю.
2 ответа
Да, здесь есть проблема параллелизма. Два разных потока могут получить одинаковые maxIdTicket
, а затем сохранить покупку с тем же ticketId
,
Я вижу 3 решения здесь:
- Используйте синхронизированный
- Используйте
AtomicInteger
сохранить счетчик в памяти - Используйте конкретную таблицу в MySQL, только с одним столбцом,
AUTO_INCREMENT
и вставлять / получать строки каждый раз, когда вам нужно значение счетчика. С другими RDBMS вы можете использовать последовательность, но я почти уверен, что в MySQL нет последовательностей.
Первые 2 решения не работают в распределенной среде, поэтому я бы сделал третье.
Да, это, безусловно, склонны к проблемам параллелизма. Одно из предложений - сохранить счетчик в памяти с помощью AtomicInteger, чтобы в конечном итоге вы не всегда получали текущий максимальный идентификатор из БД, что может вызвать состояние гонки. Таким образом, когда приложение запускается, запрашивая БД, оно может сохранить maxId в памяти. Это надежно, даже в случае любого сбоя, он всегда может прочитать информацию из БД.
Это не сработает в распределенной среде, в этом случае, если в комментариях предложено использовать другую таблицу, предназначенную для хранения счетчика, то это лучший подход.