mysql 锁

1、概述

MySQL锁是用于控制并发访问数据库中共享资源的一种机制,它可以防止多个并发事务同时对同一数据进行读写操作,从而保护数据的一致性和完整性。

以下是一个简单的例子,涉及到一个名为orders的表,其中包含了订单信息。

首先,我们创建一个orders表,包含订单号、客户名和订单状态三个字段。

CREATE TABLE orders (
  order_id INT PRIMARY KEY AUTO_INCREMENT,
  customer_name VARCHAR(50),
  order_status VARCHAR(20)
);

接下来,我们插入一些初始数据。

INSERT INTO orders (customer_name, order_status) VALUES ('John', 'pending');
INSERT INTO orders (customer_name, order_status) VALUES ('Alice', 'processing');

现在,假设有两个事务同时想要修改order_id为1的订单的order_status字段,一个事务想要将其更新为'confirmed',另一个事务想要将其更新为'cancelled'

事务1的更新语句如下:

BEGIN;
UPDATE orders SET order_status = 'confirmed' WHERE order_id = 1;
COMMIT;

事务2的更新语句如下:

BEGIN;
UPDATE orders SET order_status = 'cancelled' WHERE order_id = 1;
COMMIT;

在默认情况下,MySQL的隔离级别是REPEATABLE READ,意味着在事务1执行更新操作时,会对order_id为1的订单进行加锁,防止其他事务同时对其进行修改

如果事务2在事务1之前执行,事务2会被阻塞,并等待事务1释放锁。一旦事务1提交或回滚,事务2才能继续执行。

这种方式可以保证在并发访问情况下,同一条订单记录只能被一个事务修改,从而避免了数据的不一致性和冲突。但需要注意,过多的锁可能会导致性能下降,因此在使用MySQL锁时需要慎重考虑。

2、锁的使用

-- 创建账户表
CREATE TABLE IF NOT EXISTS accounts (
    account_id INT PRIMARY KEY AUTO_INCREMENT,
    account_name VARCHAR(50) NOT NULL,
    balance DECIMAL(10, 2) NOT NULL
);

-- 创建交易记录表
CREATE TABLE IF NOT EXISTS transactions (
    transaction_id INT PRIMARY KEY AUTO_INCREMENT,
    account_id INT NOT NULL,
    transaction_amount DECIMAL(10, 2) NOT NULL,
    transaction_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (account_id) REFERENCES accounts(account_id)
);

-- 插入账户数据
INSERT INTO accounts (account_name, balance)
VALUES ('Account A', 1000.00), ('Account B', 2000.00);

-- 使用 GET_LOCK 和 RELEASE_LOCK 实现写锁的业务逻辑
-- 假设账户余额足够,更新账户余额并插入一条交易记录
-- 否则,抛出一个错误

DELIMITER //
CREATE PROCEDURE update_account_balance_with_lock()
BEGIN
    DECLARE balance DECIMAL(10, 2);
    DECLARE transaction_amount DECIMAL(10, 2);
    SET transaction_amount = 100.00; -- 假设交易金额为100元
    
    -- 获取写锁
    SELECT GET_LOCK('update_account_balance_lock', 10); -- 锁名称为 'update_account_balance_lock',超时时间为 10 秒
    
    -- 查询当前账户余额
    SELECT balance INTO @balance FROM accounts WHERE account_id = 1;
    
    -- 检查账户余额是否足够
    IF balance >= transaction_amount THEN
        BEGIN
            -- 更新账户余额
            UPDATE accounts SET balance = balance - transaction_amount WHERE account_id = 1;
            
            -- 插入交易记录
            INSERT INTO transactions (account_id, transaction_amount)
            VALUES (1, transaction_amount);
            
            SELECT 'Transaction successful.' AS message;
        END;
    ELSE
        BEGIN
            -- 抛出错误
            SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Insufficient balance in the account.';
        END;
    END IF;
    
    -- 释放写锁
    SELECT RELEASE_LOCK('update_account_balance_lock');
END//
DELIMITER ;

-- 调用存储过程
CALL update_account_balance_with_lock();

以上代码中,我们在存储过程中使用了 GET_LOCK 函数获取一个写锁,锁的名称为 'update_account_balance_lock',超时时间为 10 秒。在获取到锁后,执行账户余额更新和交易记录插入操作,并在操作完成后使用 RELEASE_LOCK 函数释放锁。这样可以确保在更新账户余额时只有一个会话能够获得写锁,避免了并发写入导致的数据不一致问题。

你可能感兴趣的:(mysql)