# 分布式锁使用指南 ## 概述 本文档介绍了项目中分布式锁的使用策略和最佳实践,帮助开发者正确使用分布式锁来保护关键业务操作。 ## 分布式锁的作用 分布式锁主要用于解决以下问题: 1. **防止并发冲突**:避免多个进程同时操作同一资源 2. **保证数据一致性**:确保关键操作的原子性 3. **防止重复操作**:避免重复执行相同的业务逻辑 ## 使用策略 ### 1. 短期操作(推荐不开启自动续期) **适用场景**: - 用户登录 - Token刷新 - 数据查询 - 简单的数据更新 **配置建议**: ```typescript const lock = await DistributedLockService.acquire({ key: 'user:login:username', ttl: 15, // 15秒过期 timeout: 8000, // 8秒超时 autoRenew: false, // 不开启自动续期 }); ``` **优点**: - 简单可靠,不会出现死锁 - 性能开销小 - 适合快速操作 ### 2. 长期操作(需要开启自动续期) **适用场景**: - 用户注册(包含邮件发送) - 密码重置(包含邮件发送) - 文件上传 - 复杂的数据处理 **配置建议**: ```typescript const lock = await DistributedLockService.acquire({ key: 'user:register:username:email', ttl: 60, // 60秒过期 timeout: 15000, // 15秒超时 autoRenew: true, // 开启自动续期 renewInterval: 20000, // 20秒续期一次 }); ``` **注意事项**: - 必须确保在操作完成后手动释放锁 - 进程退出时会自动清理锁 - 续期失败时会记录警告日志 ## 锁键名设计规范 ### 1. 命名规则 ``` {业务模块}:{操作类型}:{关键标识} ``` ### 2. 示例 ```typescript // 用户注册锁 'user:register:username:email'; // 用户登录锁 'user:login:username'; // 密码重置锁 'password:reset:email'; // Token刷新锁 'token:refresh:token_value'; ``` ### 3. 注意事项 - 键名要具有唯一性 - 避免使用过长的键名 - 使用有意义的标识符 ## 最佳实践 ### 1. 锁的粒度控制 **好的做法**: ```typescript // 针对特定用户加锁 const lock = await DistributedLockService.acquire({ key: `user:login:${username}`, ttl: 15, autoRenew: false, }); ``` **避免的做法**: ```typescript // 锁的粒度太粗,影响其他用户 const lock = await DistributedLockService.acquire({ key: 'user:login', // 所有用户登录都被阻塞 ttl: 15, autoRenew: false, }); ``` ### 2. 超时时间设置 **原则**: - 超时时间应该大于预期的操作时间 - 但不要设置过长,避免长时间阻塞 **建议**: ```typescript // 快速操作 timeout: 5000; // 5秒 // 中等操作 timeout: 10000; // 10秒 // 慢速操作 timeout: 30000; // 30秒 ``` ### 3. TTL设置 **原则**: - TTL应该大于操作时间 - 对于自动续期的锁,TTL可以设置得相对较短 **建议**: ```typescript // 快速操作 ttl: 10; // 10秒 // 中等操作 ttl: 30; // 30秒 // 慢速操作 ttl: 60; // 60秒 ``` ### 4. 错误处理 **必须使用 try-finally**: ```typescript const lock = await DistributedLockService.acquire(config); try { // 执行业务逻辑 await doSomething(); } finally { // 确保锁被释放 await lock.release(); } ``` ### 5. 监控和日志 **监控指标**: - 锁获取成功率 - 锁等待时间 - 锁释放情况 - 死锁检测 **日志记录**: ```typescript Logger.info(`获取分布式锁成功: ${lockKey}`); Logger.warn(`锁续期失败: ${lockKey}`); Logger.error(`获取锁超时: ${lockKey}`); ``` ## 常见问题 ### 1. 死锁问题 **原因**: - 进程崩溃但锁未释放 - 网络中断导致无法续期 - 业务逻辑异常导致锁未释放 **解决方案**: - 设置合理的TTL - 使用try-finally确保锁释放 - 进程退出时自动清理锁 - 定期检查并清理过期锁 ### 2. 性能问题 **原因**: - 锁的粒度太粗 - 锁的持有时间过长 - 频繁的锁竞争 **解决方案**: - 细化锁的粒度 - 优化业务逻辑,减少锁持有时间 - 使用读写锁分离 - 考虑使用乐观锁 ### 3. 一致性问题 **原因**: - 锁释放时机不当 - 业务逻辑异常 - 并发控制不当 **解决方案**: - 确保锁的原子性操作 - 使用事务保证数据一致性 - 添加业务层面的幂等性检查 ## 工具函数 ### 1. 装饰器使用 ```typescript class UserService { @withDistributedLock('user:register', 30, 10000) async register(userData: UserData) { // 业务逻辑 } } ``` ### 2. 手动管理锁 ```typescript async function complexOperation() { const lock = await DistributedLockService.acquire({ key: 'complex:operation', ttl: 60, autoRenew: true, }); try { // 复杂业务逻辑 await step1(); await step2(); await step3(); } finally { await lock.release(); } } ``` ## 总结 分布式锁是保证系统一致性的重要工具,但使用不当也会带来问题。遵循以下原则: 1. **合理选择锁策略**:短期操作不续期,长期操作要续期 2. **控制锁粒度**:避免锁的粒度过粗 3. **设置合理超时**:避免无限等待 4. **确保锁释放**:使用try-finally模式 5. **监控和日志**:及时发现问题 6. **定期清理**:防止死锁积累 通过合理使用分布式锁,可以有效保证系统的数据一致性和业务正确性。