數(shù)據(jù)庫面試必備之MySQL中的樂觀鎖與悲觀鎖
一、引言
在多用戶并發(fā)訪問數(shù)據(jù)庫的場景中,數(shù)據(jù)的一致性和完整性至關(guān)重要。為了避免并發(fā)操作導(dǎo)致的數(shù)據(jù)沖突問題,MySQL 提供了樂觀鎖和悲觀鎖兩種并發(fā)控制機(jī)制。理解它們的工作原理、適用場景以及如何在實(shí)際代碼中運(yùn)用,對(duì)于開發(fā)高效且可靠的數(shù)據(jù)庫應(yīng)用程序具有關(guān)鍵意義。
二、樂觀鎖
(一)原理
樂觀鎖基于一種樂觀的假設(shè),即認(rèn)為在大多數(shù)情況下,并發(fā)事務(wù)之間不會(huì)發(fā)生沖突。它不會(huì)在事務(wù)開始時(shí)就對(duì)數(shù)據(jù)進(jìn)行加鎖,而是在提交數(shù)據(jù)更新之前,檢查數(shù)據(jù)是否在事務(wù)執(zhí)行期間被其他事務(wù)修改過。如果沒有被修改,則允許提交更新;如果發(fā)現(xiàn)數(shù)據(jù)已經(jīng)被修改,則根據(jù)具體的業(yè)務(wù)邏輯決定是重試更新操作還是拋出異常。
(二)應(yīng)用場景
樂觀鎖適用于讀多寫少的場景,因?yàn)樵谶@種情況下,并發(fā)沖突的概率相對(duì)較低。例如,在一個(gè)電商系統(tǒng)中,商品的瀏覽次數(shù)統(tǒng)計(jì)就是一個(gè)典型的讀多寫少的操作。多個(gè)用戶可以同時(shí)查看商品詳情頁面,而對(duì)瀏覽次數(shù)的更新相對(duì)較少。
(三)示例代碼
假設(shè)我們有一個(gè) products
表,其中包含 id
(主鍵)、name
、quantity
和 version
字段。version
字段將作為樂觀鎖的版本號(hào)。
-- 創(chuàng)建 products 表 CREATE TABLE products ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50), quantity INT, version INT );
以下是在 Java 代碼中使用樂觀鎖更新商品數(shù)量的示例:
import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; public class OptimisticLockingExample { public static void main(String[] args) { // 數(shù)據(jù)庫連接信息 String url = "jdbc:mysql://localhost:3306/mydb"; String username = "root"; String password = "password"; try (Connection connection = DriverManager.getConnection(url, username, password)) { // 開啟事務(wù) connection.setAutoCommit(false); // 查詢商品信息并獲取當(dāng)前版本號(hào) String selectSql = "SELECT quantity, version FROM products WHERE id =?"; try (PreparedStatement selectStmt = connection.prepareStatement(selectSql)) { selectStmt.setInt(1, 1); // 假設(shè)要更新 id 為 1 的商品 try (ResultSet resultSet = selectStmt.executeQuery()) { if (resultSet.next()) { int currentQuantity = resultSet.getInt("quantity"); int currentVersion = resultSet.getInt("version"); // 模擬業(yè)務(wù)邏輯,減少商品數(shù)量 int newQuantity = currentQuantity - 1; // 更新商品數(shù)量和版本號(hào),使用樂觀鎖機(jī)制 String updateSql = "UPDATE products SET quantity =?, version = version + 1 WHERE id =? AND version =?"; try (PreparedStatement updateStmt = connection.prepareStatement(updateSql)) { updateStmt.setInt(1, newQuantity); updateStmt.setInt(2, 1); // 商品 id updateStmt.setInt(3, currentVersion); int rowsUpdated = updateStmt.executeUpdate(); if (rowsUpdated == 0) { // 說明在更新之前數(shù)據(jù)被其他事務(wù)修改,樂觀鎖沖突 System.out.println("樂觀鎖沖突,更新失敗"); connection.rollback(); } else { // 提交事務(wù) connection.commit(); System.out.println("商品數(shù)量更新成功"); } } } } } } catch (SQLException e) { e.printStackTrace(); } } }
在上述代碼中,首先查詢商品的當(dāng)前數(shù)量和版本號(hào),然后根據(jù)業(yè)務(wù)需求計(jì)算新的數(shù)量。在更新時(shí),通過 WHERE
子句中的版本號(hào)條件來檢查數(shù)據(jù)是否被其他事務(wù)修改。如果更新的行數(shù)為 0,則表示發(fā)生了樂觀鎖沖突。
三、悲觀鎖
(一)原理
與樂觀鎖相反,悲觀鎖持一種悲觀的態(tài)度,認(rèn)為在并發(fā)環(huán)境中,數(shù)據(jù)很可能會(huì)被其他事務(wù)修改。因此,在事務(wù)開始時(shí),就對(duì)要操作的數(shù)據(jù)進(jìn)行加鎖,直到事務(wù)完成并提交或回滾后才釋放鎖。這樣可以確保在事務(wù)執(zhí)行期間,其他事務(wù)無法對(duì)鎖定的數(shù)據(jù)進(jìn)行修改操作。
(二)應(yīng)用場景
悲觀鎖適用于寫多讀少且對(duì)數(shù)據(jù)一致性要求極高的場景。例如,在銀行系統(tǒng)的轉(zhuǎn)賬操作中,涉及到對(duì)賬戶余額的修改,必須確保在整個(gè)轉(zhuǎn)賬事務(wù)過程中,賬戶余額數(shù)據(jù)不會(huì)被其他并發(fā)事務(wù)干擾,此時(shí)使用悲觀鎖可以有效避免數(shù)據(jù)不一致的問題。
(三)示例代碼
同樣以 products
表為例,在 MySQL 中可以使用 SELECT... FOR UPDATE
語句來實(shí)現(xiàn)悲觀鎖。
-- 開啟事務(wù) START TRANSACTION; -- 使用悲觀鎖查詢商品信息并鎖定行 SELECT quantity FROM products WHERE id = 1 FOR UPDATE; -- 模擬業(yè)務(wù)邏輯,減少商品數(shù)量 UPDATE products SET quantity = quantity - 1 WHERE id = 1; -- 提交事務(wù) COMMIT;
在上述 SQL 代碼中,SELECT quantity FROM products WHERE id = 1 FOR UPDATE
語句會(huì)對(duì) id
為 1 的商品行進(jìn)行鎖定,直到事務(wù)提交或回滾。在鎖定期間,其他事務(wù)如果嘗試對(duì)該行數(shù)據(jù)進(jìn)行修改操作,將會(huì)被阻塞,直到鎖被釋放。
四、總結(jié)
樂觀鎖和悲觀鎖是 MySQL 中兩種重要的并發(fā)控制機(jī)制,它們各自適用于不同的應(yīng)用場景。在實(shí)際開發(fā)中,需要根據(jù)業(yè)務(wù)需求、數(shù)據(jù)讀寫比例以及對(duì)數(shù)據(jù)一致性的要求等因素,合理選擇使用樂觀鎖或悲觀鎖,以確保數(shù)據(jù)庫系統(tǒng)在高并發(fā)環(huán)境下能夠穩(wěn)定、高效地運(yùn)行,同時(shí)保證數(shù)據(jù)的完整性和一致性。
到此這篇關(guān)于數(shù)據(jù)庫面試必備之MySQL中的樂觀鎖與悲觀鎖的文章就介紹到這了,更多相關(guān)MySQL樂觀鎖與悲觀鎖內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Mysql關(guān)于數(shù)據(jù)庫是否應(yīng)該使用外鍵約束詳解說明
MySQL 外鍵約束(FOREIGN KEY)是表的一個(gè)特殊字段,經(jīng)常與主鍵約束一起使用。對(duì)于兩個(gè)具有關(guān)聯(lián)關(guān)系的表而言,相關(guān)聯(lián)字段中主鍵所在的表就是主表,外鍵所在的表就是從表。外鍵用來建立主表與從表的關(guān)聯(lián)關(guān)系,為兩個(gè)表的數(shù)據(jù)建立連接,約束兩個(gè)表中數(shù)據(jù)的一致性和完整性2021-10-10Ubuntu查看修改mysql的登錄名和密碼、安裝phpmyadmin
這篇文章主要介紹了Ubuntu查看修改mysql的登錄名和密碼、安裝phpmyadmin,本文分步驟給大家講解的非常詳細(xì),需要的朋友可以參考下2019-11-11mysql設(shè)置遠(yuǎn)程訪問數(shù)據(jù)庫的多種方法
最近有一同學(xué)問我MySQL無法遠(yuǎn)程訪問怎么呢,但能使用localhost來進(jìn)行方法,下面腳本之家來給各位介紹一下解決辦法,需要的朋友可以參考下2013-10-10MySQL物理備份與恢復(fù)工具XtraBackup使用小結(jié)
本文主要介紹了MySQL物理備份與恢復(fù)工具XtraBackup使用小結(jié),借助Percona XtraBackup工具實(shí)現(xiàn)MySQL的物理備份與恢復(fù),相當(dāng)于將整個(gè)MySQL進(jìn)行了復(fù)制,再粘貼到其他地方運(yùn)行,感興趣的可以了解一下2024-07-07在阿里云的CentOS環(huán)境中安裝配置MySQL的教程
這篇文章主要介紹了在阿里云的CentOS環(huán)境中安裝配置MySQL的教程,注意一下文章開頭所提到的系統(tǒng)自帶MariaDB的問題,需要的朋友可以參考下2015-12-12sql查詢語句教程之插入、更新和刪除數(shù)據(jù)實(shí)例
如果要在程序運(yùn)行過程中操作數(shù)據(jù)庫中的數(shù)據(jù),那得先學(xué)會(huì)使用SQL語句,下面這篇文章主要給大家介紹了關(guān)于sql查詢語句教程之插入、更新和刪除數(shù)據(jù)的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-06-06