Redis进阶
日常开发中Redis是常用的缓存工具,Redis其实不仅仅能做缓存,Redis支持string(字符串)、list(列表)、hash(字典)、set(集合) 和zset(有序集合)这五种数据结构,本文介绍更多Redis的使用场景。
分布式锁
SET key value [EX seconds] [PX milliseconds] [NX|XX]
将字符串值 value 关联到 key 。
如果 key 已经持有其他值, SET 就覆写旧值,无视类型。
对于某个原本带有生存时间(TTL)的键来说, 当 SET 命令成功在这个键上执行时, 这个键原有的 TTL 将被清除。
可选参数
从 Redis 2.6.12 版本开始, SET 命令的行为可以通过一系列参数来修改:
EX second :设置键的过期时间为 second 秒。
PX millisecond :设置键的过期时间为 millisecond 毫秒。
NX :只在键不存在时,才对键进行设置操作。 SET key value NX 效果等同于 SETNX key value 。
XX :只在键已经存在时,才对键进行设置操作。
因为 SET 命令可以通过参数来实现和 SETNX 、 SETEX 和 PSETEX 三个命令的效果,所以将来的 Redis 版本可能会废弃并最终移除 SETNX 、 SETEX 和 PSETEX 这三个命令。
使用RedisTemplate加锁
public boolean lock(String lockKey, String lockValue, int expire) {
// 加锁
String lock = stringRedisTemplate.execute((RedisCallback<String>) connection -> {
JedisCommands commands = (JedisCommands) connection.getNativeConnection();
return commands.set(lockKey, lockValue, "NX", "EX", expire);
});
return Objects.equals(LOCK_RESULT, lock);
}
使用RedisTemplate解锁
public void unlock(String lockKey){
// 解锁
stringRedisTemplate.delete(lockKey);
}
锁不具备拥有者标识,即任何客户端都可以解锁。
if redis.call("get", KEYS[1]) == ARGV[1]
then
return redis.call("del", KEYS)
else
return 0
end
public void unlock(String lockKey,String requestId){
List<String> keys = Collections.singletonList(lockKey);
// 采用lua脚本方式保证在多线程环境下也是原子操作
DefaultRedisScript<Long> script = new DefaultRedisScript<>();
script.setResultType(Long.class);
script.setScriptText(luaScriptText);
stringRedisTemplate.execute(script,keys,requestId);
}
消息队列
可使用list实现一个简单的消息队列
RPUSH key value [value ...]
将一个或多个值 value 插入到列表 key 的表尾(最右边)。
redis_conn.lpush(key,3)
LRANGE key start stop
返回列表 key 中指定区间内的元素,区间以偏移量 start 和 stop 指定。
redis_conn.lrange(key,0,-1)
BLPOP key [key ...] timeout
BLPOP 是列表的阻塞式(blocking)弹出原语。
while True:
# 阻塞
data = redis_conn.blpop(key)
print(data)
有序集合
有序集合实现排行榜功能
ZINCRBY key increment member
为有序集 key 的成员 member 的 score 值加上增量 increment 。
BoundZSetOperations<String, String> operations = stringRedisTemplate.boundZSetOps(INVITE_ACTIVITY_KEY);
operations.incrementScore(userId.toString(), 1);
ZREVRANGE key start stop [WITHSCORES]
返回有序集 key 中,指定区间内的成员。
bscore相同时会按照member字典顺序排序
// 取前十名
Set<ZSetOperations.TypedTuple<String>> typedTuples = operations.reverseRangeWithScores(0, 9);
// 遍历取用户Id和积分
for (ZSetOperations.TypedTuple<String> typedTuple : typedTuples) {
Integer inviterId = Integer.parseInt(typedTuple.getValue().trim());
// 邀请用户数量
Integer inviteCount = typedTuple.getScore().intValue();
}
ZSCORE key member
返回有序集 key 中,成员 member 的 score 值。
Double score = operations.score(userId.toString());
发布/订阅
事务
MULTI
标记一个事务块的开始。
事务块内的多条命令会按照先后顺序被放进一个队列当中,最后由 EXEC
命令原子性(atomic)地执行。
EXEC
执行所有事务块内的命令。
WATCH key [key ...]
监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。
减库存操作
# 监视一个key
pipe.watch('stock_count')
count = int(pipe.get('stock_count'))
# 有库存
if count > 0:
# 事务开始
pipe.multi()
# pipe.decr('stock_count')
pipe.set('stock_count', count - 1)
# 事务结束(可能会抛异常)
pipe.execute()