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()

Redis进阶
https://blog.yjll.blog/post/3a12d43e.html
作者
简斋
发布于
2019年3月28日
许可协议