Redis高级特性及应用

一、Redis慢查询

1.1 Redis命令流程

在这里插入图片描述

1.2 慢查询配置:

可以通过以下命令配置慢查询时间阈值(慢查询值得是上图中执行命令的时间,不包含其他时间)

config set slowlog-log-slower-than 10000   //单位微秒
config rewrite  //写入配置文件
  • 时间阈值 ==0 所有查询时间都记录
  • 时间阈值 < 0 都不记录
  • 时间阈值 ==N 所有查询时间超过N微秒的都记录

多慢算慢查询?
一般配置到1000就可以
慢查询记录在哪里?
慢查询保存在内存的一个链表中
slowlog-max-len 128 //这个配置项是慢查询链表的长度,默认128

slowlog get 3 //查看最近3条慢查询记录
slowlog reset //清空慢查询

执行两条命令,并查看慢查询记录

127.0.0.1:6379> set shibng333 333
OK
127.0.0.1:6379> set shibing444 444
OK
127.0.0.1:6379> slowlog get 3
1) 1) (integer) 7           //命令序号
   2) (integer) 1726739963  //执行的时间戳
   3) (integer) 72          //执行耗时,单位微秒
   4) 1) "set"              //命令
      2) "shibing444"
      3) "444"
   5) "127.0.0.1:38730"     //节点地址
   6) ""

二、Redis的PipeLine

Pipeline相当于一次性发送多条执行给Redis执行,相比普通命令执行方式减少了往返的网络时间。
Pipeline命令多长合适呢?
pipeline 内存输入输出缓冲区大小4k-8k。单个TCP报文大小1460B,超过这个大小会拆包,有额外的网络开销,所以尽量不要超过这个大小
在这里插入图片描述

实战

pipeline命令的使用

public class RedisPipeline {

    @Autowired
    private JedisPool jedisPool;

    public List<Object> plGet(List<String> keys) {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            //pipe是将所有的命令组装成pipeline
            Pipeline pipelined = jedis.pipelined();
            pipelined.multi();//开启事务
            //。。。。。等等命令
            pipelined.exec();//提交事务
            for(String key:keys){
                pipelined.get(key);//不是仅仅是get方法,set方法还要很多很多方法pipeline都提供了支持
            }
            return pipelined.syncAndReturnAll();//这里只会向redis发送一次
        } catch (Exception e) {
            throw new RuntimeException("执行Pipeline获取失败!",e);
        } finally {
            jedis.close();
        }
    }

    public void plSet(List<String> keys,List<String> values) {
        if(keys.size()!=values.size()) {
            throw new RuntimeException("key和value个数不匹配!");
        }
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            Pipeline pipelined = jedis.pipelined();
            for(int i=0;i<keys.size();i++){
                pipelined.set(keys.get(i),values.get(i));
            }
            pipelined.sync();
        } catch (Exception e) {
            throw new RuntimeException("执行Pipeline设值失败!",e);
        } finally {
            jedis.close();
        }
    }
}

三、redis事物

redis事物有原子性,一致性,隔离性,特殊情况下支持持久性。不支持回滚。
但是Redis事务对回滚支持有问题,对于命令错误,可以支持回滚。但是命令没错,其他错误不支持回滚机制。所以不建议使用Redis事务。
Redis事务multi命令开始,exec提交事务。
如下 sadd命令的key和上一个重复了,但是上一个操作没有回滚,仍然是成功的:

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set shi1900 apple
QUEUED
127.0.0.1:6379> sadd shi1900 grape
QUEUED
127.0.0.1:6379> exec
1) OK
2) (error) WRONGTYPE Operation against a key holding the wrong kind of value
127.0.0.1:6379> get shi1900
"apple"
127.0.0.1:6379> 

四、Redis Watch机制(乐观锁机制)

Watch实现了一个乐观锁的功能,如果一个key被watch,那么key被修改的时候,会将key当前的值和watch之前的值进行比较,相同就可以修改成功。否则会失败。
例:客户端1对key-watched进行了watch操作,然后开启事务执行命令给key-watched设置一个新值999,客户端2这时也给key-watched设置新值555成功,然后客户端1提交事务失败。

//客户端1:
127.0.0.1:6379> set key-watched 111
OK
127.0.0.1:6379> watch key-watched
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set key-watched 999  //执行该条命令后,在客户端2执行命令 set key-watched 555
QUEUED
127.0.0.1:6379> exec              //提交失败
(nil)
127.0.0.1:6379> get key-watched   //值已经被改成555
"555"
127.0.0.1:6379>

//客户端2:
127.0.0.1:6379> set key-watched 555
OK 

五 Redis和Lua脚本

Redis可以支持使用Lua脚本执行命令,一个Lua脚本一次性执行完成,是个原子操作。
使用LUA脚本的好处

  • 减少网络开销,在Lua脚本中可以把多个命令放在同一个脚本中运行
  • 原子操作,Redis会将整个脚本作为一个整体执行,中间不会被其他命令插入。(Redis执行命令是单线程)
  • 复用性,客户端发送的脚本会存储在Redis中,这意味着其他客户端可以复用这一脚本来完成同样的逻辑
    在这里插入图片描述

六、Lua脚本限流实战

在Redis维护一个计数器count,每次有请求进来,计数器count+1,当计数器的值超过限流值之后,请求会被拒绝,计数器设置一个超时时间,比如一分钟。计数器过期之后又可以接收请求。
在这里插入图片描述
代码实现:

@RequestMapping("/order")
public String killProduct(@RequestParam(required = true) String name) throws Exception{
    //rateLimiter.tryAcquire(1); //调用
    if(isAcquire.acquire("iphone",10,60)){//60秒只能进行10次
        System.out.println("业务成功!");
        return "恭喜("+name+"),抢到iphone!";
    }else{
        System.out.println("-----------业务被限流");
        return "对不起,你被限流了!";
    }

}

//判断限流方法---类似于RateLimiter
public boolean acquire(String limitKey,int limit,int expire) throws  Exception{
    //连接Redis
    Jedis jedis =  new Jedis("127.0.0.1",6379);
    getRedisScript =new  DefaultRedisScript<>();
    getRedisScript.setResultType(Long.class);//脚本执行返回值 long
    getRedisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("rateLimiter.lua")));
    Long result = (Long)jedis.eval(getRedisScript.getScriptAsString(),
            1,limitKey,String.valueOf(limit),String.valueOf(expire));
    if(result ==0){
        return false;
    }
    return true;
}

限流计数的Lua脚本:
afterval做流量计数,limit是流量限制次数,超过流量就会禁止

--java端送入三个参数(1个key,2个param  )string
--limitKey(redi中key的值)
local key =KEYS[1];
--limit(次数)
local times = ARGV[1];
--expire(秒S)
local expire = ARGV[2];
--对key-value中的 value +1的操作  返回一个结果
local afterval=  redis.call('incr',key);
if afterval ==1 then --第一次
    redis.call('expire',key,tonumber(expire) )  --失效时间(1S)  TLL 1S
    return 1; --第一次不会进行限制
end
--不是第一次,进行判断
if afterval > tonumber(times) then
    --限制了
    return 0;
end
return 1;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值