记 redis 使用

项目需要远程连接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命令加上选项已经可以完全取代SETNXSETEXPSETEX的功能,所以在将来的版本中,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));

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值