项目需要远程连接redis进行调试,记录一下。
windows端的redis下载链接如下,官网只有linux版本.....
https://github.com/MicrosoftArchive/redis/tags
1.下载对应版本的redis后,修改redis目录中的redis.windows.conf,将配置文件中的ip和port修改远程redis所对应的。
2.cmd命令窗口进入到redis目录下,执行redis-cli -h 172.17.6.100 -p 6800(这里的ip和port填写上面配置的)即可访问到远程redis进行操作了。
----------------------------------------
在项目测试过程中,生成了大量的废key,需要进行批量删除,可分为两种实现方式。
1.在linux端执行命令删除,通过管道将keys prefix*的结果传给del进行删除。
redis-cli -h 172.17.8.100 -p 6800 keys "zhutiancheng*" | xargs del
2.Java代码进行删除,由于使用的是windows,windows cmd的指令不知道咋写....流下了没有技术的泪水.....
这里使用的公司封装好的redisClient,使用Jedis应该也是类似的操作,代码如下,关键代码在init方法中,纯粹记录,没有参考性。
package com.lizhi.ztc.util;
import com.netflix.governator.annotations.AutoBindSingleton;
import com.netflix.governator.annotations.WarmUp;
import fm.lizhi.common.datastore.redis.client.RedisClient;
import javax.inject.Inject;
import java.util.Set;
@AutoBindSingleton
public class ClearTestData {
@Inject
RedisClient redisClient;
@WarmUp
public void init(){
Set<String> keys = redisClient.keys("zhutiancheng*");
for(String key:keys){
redisClient.del(key);
}
}
}
不得不提一下,公司使用的轻量级IOC框架guice,和Spring IOC很像,@AutoBindSingleton相当于@Component吧,Spring的ioc容器分父子容器,这个似乎是不分的。@Inject作用和@Autowired类似,@WarmUp和@PostConstruct类似,可以在Bean的属性初始化之后进行一个预热的操作。
------------------------------------------------
想跑一下上面的代码,结果报错了,公司的redis禁用了keys *操作,我人晕了,报错如下
com.netflix.governator.lifecycle.warmup.WarmUpException:
Key:com.lizhi.ztc.util.ClearTestData - Object: com.lizhi.ztc.util.ClearTestData: Unsupported redis command 'keys'.
想起面试时的一个问题,面试官问redis的keys * 存在什么隐患,当然我当时是不会的.....答案如下,
keys * 操作可能会耗时较长,而redis是单线程的,这样大量的线程请求会阻塞堆积,最终导致宕机崩溃。
贴个博客,可以看看详细解释:https://blog.csdn.net/u014756578/article/details/91951793
-----------------------------------------------2020.6.15-------------------------------------------
今天写redis锁写错了,好尴尬啊.....
网上很多博客讲的有点不明不白,用自己的理解来记录一下吧。
加锁使用的是 set 命令,注意,只有在redis 2.6.12 以后才可用做锁,
redis 2.6.12以后,redis为set命令增加了一系列选项,专门用来实现锁:
EX
seconds – 设置键key的过期时间,单位时秒PX
milliseconds – 设置键key的过期时间,单位时毫秒NX
– 只有键key不存在的时候才会设置key的值XX
– 只有键key存在的时候才会设置key的值
由于SET
命令加上选项已经可以完全取代SETNX, SETEX, PSETEX的功能,所以在将来的版本中,redis可能会不推荐使用并且最终抛弃这几个命令。
下面是对应的Java加锁代码,使用的是公司封装的RedisClient。加锁的关键在于set函数的第一个参数key。第二个参数是value,
ex用于设置key的过期时间,客户端加锁之后,如果没有主动释放,会在过期时间之后自动释放。nx表示只有键key不存在的时候才会设置key的值。
//进行加锁
SetParams setParams = new SetParams();
setParams.ex(60).nx();
String requestId = UUID.randomUUID().toString();//随机的值
String lock = null;
while (lock==null){
//String set(String var1, String var2, SetParams var3);
lock = redisClient.set(RedisUtil.updateLockKey, requestId, setParams);
if(lock.equals("OK")) break;//获取锁成功
seleep(10, 50000);
}
当加锁成功时,set函数会返回字符串"ok",加锁不成功时,返回null,线程睡眠一段时间再次请求。
解锁代码如下,eval也是redis 2.6.0以后版本提供的命令,使用内置的 Lua 解释器,可以对 Lua 脚本进行求值。
它可以保证被解释的lua代码原子性执行。下面的unLockScript就是根据传入的参数key(KEYS[1])去获取对应的value,去和加锁时生成的requestId对比,相等就可以删除,实现了锁的释放。Collections.singletonList()这个方法主要用于只有一个元素的优化,减少内存分配,无需分配额外的内存,可以从SingletonList内部类看得出来,由于只有一个element,因此可以做到内存分配最小化,不可以进行add操作。
//安全的解锁
String unLockScript="if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";//解锁lua脚本
redisClient.eval(RedisUtil.unLockScript,
Collections.singletonList(RedisUtil.updateLockKey),
Collections.singletonList(requestId));
这样实现锁的优点是什么?主要是保证了解锁的安全性,有两个点。
1.使用eval命令执行lua脚本,获取key的值和删除key这两个操作是原子性的,保证了线程安全。
2.value不适用固定字符串,而是使用随机大字符串UUID。
上述优化方法会避免下述场景:a客户端获得的锁(键key)已经由于过期时间到了被redis服务器删除,但是这个时候a客户端还去执行DEL命令。而b客户端已经在a设置的过期时间之后重新获取了这个同样key的锁,那么a执行DEL就会释放了b客户端加好的锁。
完整示例代码如下:
String unLockScript="if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";//解锁lua脚本
//进行加锁
SetParams setParams = new SetParams();
setParams.ex(60).nx();
String requestId = UUID.randomUUID().toString();//随机的值
String lock = null;
while (lock==null){
lock = redisClient.set(RedisUtil.updateLockKey, requestId, setParams);
if(lock.equals("OK")) break;//获取锁成功
seleep(10, 50000);
}
//加锁后的操作
.....
//安全的解锁
redisClient.eval(RedisUtil.unLockScript,
Collections.singletonList(RedisUtil.updateLockKey),
Collections.singletonList(requestId));