转自:https://www.cnblogs.com/seeseabky/p/10374133.html
官网SET命令地址:SET命令官网地址
Set key
to hold the string value
.
设置key
保存字符串value
If key
already holds a value, it is overwritten, regardless of its type.
如果key
已经存贮一个值,那么值将被覆盖,忽略之前的类型。
Any previous time to live associated with the key is discarded on successful SET
operation.
之前的与之关联的存活时间,在set成功之后,将被丢弃。
Options
选项
Starting with Redis 2.6.12 SET
supports a set of options that modify its
自Redis 2.6.12 版本起 SET
命令支持一组修订的选项。
behavior:
行为
EX
seconds -- Set the specified expire time, in seconds.EX
秒
--设置指定的过期时间以秒为单位。PX
milliseconds -- Set the specified expire time, in milliseconds.PX
毫秒 -- 设置指定的过期时间以毫秒为单位。NX
-- Only set the key if it does not already exist.NX
-- 只有key不存在值时设置XX
-- Only set the key if it already exist.XX
-- 只有key存在的时候设置
Note: Since the SET
command options can replace SETNX
, SETEX
, PSETEX
, it is possible that in future versions of Redis these three commands will be deprecated and finally removed.
注解:自此SET
命令 选项能够取代SETNX
, SETEX
, PSETEX
,在将来的Redis版本这三个命令会被弃用甚至被删除。
@return
@simple-string-reply: OK
if SET
was executed correctly.
@simple-string-reply: 如果 SET
被正确执行,返回 OK
。
@nil-reply: a Null Bulk Reply is returned if the SET
operation was not performed because the user specified the NX
or XX
option but the condition was not met.
@nil-reply: 在条件不符合时,返回null
@examples
<span style="color:#000000"><code><span style="color:#0000ff">SET</span> mykey <span style="color:#a31515">"Hello"</span>
<span style="color:#0000ff">GET</span> mykey</code></span>
Patterns
模式
Note: The following pattern is discouraged in favor of the Redlock algorithm which is only a bit more complex to implement, but offers better guarantees and is fault tolerant.
Note: 下面的模式是不建议的,更好的方法是使用the Redlock algorithm,虽然这种方式实现起来有点复杂,但是提供了更好的保证,以及更好的容错。
The command SET resource-name anystring NX EX max-lock-time
is a simple way to implement a locking system with Redis.
通过命令SET 键 值 NX EX 最大锁定时间
是一种简单的方式,实现redis锁
A client can acquire the lock if the above command returns OK
(or retry after some time if the command returns Nil), and remove the lock just using DEL
.
一个客户端能获得锁如果上面的命令返回OK
(或者尝试多次后这个命令返回null),且用 DEL
命令去删除锁
The lock will be auto-released after the expire time is reached.
锁会在达到过期时间的时自动释放。
It is possible to make this system more robust modifying the unlock schema as follows:
尽可能的让系统更加健壮,修改解锁模式如下:
-
Instead of setting a fixed string, set a non-guessable large random string, called token.
-
不是设置一个固定的字符串,而是用一个随机的字符串,叫做token
-
Instead of releasing the lock with
DEL
, send a script that only removes the key if the value matches. -
不是用
DEL
命令释放锁,而是执行一个脚本,如果值匹配的时候,把key删除。
This avoids that a client will try to release the lock after the expire time deleting the key created by another client that acquired the lock later.
这是避免一个客户端在设置的锁过期之后尝试释放锁,删除另外一个客户端设置的锁。
An example of unlock script would be similar to the following:
一个解锁脚本的示例大致如下:
if redis.call("get",KEYS[1]) == ARGV[1]
then
return redis.call("del",KEYS[1])
else
return 0
end
The script should be called with EVAL ...script... 1 resource-name token-value
这个脚本应该通过EVAL ...script... 1 key名字 token的值
调用
代码实现
通过set命令和Redisson实现分布式锁
官网提供了两种方式实现分布式锁
-
SET,DEL 组合命令 REDIS官网相关命令
-
通过使用Redisson Redisson的GITHUB地址
相关jar包
可以直接下载jar包
jedis
https://mvnrepository.com/artifact/redis.clients/jedis/2.9.0
redisson
https://mvnrepository.com/artifact/org.redisson/redisson/2.15.2
也可以使用maven
<span style="color:#000000"><code> <span style="color:green"><!--redis jedis --></span>
<span style="color:#0000ff"><<span style="color:#0000ff">dependency</span>></span>
<span style="color:#0000ff"><<span style="color:#0000ff">groupId</span>></span>redis.clients<span style="color:#0000ff"></<span style="color:#0000ff">groupId</span>></span>
<span style="color:#0000ff"><<span style="color:#0000ff">artifactId</span>></span>jedis<span style="color:#0000ff"></<span style="color:#0000ff">artifactId</span>></span>
<span style="color:#0000ff"><<span style="color:#0000ff">version</span>></span>2.9.0<span style="color:#0000ff"></<span style="color:#0000ff">version</span>></span>
<span style="color:#0000ff"></<span style="color:#0000ff">dependency</span>></span>
<span style="color:green"><!-- redis redisson--></span>
<span style="color:#0000ff"><<span style="color:#0000ff">dependency</span>></span>
<span style="color:#0000ff"><<span style="color:#0000ff">groupId</span>></span>org.redisson<span style="color:#0000ff"></<span style="color:#0000ff">groupId</span>></span>
<span style="color:#0000ff"><<span style="color:#0000ff">artifactId</span>></span>redisson<span style="color:#0000ff"></<span style="color:#0000ff">artifactId</span>></span>
<span style="color:#0000ff"><<span style="color:#0000ff">version</span>></span>2.15.2<span style="color:#0000ff"></<span style="color:#0000ff">version</span>></span>
<span style="color:#0000ff"></<span style="color:#0000ff">dependency</span>></span> </code></span>
java代码
<span style="color:#000000"><code><span style="color:#0000ff">package</span> com.util;
<span style="color:#0000ff">import</span> java.util.UUID;
<span style="color:#0000ff">import</span> java.util.concurrent.atomic.AtomicLong;
<span style="color:#0000ff">import</span> org.redisson.Redisson;
<span style="color:#0000ff">import</span> org.redisson.api.RLock;
<span style="color:#0000ff">import</span> org.redisson.api.RedissonClient;
<span style="color:#0000ff">import</span> org.redisson.config.Config;
<span style="color:#0000ff">import</span> redis.clients.jedis.Jedis;
<span style="color:#0000ff">import</span> redis.clients.jedis.JedisPool;
<span style="color:#0000ff">public</span> <span style="color:#0000ff">class</span> <span style="color:#a31515">RedisUtil</span> <span style="color:#0000ff">implements</span> <span style="color:#a31515">RedisBasic</span> {
<span style="color:#0000ff">private</span> <span style="color:#0000ff">static</span> <span style="color:#0000ff">final</span> AtomicLong ThreadRedisSimpleCount = <span style="color:#0000ff">new</span> AtomicLong();
<span style="color:#0000ff">private</span> <span style="color:#0000ff">static</span> <span style="color:#0000ff">final</span> AtomicLong ThreadRedisRedissonCount = <span style="color:#0000ff">new</span> AtomicLong();
<span style="color:#0000ff">public</span> <span style="color:#0000ff">static</span> <span style="color:#0000ff">void</span> <span style="color:#a31515">main</span>(String[] args) {
<span style="color:#0000ff">final</span> RedisUtil redisUtil = <span style="color:#0000ff">new</span> RedisUtil();
<span style="color:green">// SET命令方式实现lock</span>
<span style="color:#0000ff">for</span> (<span style="color:#0000ff">int</span> i = 0; i < 10; i++) {
<span style="color:#0000ff">new</span> Thread(<span style="color:#a31515">"【简单命令方式】"</span> + ThreadRedisSimpleCount.incrementAndGet()) {
<span style="color:#2b91af">@Override</span>
<span style="color:#0000ff">public</span> <span style="color:#0000ff">void</span> <span style="color:#a31515">run</span>() {
String token = UUID.randomUUID().toString();
<span style="color:#0000ff">if</span> (<span style="color:#a31515">"OK"</span>.equals(redisUtil.set(<span style="color:#a31515">"REDIS_SIMPLE_LOCK"</span>, token, RedisBasic.RedisType.JEDIS_SINGALE))) {
System.out.println(<span style="color:#a31515">"已获得锁*****************"</span> + Thread.currentThread().getName());
redisUtil.delKey(<span style="color:#a31515">"REDIS_SIMPLE_LOCK"</span>, token, RedisBasic.RedisType.JEDIS_SINGALE);
System.out.println(<span style="color:#a31515">"已解除锁*****************"</span> + Thread.currentThread().getName());
} <span style="color:#0000ff">else</span> {
System.out.println(<span style="color:#a31515">"未获得锁*****************"</span> + Thread.currentThread().getName());
}
}
}.start();
}
<span style="color:green">// REDISSON方式实现lock</span>
<span style="color:green">// REDISSON的解锁只能解锁当前线程的锁</span>
<span style="color:#0000ff">for</span> (<span style="color:#0000ff">int</span> i = 0; i < 10; i++) {
<span style="color:#0000ff">new</span> Thread(<span style="color:#a31515">"【REDISSON方式】"</span> + ThreadRedisRedissonCount.incrementAndGet()) {
<span style="color:#2b91af">@Override</span>
<span style="color:#0000ff">public</span> <span style="color:#0000ff">void</span> <span style="color:#a31515">run</span>() {
RLock lock = redisUtil.getLock(<span style="color:#a31515">"REDIS_REDISSON_LOCK"</span>);
<span style="color:#0000ff">if</span> (redisUtil.lock(lock, 0, 30)) {
System.out.println(<span style="color:#a31515">"已获得锁*****************"</span> + Thread.currentThread().getName());
redisUtil.unlock(lock);
System.out.println(<span style="color:#a31515">"已解除锁*****************"</span> + Thread.currentThread().getName());
} <span style="color:#0000ff">else</span> {
System.out.println(<span style="color:#a31515">"未获得锁*****************"</span> + Thread.currentThread().getName());
}
}
}.start();
}
}
<span style="color:#2b91af">@Override</span>
<span style="color:#0000ff">public</span> Jedis <span style="color:#a31515">getJedisSingle</span>() {
<span style="color:#0000ff">return</span> <span style="color:#0000ff">new</span> Jedis(<span style="color:#a31515">"127.0.0.1"</span>, 6378);
}
<span style="color:#2b91af">@Override</span>
<span style="color:#0000ff">public</span> JedisPool <span style="color:#a31515">getJedisPool</span>() {
<span style="color:#0000ff">return</span> <span style="color:#0000ff">new</span> JedisPool(<span style="color:#a31515">"127.0.0.1"</span>, 6378);
}
<span style="color:#2b91af">@Override</span>
<span style="color:#0000ff">public</span> RedissonClient <span style="color:#a31515">getRedisson</span>() {
Config config = <span style="color:#0000ff">new</span> Config();
config.useSingleServer().setAddress(<span style="color:#a31515">"redis://127.0.0.1:6378"</span>);
<span style="color:#0000ff">return</span> Redisson.create(config);
}
}</code></span>
执行main方法结果
已获得锁*****************【简单命令方式】6
未获得锁*****************【简单命令方式】7
未获得锁*****************【简单命令方式】5
未获得锁*****************【简单命令方式】3
未获得锁*****************【简单命令方式】9
未获得锁*****************【简单命令方式】1
未获得锁*****************【简单命令方式】10
未获得锁*****************【简单命令方式】2
未获得锁*****************【简单命令方式】8
未获得锁*****************【简单命令方式】4
已解除锁*****************【简单命令方式】6
未获得锁*****************【REDISSON方式】1
未获得锁*****************【REDISSON方式】5
未获得锁*****************【REDISSON方式】3
未获得锁*****************【REDISSON方式】7
未获得锁*****************【REDISSON方式】4
已获得锁*****************【REDISSON方式】6
未获得锁*****************【REDISSON方式】10
未获得锁*****************【REDISSON方式】8
未获得锁*****************【REDISSON方式】2
未获得锁*****************【REDISSON方式】9
已解除锁*****************【REDISSON方式】6
相关lua脚本
组合命令del的脚本实现
<span style="color:#000000"><code>
<span style="color:#0000ff">public</span> <span style="color:#0000ff">default</span> <span style="color:#0000ff">void</span> <span style="color:#a31515">delKey</span>(String key, String value, RedisType redisType) {
<span style="color:#0000ff">if</span> (redisType == RedisType.JEDIS_SINGALE) {
List<String> keys = <span style="color:#0000ff">new</span> ArrayList<>();
keys.add(key);
List<String> args = <span style="color:#0000ff">new</span> ArrayList<>();
args.add(value);
getJedisSingle()
.eval(<span style="color:#a31515">"if redis.call(\"get\",KEYS[1]) == ARGV[1] then return redis.call(\"del\",KEYS[1]) else return 0 end"</span>,
keys, args);
}
}</code></span>
Redisson获取锁的脚本源码
<span style="color:#000000"><code> <T> RFuture<T> <span style="color:#a31515">tryLockInnerAsync</span>(<span style="color:#0000ff">long</span> leaseTime, TimeUnit unit, <span style="color:#0000ff">long</span> threadId, RedisStrictCommand<T> command) {
internalLockLeaseTime = unit.toMillis(leaseTime);
<span style="color:#0000ff">return</span> commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, command,
<span style="color:#a31515">"if (redis.call('exists', KEYS[1]) == 0) then "</span> +
<span style="color:#a31515">"redis.call('hset', KEYS[1], ARGV[2], 1); "</span> +
<span style="color:#a31515">"redis.call('pexpire', KEYS[1], ARGV[1]); "</span> +
<span style="color:#a31515">"return nil; "</span> +
<span style="color:#a31515">"end; "</span> +
<span style="color:#a31515">"if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then "</span> +
<span style="color:#a31515">"redis.call('hincrby', KEYS[1], ARGV[2], 1); "</span> +
<span style="color:#a31515">"redis.call('pexpire', KEYS[1], ARGV[1]); "</span> +
<span style="color:#a31515">"return nil; "</span> +
<span style="color:#a31515">"end; "</span> +
<span style="color:#a31515">"return redis.call('pttl', KEYS[1]);"</span>,
Collections.<Object>singletonList(getName()), internalLockLeaseTime, getLockName(threadId));
}</code></span>
Redisson解锁的脚本源码
<span style="color:#000000"><code>
<span style="color:#0000ff">protected</span> RFuture<Boolean> <span style="color:#a31515">unlockInnerAsync</span>(<span style="color:#0000ff">long</span> threadId) {
<span style="color:#0000ff">return</span> commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
<span style="color:#a31515">"if (redis.call('exists', KEYS[1]) == 0) then "</span> +
<span style="color:#a31515">"redis.call('publish', KEYS[2], ARGV[1]); "</span> +
<span style="color:#a31515">"return 1; "</span> +
<span style="color:#a31515">"end;"</span> +
<span style="color:#a31515">"if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then "</span> +
<span style="color:#a31515">"return nil;"</span> +
<span style="color:#a31515">"end; "</span> +
<span style="color:#a31515">"local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1); "</span> +
<span style="color:#a31515">"if (counter > 0) then "</span> +
<span style="color:#a31515">"redis.call('pexpire', KEYS[1], ARGV[2]); "</span> +
<span style="color:#a31515">"return 0; "</span> +
<span style="color:#a31515">"else "</span> +
<span style="color:#a31515">"redis.call('del', KEYS[1]); "</span> +
<span style="color:#a31515">"redis.call('publish', KEYS[2], ARGV[1]); "</span> +
<span style="color:#a31515">"return 1; "</span>+
<span style="color:#a31515">"end; "</span> +
<span style="color:#a31515">"return nil;"</span>,
Arrays.<Object>asList(getName(), getChannelName()), LockPubSub.unlockMessage, internalLockLeaseTime, getLockName(threadId));
}</code></span>