Redis原生GET/SET方法
- SET()命令:有两个参数 set(key, value),将字符串值 value 关联到 key ,如果 key 已经持有其他值, SET 就覆写旧值,无视类型
- SETNX():含义就是SET if Not Exists,有两个参数 setnx(key, value),该方法是原子的,如果key不存在,则设置当前key成功,返回1;如果当前key已经存在,则设置当前key失败,返回0。
- SETEX()命令:有三个参数 set(key, time, value),在设置的时候给key设置一个过期时间time,时间到了key自动失效。
- GET()命令:获取key的值,如果存在,则返回;如果不存在,则返回nil;
从 Redis 2.6.12 版本开始, SET 命令的行为可以通过添加一系列参数来修改:
EX second :设置键的过期时间为 second 秒。 SET key value EX second 效果等同于 SETEX key second value 。
PX millisecond :设置键的过期时间为 millisecond 毫秒。 SET key value PX millisecond 效果等同于 PSETEX key millisecond value 。
NX :只在键不存在时,才对键进行设置操作。 SET key value NX 效果等同于 SETNX key value 。
XX :只在键已经存在时,才对键进行设置操作。
因为 SET 命令可以通过参数来实现和 SETNX 、 SETEX 和 PSETEX三个命令的效果,所以将来的 Redis
版本可能会废弃并最终移除 SETNX 、 SETEX 和 PSETEX 这三个命令。
Spring框架中RedisTemplate.opsForValue()方法的使用。
方法 | 解释 |
---|---|
set(K key, V value) | 新增一个字符串类型的值,key是键,value是值。 |
setIfAbsent(K key, V value) | 如果键不存在则新增,存在则不改变已经有的值。相当于SETNX |
set(K key, V value, long timeout, TimeUnit unit) | 添加数据,并设置过期时间。 |
setIfAbsent(K key, V value, long timeout, TimeUnit unit); | 当key不存在的时候添加数据,并设置过期时间 |
set(K key, V value, long offset) | 覆盖从指定位置开始的值。 |
get(Object key) | 获取key键对应的值。 |
getAndSet(K key, V value) | 获取原来key键对应的旧值并重新赋新值。 |
increment(K key, double delta) | 以增量的方式将double值存储在变量中。 |
increment(K key, long delta) | 以增量的方式将long值存储在变量中。 |
decrement(K key, long delta); | 自减运算,并设置步长 (默认是1) |
size(K key) | 获取指定字符串的长度 |
multiSet(Map<? extends K,? extends V> map) | 设置map集合到redis。 |
multiGet(Collection keys) | 批量获取数据 |
multiSetIfAbsent(Map<? extends K,? extends V> map) | 如果对应的map集合名称不存在,则添加;如果存在则不做修改 |
append(K key, String value) | 在原有的值基础上新增字符串到末尾。 |
get(K key, long start, long end) | 截取key键对应值得字符串,从开始下标位置开始到结束下标的位置(包含结束下标)的字符串 |
setBit(K key, long offset, boolean value) | key键对应的值value对应的ascii码,在offset的位置(从左向右数)变为value |
getBit(K key, long offset) | 判断指定的位置ASCII码的bit位是否为1 |
基于Redis的分布式锁
之前在别的项目里看到的redis分布式锁通用代码:
/** 上锁 */
public boolean lock(String key, String value) {
//如果key值不存在,返回 true,并设置 value
if (redisClient.opsForValue().setIfAbsent(key, value)) {
return true;
}
String currentValue = redisClient.opsForValue().get(key);
//判断锁是否已过期
if (!StringUtils.isEmpty(currentValue)&& Long.parseLong(currentValue) < System.currentTimeMillis()) {
//获得之前的value值,同时设置新的value。这个地方可能几个线程同时过来,但是redis本身天然是单线程的,所以getAndSet方法还是会安全执行。
String oldValue = redisClient.opsForValue().getAndSet(key, value);
//判断currentValue是否和oldVal值相等,如果是同一个值,该线程就set新的value,后面的线程就取不到锁了
if (!StringUtils.isEmpty(oldValue) && oldValue.equals(currentValue)) {
return true;
}
}
return false;
}
/** 解锁 */
public void unlock(String key, String value) {
try {
String currentValue = redisClient.opsForValue().get(key);
if (!StringUtils.isEmpty(currentValue) && currentValue.equals(value)) {
redisClient.opsForValue().getOperations().delete(key);
}
} catch (Exception e) {
log.error("RedisLock unlock error ", e);
}
}
但是在高并发访问情况下,会出现一个线程获取锁后被其他线程修改掉超时时间。并且解锁时也会出现一些问题;
redis官方给出了正确的加锁姿势!
要在Redis 中实现分布式锁,可以用下面的方法:
SET resource-name anystring NX EX lock-time
java中可以使用jedis set 命令加锁,即
jedis.set(keys,args,"NX","PX",3000)
该命令仅在密钥尚不存在时才设置密钥(NX选项),到期时间为3000毫秒(PX选项,EX选项单位为秒)
客户端执行以上的命令:
- 如果服务器返回 OK ,那么这个客户端获得锁。
- 如果服务器返回 NIL ,那么客户端获取锁失败,可以在稍后再重试。
- 设置的过期时间到达之后,锁将自动释放。
可以通过以下修改,让这个锁的实现更健壮:
- 不使用固定的字符串作为键的值,而是设置一个不可猜测(non-guessable)的长随机字符串,作为口令串(token)。
- 不使用 DEL 命令来释放锁,而是发送一个 Lua 脚本,这个脚本只在客户端传入的值和键的口令串相匹配时,才对键进行删除。
这两个改动可以防止持有过期锁的客户端误删现有锁的情况出现。
以下是一个简单的解锁脚本示例:
if redis.call("get",KEYS[1]) == ARGV[1]
then
return redis.call("del",KEYS[1])
else
return 0
end
这个脚本可以通过 EVAL …script… 1 resource-name token-value 命令来调用。
如果我的文章对您有帮助的话,请点个赞吧~