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锁时需要慎重考虑。
-- 创建账户表
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
函数释放锁。这样可以确保在更新账户余额时只有一个会话能够获得写锁,避免了并发写入导致的数据不一致问题。