InnoDB 里的行鎖機制主要通過索引來實現,而不是直接對表中的記錄加鎖。具體來說,InnoDB 使用以下幾種鎖定機制來實現行鎖:
- 記錄鎖 (Record Lock):鎖定單個索引記錄。
- 間隙鎖 (Gap Lock):鎖定索引記錄之間的間隙,防止其他事務在該間隙插入新記錄。
- 臨鍵鎖 (Next-Key Lock):結合記錄鎖和間隙鎖,鎖定一個索引記錄以及其前面的間隙。
行鎖的實現
InnoDB 的行鎖是通過對索引項加鎖來實現的,這意味著只有通過索引訪問數據,InnoDB 才能使用行級鎖定。如果一個 SQL 語句不使用索引,InnoDB 會退化為表鎖。
鎖的類型
- 共享鎖 (S 鎖):允許事務讀取一條記錄,但不允許修改。
- 排他鎖 (X 鎖):不允許其他事務讀取或修改一條記錄。
示例
假設有一個 employees
表:
CREATE TABLE employees (id INT PRIMARY KEY,name VARCHAR(100),salary DECIMAL(10, 2)
) ENGINE=InnoDB;
使用 FOR UPDATE
加鎖
BEGIN;
SELECT * FROM employees WHERE id = 1 FOR UPDATE;
該語句在 id = 1
的記錄上加了一個排他鎖 (X 鎖)。由于 id
是主鍵,這是一個記錄鎖。
插入操作導致的間隙鎖
BEGIN;
INSERT INTO employees (id, name, salary) VALUES (5, 'Alice', 5000);
插入操作會導致在相應索引上的間隙鎖,以防止其他事務在相同間隙中插入記錄。
使用共享鎖
BEGIN;
SELECT * FROM employees WHERE salary > 4000 LOCK IN SHARE MODE;
該語句在符合條件的記錄上加了共享鎖 (S 鎖),同時會在表上加意向共享鎖 (IS 鎖)。
Java 代碼示例
假設你想通過 Java 代碼來執行這些操作,可以使用 JDBC:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;public class InnoDBLockExample {public static void main(String[] args) {String url = "jdbc:mysql://localhost:3306/yourdatabase";String user = "yourusername";String password = "yourpassword";try (Connection conn = DriverManager.getConnection(url, user, password)) {// 開始事務conn.setAutoCommit(false);// 加鎖查詢String query = "SELECT * FROM employees WHERE id = ? FOR UPDATE";try (PreparedStatement pstmt = conn.prepareStatement(query)) {pstmt.setInt(1, 1);try (ResultSet rs = pstmt.executeQuery()) {while (rs.next()) {System.out.println("ID: " + rs.getInt("id") + ", Name: " + rs.getString("name"));}}}// 提交事務conn.commit();} catch (SQLException e) {e.printStackTrace();}}
}
該 Java 代碼示例演示了如何使用 JDBC 來執行一個帶有行鎖的查詢。首先,設置連接為手動提交模式,然后執行一個帶有 FOR UPDATE
的查詢以加鎖。最后,提交事務。