高并發(fā)環(huán)境下安全修改同一行數(shù)據(jù)庫數(shù)據(jù)的策略分享
隨著互聯(lián)網(wǎng)技術(shù)的飛速發(fā)展,越來越多的應(yīng)用需要在高并發(fā)環(huán)境中運(yùn)行,尤其是在處理大量用戶請求時(shí),數(shù)據(jù)庫的并發(fā)控制成為了業(yè)務(wù)的關(guān)鍵。本文將介紹如何在高并發(fā)情況下,安全地修改數(shù)據(jù)庫中的同一行數(shù)據(jù)。
1. 理解并發(fā)問題
并發(fā)操作的問題主要源于多個(gè)事務(wù)在同一時(shí)間嘗試訪問或修改同一行數(shù)據(jù)。這可能導(dǎo)致數(shù)據(jù)不一致,或者在極端情況下,導(dǎo)致數(shù)據(jù)丟失。為了防止這種情況,我們需要一種機(jī)制來確保在一個(gè)時(shí)間點(diǎn),只有一個(gè)事務(wù)能修改特定的數(shù)據(jù)行。
2. 悲觀鎖
悲觀鎖是一種常見的并發(fā)控制技術(shù),適用于數(shù)據(jù)競爭激烈的場景。當(dāng)一個(gè)事務(wù)嘗試讀取或修改數(shù)據(jù)時(shí),悲觀鎖會(huì)假設(shè)其他事務(wù)可能也會(huì)嘗試修改該數(shù)據(jù),因此會(huì)在數(shù)據(jù)上加鎖。其它事務(wù)在此期間將不能對該數(shù)據(jù)進(jìn)行修改,直到鎖被釋放。這種鎖機(jī)制主要通過數(shù)據(jù)庫提供的鎖機(jī)制實(shí)現(xiàn),如行鎖、表鎖等。
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = dataSource.getConnection();
//關(guān)閉自動(dòng)提交
conn.setAutoCommit(false);
//SELECT ... FOR UPDATE是悲觀鎖的典型應(yīng)用,此時(shí)會(huì)鎖住被選中的行,其他事務(wù)無法更新這些行
String sql = "SELECT * FROM product WHERE id = ? FOR UPDATE";
ps = conn.prepareStatement(sql);
ps.setInt(1, 1);
rs = ps.executeQuery();
if (rs.next()) {
//獲取當(dāng)前庫存數(shù)量
int count = rs.getInt("count");
if (count > 0) {
String updateSql = "UPDATE product SET count = count - 1 WHERE id = ?";
ps = conn.prepareStatement(updateSql);
ps.setInt(1, 1);
ps.executeUpdate();
conn.commit();
System.out.println("扣減成功,剩余庫存:" + (count - 1));
} else {
System.out.println("庫存不足,扣減失敗");
}
}
} catch (Exception e) {
if (conn != null) {
try {
//如果出現(xiàn)異常,進(jìn)行回滾
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
}
e.printStackTrace();
} finally {
//在最后,關(guān)閉資源
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (ps != null) {
try {
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}3. 樂觀鎖
與悲觀鎖不同,樂觀鎖在數(shù)據(jù)被修改時(shí)并不加鎖,而是在數(shù)據(jù)提交時(shí)檢查數(shù)據(jù)是否被其他事務(wù)修改過。如果在一個(gè)事務(wù)處理過程中數(shù)據(jù)被另一個(gè)事務(wù)修改,那么這個(gè)事務(wù)就會(huì)回滾并重新嘗試。樂觀鎖適用于數(shù)據(jù)競爭不激烈,讀操作遠(yuǎn)多于寫操作的場景。
在Java中,樂觀鎖常常通過"版本號(hào)"或者"時(shí)間戳"等方式實(shí)現(xiàn)。當(dāng)讀取數(shù)據(jù)時(shí),同時(shí)也會(huì)讀取數(shù)據(jù)的版本號(hào),當(dāng)更新數(shù)據(jù)時(shí),會(huì)帶上這個(gè)版本號(hào),只有當(dāng)數(shù)據(jù)庫中的當(dāng)前版本和帶上的版本號(hào)相同,才會(huì)更新數(shù)據(jù),并把版本號(hào)加1。
示例如下:首先,我們需要在實(shí)體類中添加一個(gè)版本號(hào)字段,并且用@Version注解標(biāo)注
import javax.persistence.*;
@Entity
@Data
public class Product {
@Id
@GeneratedValue
private Long id;
private Integer count;
//版本號(hào),用于實(shí)現(xiàn)樂觀鎖
@Version
private Integer version;
}然后,在進(jìn)行更新操作時(shí),JPA會(huì)自動(dòng)帶上版本號(hào)進(jìn)行檢查:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class ProductService {
@Autowired
private ProductRepository productRepository;
@Transactional
public void reduceCount(Long productId) {
//找到產(chǎn)品
Product product = productRepository.findById(productId).get();
//檢查庫存
if (product.getCount() > 0) {
//如果庫存足夠,減少庫存
product.setCount(product.getCount() - 1);
productRepository.save(product);
} else {
//庫存不足,拋出異常
throw new RuntimeException("庫存不足");
}
}
}4. MVCC(多版本并發(fā)控制)
多版本并發(fā)控制(MVCC)是一種用于增加數(shù)據(jù)庫并發(fā)性能的技術(shù),它會(huì)為每一行數(shù)據(jù)創(chuàng)建版本,以支持高并發(fā)讀寫。每當(dāng)有新的事務(wù)嘗試修改數(shù)據(jù),它都會(huì)創(chuàng)建一個(gè)新的數(shù)據(jù)版本,而不是直接修改原始數(shù)據(jù)。這樣可以讓讀事務(wù)繼續(xù)訪問舊版本數(shù)據(jù),而寫事務(wù)則修改新版本數(shù)據(jù),從而提高并發(fā)性能。
5. 分布式鎖
在分布式系統(tǒng)中,由于數(shù)據(jù)可能分散在多個(gè)節(jié)點(diǎn)上,我們需要一種全局的鎖機(jī)制來保證數(shù)據(jù)一致性。分布式鎖可以提供這樣的機(jī)制,常見的實(shí)現(xiàn)方式有基于數(shù)據(jù)庫的分布式鎖,基于緩存(Redis等)的分布式鎖,以及基于Zookeeper的分布式鎖等。
// 使用 Redisson 庫實(shí)現(xiàn) Redis 分布式鎖
RedissonClient redisson = Redisson.create();
RLock lock = redisson.getLock("myLock");
try{
lock.lock();
// 執(zhí)行業(yè)務(wù)操作
}finally{
lock.unlock();
}總的來說,在高并發(fā)環(huán)境下安全地修改同一行數(shù)據(jù),我們需要選擇合適的鎖機(jī)制,以保證數(shù)據(jù)的一致性和系統(tǒng)的性能。同時(shí),我們也需要關(guān)注系統(tǒng)的實(shí)際需求和性能瓶頸,以實(shí)現(xiàn)最優(yōu)的并發(fā)控制策略。
到此這篇關(guān)于高并發(fā)環(huán)境下安全修改同一行數(shù)據(jù)庫數(shù)據(jù)的策略分享的文章就介紹到這了,更多相關(guān)修改數(shù)據(jù)庫數(shù)據(jù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
解析springBoot-actuator項(xiàng)目構(gòu)造中health端點(diǎn)工作原理
這篇文章主要介紹了springBoot-actuator中health端點(diǎn)工作原理,對spring-boot-actuator的項(xiàng)目構(gòu)造,工作原理進(jìn)行了全面的梳理,側(cè)重health健康檢查部分2022-02-02
Spring Security其它權(quán)限校驗(yàn)方式&自定義權(quán)限校驗(yàn)方式
Java統(tǒng)計(jì)一個(gè)字符串在另外一個(gè)字符串出現(xiàn)次數(shù)的方法
spring cloud gateway 如何修改請求路徑Path
Spring?MVC數(shù)據(jù)響應(yīng)處理詳解
Spring?Boot?集成Elasticsearch模塊實(shí)現(xiàn)簡單查詢功能

