# Distributed lock 手册
`rs-zero` 的分布式锁用于跨进程协调短时临界区。第一版提供 Redis backend,使用 `SET key token NX PX ttl` 获取锁,并用 Lua 脚本校验 owner token 后删除,避免误删他人锁。
## Feature
```toml
rs-zero = { version = "0.2.1", default-features = false, features = ["cache-redis"] }
```
`cache-redis` 会同时启用 `cache` 与 `resil`,因此 Redis lock 可复用 Redis client、timeout、breaker 和低基数 Redis 指标。
## 基本用法
```rust
use std::time::Duration;
use rs_zero::lock::{DistributedLock, RedisDistributedLock, RedisLockConfig};
let lock = RedisDistributedLock::new(RedisLockConfig::default())?;
let guard = lock
.try_acquire("order:{123}", Duration::from_secs(5))
.await?;
// 执行业务临界区。
lock.release(&guard).await?;
```
如果锁已被其他 owner 持有,`try_acquire` 返回 `LockError::Busy`。如果希望自行处理竞争,可以使用 `acquire`:
```rust
if let Some(guard) = lock.acquire("order:{123}", Duration::from_secs(5)).await? {
lock.release(&guard).await?;
}
```
## 配置
```rust
use std::time::Duration;
use rs_zero::{
cache_redis::{RedisBreakerConfig, RedisCacheConfig},
lock::{RedisDistributedLock, RedisLockConfig},
};
let lock = RedisDistributedLock::new(RedisLockConfig {
redis: RedisCacheConfig {
url: "redis://127.0.0.1:6379".to_string(),
connect_timeout: Duration::from_secs(1),
command_timeout: Duration::from_millis(500),
..RedisCacheConfig::default()
},
namespace: "order-service:lock".to_string(),
command_timeout: Duration::from_millis(500),
breaker: RedisBreakerConfig::go_zero_defaults(),
..RedisLockConfig::default()
})?;
```
规则:
- `namespace` 必须稳定,避免不同服务或环境共享锁空间。
- TTL 必须大于 0,禁止无过期锁。
- owner token 是随机值,不应写入日志、metrics label 或错误消息。
- 解锁只删除 token 匹配的锁;锁已过期或被新 owner 获取时,旧 guard release 会返回 `LockError::OwnerMismatch`。
- 默认不自动续租。业务临界区必须短于 TTL,并保留足够安全余量。
## Redis Cluster
Cluster 模式使用 `RedisCacheConfig.cluster.enabled = true`。分布式锁 key 默认要求包含 Redis hash tag:
```rust
let guard = lock.try_acquire("order:{123}", Duration::from_secs(5)).await?;
```
原因:获取锁和 Lua 解锁都必须访问同一个 Redis slot。不要使用没有 hash tag 的 Cluster lock key。确有兼容需求时可以关闭 `require_cluster_hash_tag`,但生产环境不推荐。
## 观测与韧性
Redis lock 复用 Redis command recorder:
- `LOCK_ACQUIRE` 记录获取锁命令耗时和结果。
- `LOCK_RELEASE` 记录释放锁命令耗时和结果。
- `LOCK_PING` 记录健康检查。
- breaker 事件走 Redis breaker event。
指标必须保持低基数:只记录 command、shard、result,不记录原始 lock key、owner token、URL、用户 ID 或业务参数。
## 生产边界
- 分布式锁不是数据库唯一约束、事务或幂等设计的替代品。
- Redis 单实例锁适合短时互斥;强一致协调场景优先考虑 etcd lease/txn 或数据库事务。
- 第一版不实现 Redlock,也不默认自动续租。
- 若锁保护外部资源写入,建议业务额外设计 fencing token 或版本号检查。
- Redis down、breaker open、timeout 都应作为明确错误处理,不要静默放行业务临界区。
## 验证
默认测试不依赖真实 Redis:
```bash
cargo test -p rs-zero --features cache-redis --test redis_lock
```
真实 Redis 验证走 opt-in:
```bash
scripts/external-integration.sh redis-lock
```