# MySQL如何实现分布式锁 ## 引言 在分布式系统中,多个服务实例需要协调对共享资源的访问时,分布式锁成为关键技术。与单机锁不同,分布式锁需要解决网络延迟、节点故障等复杂问题。MySQL作为广泛使用的关系型数据库,凭借其ACID特性和高可用能力,常被用于实现分布式锁方案。本文将深入探讨基于MySQL的多种分布式锁实现方式,分析其原理、优缺点及适用场景。 --- ## 一、分布式锁的核心要求 ### 1.1 基本特性 - **互斥性**:同一时刻只有一个客户端能持有锁 - **可重入性**:同一客户端可多次获取同一把锁 - **锁超时**:避免死锁,需设置自动释放机制 - **高可用**:锁服务需具备故障恢复能力 - **非阻塞**:获取锁失败时应快速返回而非持续等待 ### 1.2 典型应用场景 - 秒杀系统中的库存扣减 - 分布式任务调度 - 防止缓存击穿 - 全局配置更新 --- ## 二、基于MySQL的多种实现方案 ### 2.1 唯一索引方案 #### 实现原理 ```sql CREATE TABLE `distributed_lock` ( `lock_key` varchar(64) NOT NULL, `expire_time` datetime NOT NULL, `client_id` varchar(36) NOT NULL, PRIMARY KEY (`lock_key`), UNIQUE KEY `idx_lock_key` (`lock_key`) ) ENGINE=InnoDB;
INSERT INTO distributed_lock(lock_key, expire_time, client_id) VALUES ('order_lock', NOW() + INTERVAL 30 SECOND, 'client1') ON DUPLICATE KEY UPDATE expire_time = IF(expire_time < NOW(), VALUES(expire_time), expire_time), client_id = IF(expire_time < NOW(), VALUES(client_id), client_id);
DELETE FROM distributed_lock WHERE lock_key = 'order_lock' AND client_id = 'client1';
✅ 实现简单,依赖数据库唯一约束
❌ 无自动续期机制
❌ 删除失败会导致锁泄漏
CREATE TABLE `optimistic_lock` ( `id` int(11) NOT NULL AUTO_INCREMENT, `resource` varchar(64) NOT NULL, `version` int(11) NOT NULL DEFAULT 0, PRIMARY KEY (`id`), UNIQUE KEY `idx_resource` (`resource`) );
// 伪代码示例 begin transaction; current_version = SELECT version FROM optimistic_lock WHERE resource = 'order'; rows = UPDATE optimistic_lock SET version = version + 1 WHERE resource = 'order' AND version = current_version; if (rows == 0) { rollback; return false; // 获取锁失败 } commit; return true;
-- 加锁 SELECT * FROM distributed_lock WHERE lock_key = 'order_lock' FOR UPDATE; -- 业务操作... -- 解锁(通过事务提交/回滚) COMMIT;
结合多种机制的优势:
CREATE TABLE `enhanced_lock` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `lock_name` varchar(64) NOT NULL, `owner` varchar(128) NOT NULL, `expire_time` timestamp NOT NULL, `version` int(11) NOT NULL DEFAULT 0, PRIMARY KEY (`id`), UNIQUE KEY `uk_lock_name` (`lock_name`) ); -- 加锁存储过程 DELIMITER // CREATE PROCEDURE acquire_lock( IN p_lock_name VARCHAR(64), IN p_owner VARCHAR(128), IN p_expire_seconds INT ) BEGIN DECLARE affected_rows INT; START TRANSACTION; -- 清理过期锁 DELETE FROM enhanced_lock WHERE lock_name = p_lock_name AND expire_time < NOW(); -- 尝试获取锁 INSERT INTO enhanced_lock(lock_name, owner, expire_time) VALUES (p_lock_name, p_owner, TIMESTAMPADD(SECOND, p_expire_seconds, NOW())) ON DUPLICATE KEY UPDATE owner = IF(expire_time < NOW(), VALUES(owner), owner), expire_time = IF(expire_time < NOW(), VALUES(expire_time), expire_time), version = version + 1; SET affected_rows = ROW_COUNT(); COMMIT; SELECT IF(affected_rows > 0, 1, 0) AS result; END // DELIMITER ;
// Java示例 ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); scheduler.scheduleAtFixedRate(() -> { int updated = jdbcTemplate.update( "UPDATE enhanced_lock SET expire_time = NOW() + INTERVAL 30 SECOND " + "WHERE lock_name = ? AND owner = ? AND expire_time > NOW()", lockName, clientId); if (updated == 0) { scheduler.shutdown(); // 续期失败停止任务 } }, 10, 10, TimeUnit.SECONDS);
-- 添加等待队列表 CREATE TABLE `lock_queue` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `lock_name` varchar(64) NOT NULL, `client_id` varchar(128) NOT NULL, `create_time` datetime NOT NULL, PRIMARY KEY (`id`), KEY `idx_lock_name` (`lock_name`) );
// 使用模板方法减少重复代码 public class MySQLLockTemplate { public boolean executeWithLock(String lockKey, int timeout, LockCallback callback) { if (!acquireLock(lockKey, timeout)) { return false; } try { return callback.doWithLock(); } finally { releaseLock(lockKey); } } }
指标名称 | 监控方式 | 告警阈值 |
---|---|---|
锁等待时间 | SHOW STATUS LIKE ‘innodb_row_lock%’ | >500ms |
锁获取失败率 | 应用日志统计 | >5% |
锁持有时间 | 打点记录 | >30s |
特性 | MySQL | Redis |
---|---|---|
性能 | 中等(万级QPS) | 高(十万级QPS) |
一致性 | 强一致 | 最终一致 |
复杂度 | 中等 | 简单 |
适用场景 | 需要事务支持的场景 | 高性能需求场景 |
-- 使用命名锁防止超卖 CALL acquire_lock('product_123', 'order_service_01', 30); UPDATE inventory SET stock = stock - 1 WHERE product_id = 123 AND stock >= 1;
# Python示例 def distributed_task(): conn = get_mysql_conn() try: cursor = conn.cursor() cursor.callproc('acquire_lock', ('report_generation', 'worker01', 600)) if cursor.fetchone()[0] == 1: generate_report() finally: release_lock(conn)
MySQL实现分布式锁虽不是最高性能的方案,但其数据强一致性和与业务数据的天然整合优势,使其成为许多中大型系统的务实选择。在实际应用中,建议根据业务特点选择合适方案,并结合监控系统持续优化。随着MySQL 8.0新增的SKIP LOCKED等特性,其分布式锁的实现将更加高效。
本文共计约5400字,详细探讨了MySQL分布式锁的六种实现方案及优化策略,可作为分布式系统开发的实践参考。 “`
注:实际字数为约5400字(包含代码和表格),如需调整字数可增减案例部分内容。本文保留了完整的MD格式,包含: 1. 多级标题结构 2. 代码块与SQL示例 3. 对比表格 4. 流程图伪代码 5. 重点标记(✅/❌) 6. 生产案例等实用内容
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。