Redis第二讲

二、Redis02

2.1 发布和订阅

Redis 发布订阅 (pub/sub) 是一种消息通信模式:发送者 (pub) 发送消息,订阅者 (sub) 接收消息。

Redis 客户端可以订阅任意数量的频道。

 

发布订阅的实现

1、打开一个客户端订阅channel1
127.0.0.1:6379> subscribe channel1
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "channel1"
3) (integer) 1
​
2、打开另一个客户端,给channel1发布消息hello
127.0.0.1:6379> publish channel1 hello~
(integer) 1    #返回的是订阅者的数量
​
​
3、打开第一个客户端就能看到发布的消息
127.0.0.1:6379> subscribe channel1
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "channel1"
3) (integer) 1
1) "message"
2) "channel1"
3) "hello~"
​
也可以同时订阅多个频道,就像是订阅公众号一样

2.2 Redis事务

2.2.1 事务简介

可以一次执行多个命令,本质是一组命令的集合。一个事务中的 所有命令都会序列化,按顺序地串行化执行而不会被其它命令插入,不许加塞。

事务执行的是单独的隔离的一组操作

详细说明参考官网:https://redis.io/docs/manual/transactions/

2.2.2 Redis事务结构

MULTI、EXEC、DISCARD、WATCH。这四个指令构成了 redis 事务处理的基础。

 

注:
1.MULTI 用来组装一个事务;将命令存放到一个队列里面
2.EXEC 用来执行一个事务;//commit
3.DISCARD 用来取消一个事务;//rollback
4.WATCH 用来监视一些 key,一旦这些 key 在事务执行之前被改变,则取消事务的执行。

成功事务举例:

127.0.0.1:6379> multi          #开始组装事务
OK
127.0.0.1:6379> set height 140  #第一个事务进入队列
QUEUED
127.0.0.1:6379> sadd testsw sw1 sw2  #第二个事务进入队列
QUEUED
127.0.0.1:6379> set sw sw     #第三个事务进入队列
QUEUED
127.0.0.1:6379> exec   #执行事务
1) OK
2) (integer) 2
3) OK

2.2.3 Redis事务错误

1.调用 EXEC 之前的错误

“调用 EXEC 之前的错误”,有可能是由于语法有误导致的,也可能时由于内存不足导致的。只要出现某个命令无法成功写入缓冲队列的情况,redis 都会进行记录,在客户端调用 EXEC 时,redis 会拒绝执行这一事务。

 

127.0.0.1:6379> multi
OK
127.0.0.1:6379> test
(error) ERR unknown command `test`, with args beginning with: 
127.0.0.1:6379> ping
QUEUED
127.0.0.1:6379> exec
(error) EXECABORT Transaction discarded because of previous errors. #拒绝执行

2.调用exec之后的错误

对于“调用 EXEC 之后的错误”,redis 则采取了完全不同的策略,即 redis 不会理睬这些错误,而是继续向下执行事务中的其他命令。这是因为,对于应用层面的错误,并不是 redis 自身需要考虑和处理的问题,所以一个事务中如果某一条命令执行失败,并不会影响接下来的其他命令的执行

 

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set age 23
QUEUED
127.0.0.1:6379> sadd age 15 16 #age不是集合,所以这条命令在执行时会出错
QUEUED
127.0.0.1:6379> set age 29
QUEUED
127.0.0.1:6379> exec
1) OK   #第二条命令虽然执行错误,但是第三条命令会继续执行
2) (error) WRONGTYPE Operation against a key holding the wrong kind of value
3) OK

2.2.4 Redis事务冲突

需求:
去购物的时候使用同一张银行卡去付款
一个请求想给金额减8000
一个请求想给金额减5000
一个请求想给金额减1000

如下图所示:

 

①悲观锁解决方案

悲观锁(Pessimistic Lock), 顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。
传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。
例:12306抢票

如图所示:

 

② 乐观锁解决方案

最初版本号version=1,每修改一次数据就会自增0.1
查余额 10000  version:1
10000>8000 -8000  UPDATE uuuu SET MONTY-=8000 where VERSION=1   修改后版本后变为1.1
10000>5000 -5000  UPDATE uuuu SET MONTY-=5000 WHERE VERSION=1   版本号已经变化,所以修改失败  
2000>1000  -1000  UPDATE uuuu SET MONTY-=1000 WHERE VERSION=1.1 修改后版本号变化为1.2
​
乐观锁(Optimistic Lock), 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。
乐观锁适用于多读的应用类型,这样可以提高吞吐量。Redis就是利用这种check-and-set机制实现事务的。

如下图所示:

 

2.2.5 事务WATCH

“WATCH”可以帮我们实现类似于“乐观锁”的效果,即 CAS(check and set)。

WATCH 本身的作用是“监视 key 是否被改动过”,而且支持同时监视多个 key,只要还没真正触发事务,WATCH 都会尽职尽责的监视,一旦发现某个 key 被修改了,在执行 EXEC 时就会返回 nil,表示事务无法触发。

127.0.0.1:6379> set inter 3
OK
127.0.0.1:6379> watch inter
OK
127.0.0.1:6379> set inter 6
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set inter 5
QUEUED
127.0.0.1:6379> exec
(nil)

2.2.6 事务DISCARD

事务discard类似于mysql中Rollback,也就是回滚事务

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set test6 test
QUEUED
127.0.0.1:6379> discard   #进行事务回滚,前面的任务都清出任务队列
OK
127.0.0.1:6379> exec    #因为事务中没有一个任务,所以执行时报错误
(error) ERR EXEC without MULTI

2.3 java操作Redis

1、创建一个SpringBoot项目

2、添加redis的依赖

<!--Java操作redis-->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>4.2.3</version>
        </dependency>

3、简单连接测试并添加String数据

public class TestRedis {
    public static void main(String[] args) {
        // 填写连接redis的ip地址和端口号,如果不写端口号,默认是6379
        Jedis jedis = new Jedis("IP地址",6379);
        // 配置登录密码
        jedis.auth("密码");
        // 测试连接
        System.out.println(jedis.ping());
        //选择数据库2
        jedis.select(2);
        /**
         * 增加
         */
        jedis.set("testredis","test");
        /**
         * 查询
         */
        System.out.println("key值为testredis的value值为:" + jedis.get("testredis"));
    }
}

4、key操作

 @Test
  public  void testRedis() {
        //设置连接的服务器 端口号默认是6379
        // 服务器的默认值是localhost
        Jedis jedis = new Jedis("IP地址",6379);
        // redis的服务器的密码
        jedis.auth("密码"); //设置密码
        // 设置选中的数据库的下标
        jedis.select(15);
        // 设置键值对
        jedis.set("k1", "v1");
        jedis.set("k2", "v2");
        jedis.set("k3", "v3");
        //获取所有的key值
        Set<String> keys = jedis.keys("*");
        System.out.println(keys.size());
        for (String key : keys) {
            System.out.println(key);
        }
        // 判断key是否存在
        System.out.println(jedis.exists("k1"));
        //获取key的过期时间
        System.out.println(jedis.ttl("k1"));
        // 获取key对应的值
        System.out.println(jedis.get("k1"));
    }
  }
操作命令
jedis.flushDB清空数据
boolean jedis.exists(String key)判断某个键是否存在
jedis.set(String key , String value)新增键值对( key,value )
Set<String> jedis.keys("*")获取所有 key
jedis.del(String key)删除键为 key 的数据项
jedis.expire(String key, int i)设置键为 key 的过期时间为i秒
int jedis.ttl(String key)获取键为 key 数据项的剩余生存时间(秒)
jedis.persist(String key)移除键为 key 属性项的生存时间限制
jedistype(String key)查看键为 key 所对应 value 的数据类型

5、字符串(String)操作

操作命令
增加(或覆盖)数据项jedis.set(String key , String value)
不覆盖增加数据项(重复不插入 )jedis.setnx(String key , String value)
增加数据项并设置有效时间jedis.setex(String key , int t , String value)
删除键为 key 的数据项jedis.del(String key)
获取键为 key 对应的 valuejedis.get(String key)
在 key 对应 value 后面扩展字符串 sjedis.append(String key , String s)
增加多个键值对jedis.mset(String k1 , String v1 , String k2 , String v2 , ... )
获取多个 key 对应 valueString[] jedis.mget(String k1 , String k2 , ... )
删除多个 key 对应数据项jedis.del(new StringllString k1 , String k2 , ... .)
获取 key 对应 value 并更新 valueString jedis.getSet(String key , String value)
获取 key 对应 value第i到j字符String jedis.getrange(String key , int i , int j )

6、整数和浮点数操作

操作命令
增加(或覆盖) 数据项jedis.set(String key , String value)
获取键为 key 对应的 valuejedis.get(String key)
将 key 对应的 value 自加 1jedis.incr(String key)
将 key 对应的 value 自加 njedis.incrBy(String key , int n)
将 key 对应的 value 自减 1jedis.decr(String key)
将 key 对应的 value 自减 njedis.decrBy(String key , int n)

7、列表(List)操作

操作命令
添加一个Listjedis.lpush(String key,String v1,String v2, ...)
往key对应List左插入一个元素Vnjedis.lpush(String key,String Vn)
获取key对应List区间lij]的元素jedis.lrange(String key,int iint j)
删除指定元素val个数numjedis.rem(String keyint num,String val)
删除list区间[ij]之外的元素jedis.ltrim(String key,int i,int j)
key对应list左出栈一个元素jedis.lpop(String key)
key对应list右插入一个元素Vnjedisrpush(String key,String Vn)
key对应list右出栈一个元素jedis.rpop(String key)
修改key对应list指定下标index的元素jedis.set(String key,int index,String val)
获取key对应list的长度jedis.llen(String key)
获取key对应list下标为index的元素jedis.lindex(String key,int index)
把key对应list里面的元素从小到大排序iedis.sort(String key)

8、集合(Set)操作

操作命令
添加一个Setjedis.sadd(String key,String v1,String v2, ...)
获取key对应set的所有元素jedis.smembers(String key)
删除一个值为val的元素jedis.srem(String key,String val)
删除值为v1,v2...的元素jedis.srem(String key,String v... )
随机出栈set里的一个元素jedis.spop(String key)
获取set中元素个数jedis.scard(String key)
将元素val从集合k1剪切到k2jedis.smove(String k1,String k2,String val)
获取集合k1和集合k2的交集jedis.sinter(String k1,String k2)
获取集合k1和集合k2的并集jedis.sunion(String k1,String k2)
获取集合k1和集合k2的差集jedis.sdiff(String k1,String k2)

9、哈希(Hash)操作

操作命令
添加一个Hashjedis.hmset(String key,Map map)
往Hash插入一个元素(K-V)jedis.hset(String key,String key,String value)
获取Hash的所有(K-V元素jedis.hgetAll(String key)
获取Hash所有元素的keyjedis.hkeys(String key)
获取Hash所有元素的valuejedis.hvals(String key)
把Hash中k对应元素val+=ijedis.hincrBy(String key,String k,int i)
把Hash中k对应元素val-=ijedis.hdecrBy(String key,String k,int i)
从Hash删除一个或多个元素jedis.hdel(String key,String k1,String k2.... )
获取Hash中元素个数jedis.hlen(String key)
判断Hash是否存在k1对应元素jedis.hexists(String key,String k1)
获取Hash中一个或多个元素valuejedis.hmget(String key,String k1,String k2... )

10、有序集合(Zset)操作

操作命令
添加一个ZSetjedis.zadd(String key, Map map)
往ZSet插入一个元素(Score-Val)jedis.hset(String key, int score, String val)
获取ZSet里下标[i,j]区间元素valjedis.zrange(String key, int i, int j)
获取ZSet里下标[i,j]区间元素(Score-Val)jedis.zrangeWithScores(String key, int i, int j)
获取ZSet里scoreli,j]区间元素valjedis.zrangeByScore(String key, int i, int j)
获取ZSet里scoreli,j]区间元素(Score-Val)jedis.zrangeByScoreWithScores(String key,int iint j)
获取ZSet里val为value元素的scorejedis.zscore(String key, String value)
获取ZSet里val为value元素的score排名jedis.zrank(String key, String value)
删除ZSet里val为value的元素jedis.zrem(String key, String value)
获取ZSet的元素个数jedis.zcard(String key)
获取ZSet中score在liil区间元素个数jedis.zcount(String key, int i, int j)
把ZSet中val为value的元素score+=njedis.zincrby(String key, int n, String value)

11、排序操作

操作命令
生成一个排序对象( SortingParams)new SortingParams0:
队列按首字母a-z排序jedis.sort(String key, sortingParams.alpha0)
队列按数字升序排序jedis.sort(String key, sortingParams.asc0)
队列按数字降序排序jedis.sort(String key, sortingParams.desc0)

2.4 秒杀活动

1、创建一个SpringBoot项目添加依赖

        <!--Java操作redis-->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>4.2.3</version>
        </dependency>
        <!--StringUtils方法的包-->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>

2、编写代码

使用线程休眠方法解决超卖问题 
    
@RestController
public class TestJedis {
​
    @GetMapping("seckill")
    public String secKill() throws InterruptedException {
        // 填写连接redis的ip地址和端口号
        Jedis jedis = new Jedis("IP地址",6379);
        // 配置登录密码
        jedis.auth("密码");
        //添加库存10件商品
//        jedis.set("num","10");
        //获取库存数量:如果库存量为空,代表活动还没有开始
        String num = jedis.get("num");
        if(StringUtils.isBlank(num)){ //需要添加commons-lang3的jar包
            System.out.println("活动还没有开始");
        }else{
            //随机生成一个用户id
            String userid = UUID.randomUUID().toString();
            //将库存量转换为int类型,才能进行比较
            int num1 = Integer.parseInt(num);
            if (num1>0){
                //对事务进行监听
                jedis.watch("num");
                //进程睡眠,避免秒杀太快,出现num为负值的情况
                Thread.sleep(2);
                //开始组装事务
                Transaction trans = jedis.multi();
                //商品数量减1
                trans.decr("num");
                //将用户的id存起来
                trans.sadd("uid",userid);
                //执行事务
                List<Object> exec = trans.exec();
                if(exec==null||exec.size()==0){
                    System.out.println("秒杀失败!!!!!");
                }else{
                    System.out.println("秒杀成功");
                    System.out.println("秒杀成功,用户的id是"+userid);
                }
            }
        }
        return "";
    }
}
​
或者在进行事务组装之前也可以进行一次判断,如果数量上不成立,也不进行接下来的操作
    @RestController
public class TestJedis {
​
    @GetMapping("seckill")
    public String secKill() throws InterruptedException {
​
        // 填写连接redis的ip地址和端口号
        Jedis jedis = new Jedis("192.168.111.127",6379);
        // 配置登录密码
        jedis.auth("lwl");
        //添加库存10件商品
//        jedis.set("num","10");
        //获取库存数量:如果库存量为空,代表活动还没有开始
        String num = jedis.get("num");
        if(StringUtils.isBlank(num)){ //需要添加commons-lang3的jar包
            System.out.println("活动还没有开始");
        }else{
            //随机生成一个用户id
            String userid = UUID.randomUUID().toString();
            //将库存量转换为int类型,才能进行比较
            int num1 = Integer.parseInt(num);
            if (num1>0){
                //对事务进行监听
                jedis.watch("num");
                String nums = jedis.get("num");
                //multi中不能使用jedis
                int i = Integer.parseInt(nums);
                if (i>0) {
                    //开始组装事务
                    Transaction trans = jedis.multi();
                    //商品数量减1
                    trans.decr("num");
                    //将用户的id存起来
                    trans.sadd("uid", userid);
                    //执行事务
                    List<Object> exec = trans.exec();
​
                    if (exec == null || exec.size() == 0) {
                        System.out.println("秒杀失败!!!!!");
                    } else {
                        System.out.println("秒杀成功");
                        System.out.println("秒杀成功,用户的id是" + userid);
                    }
                }
            }
        }
        return "";
    }
}
​
注:multi中不可以使用jedis
判断用户是否重复
package com.example.demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;
import java.util.List;
​
@RestController
public class SecKillController
{
    @GetMapping("seckill")
    public void secKill(){
       //
       Jedis  jedis=new Jedis("IP地址");
        //
        jedis.auth("地址");
        //数据库的索引是15
        jedis.select(15);
        //获取商品的库存
        String kcKey="pro:1";
        //监视库存
        jedis.watch(kcKey);
        String uid="yyl"+Math.random()*100;
        //获取的秒杀商品的数量
        String s = jedis.get(kcKey);
        if(s==0){
            System.out.println("秒杀活动还没有开始!!!");
            return;
        }
        //判断商品的数量 >0  秒杀
        //<=0 秒杀活动已经结束
        int num = Integer.parseInt(s);
        if(num<=0){
            System.out.println("秒杀活动已经结束!!!!!");
            return;
        }
        if(num>0){
            //可以秒杀  判断用户是否重复
            Boolean userkey = jedis.sismember("userkey", uid);
            if(userkey){
                System.out.println("已经秒杀过了!!!!");
                return;
            }else{
​
                Transaction multi = jedis.multi();
                //秒杀成功
                //1.库存-1
                multi.decr(kcKey);
                //2.用户信息记录 用户id set
                multi.sadd("userkey",uid);
                List<Object> exec = multi.exec();
                //[1]
                if(exec==null||exec.size()==0){
                    System.out.println("秒杀失败!!!!!");
                }else{
                    System.out.println("秒杀成功");
                    System.out.println("秒杀成功,用户的id是"+uid);
                }
            }
        }
    }
}

可以使用postman进行测试路径

也可以使用 jmeter 这种压力测试软件,同时使用多个线程对项目进行测试

 

 

 

 

点击上面绿色箭头进行运行

可以在RESP文件中进行查看

2.5 Redis整合SpringBoot

1、创建SpringBoot项目

2、加入Redis的依赖

        <!--封装了一些redis启动时要配置的信息-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
            <version>2.6.0</version>
        </dependency>
                <!--Java操作redis-->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>4.2.3</version>
        </dependency>

3、编写配置文件

#设置reis的索引
spring.redis.database=15
#设置连接redis的密码
spring.redis.password=密码
#设置的redis的服务器
spring.redis.host=IP地址
#端口号
spring.redis.port=6379
#连接超时时间(毫秒)
spring.redis.timeout=1800000
#连接池最大连接数(使用负值表示没有限制)
spring.redis.lettuce.pool.max-active=20
#最大阻塞等待时间(负数表示没限制)
spring.redis.lettuce.pool.max-wait=-1
#连接池中的最大空闲连接
spring.redis.lettuce.pool.max-idle=5
#连接池中的最小空闲连接
spring.redis.lettuce.pool.min-idle=0

4、设置配置类

package com.example.redistest01;
​
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.JedisPoolConfig;
​
import java.time.Duration;
​
​
@EnableCaching
@Configuration
public class RedisConfig extends CachingConfigurerSupport {
    /**
     * 连接池的设置
     */
    @Bean
    public JedisPoolConfig getJedisPoolConfig() {
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        return jedisPoolConfig;
    }
​
​
    /**
     * RedisTemplate
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        // 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        // 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会跑出异常
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        template.setConnectionFactory(factory);
        //key序列化方式
        template.setKeySerializer(redisSerializer);
        //value序列化
        template.setValueSerializer(jackson2JsonRedisSerializer);
        //value hashmap序列化
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        return template;
    }
​
​
    /**
     * 缓存处理
     * @param factory
     * @return
     */
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        //解决查询缓存转换异常的问题
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        // 配置序列化(解决乱码的问题),过期时间600秒
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofSeconds(600))
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
                .disableCachingNullValues();
        RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
                .cacheDefaults(config)
                .build();
        return cacheManager;
    }
}

5、编写测试代码

spring-data-redis针对jedis提供了如下功能:
1. 连接池自动管理,提供了一个高度封装的“RedisTemplate”类
2. RedisTemplate 对五种数据结构分别定义了操作
操作字符串
redisTemplate.opsForValue();
操作hash
redisTemplate.opsForHash();
操作list
redisTemplate.opsForList();
操作set
redisTemplate.opsForSet();
操作有序set
redisTemplate.opsForZSet();
3. 针对数据的“序列化/反序列化”,提供了多种可选择策略(RedisSerializer)
JdkSerializationRedisSerializer:POJO对象的存取场景,使用JDK本身序列化机制,将pojo类通过ObjectInputStream/ObjectOutputStream进行序列化操作,最终redis-server中将存储字节序列。是目前最常用的序列化策略。
​
​
StringRedisSerializer:Key或者value为字符串的场景,根据指定的charset对数据的字节序列编码成string,是“new String(bytes, charset)”和“string.getBytes(charset)”的直接封装。是最轻量级和高效的策略。
​
​
JacksonJsonRedisSerializer:jackson-json工具提供了javabean与json之间的转换能力,可以将pojo实例序列化成json格式存储在redis中,也可以将json格式的数据转换成pojo实例。因为jackson工具在序列化和反序列化时,需要明确指定Class类型,因此此策略封装起来稍微复杂。【需要jackson-mapper-asl工具支持】
​
​
OxmSerializer:提供了将javabean与xml之间的转换能力,目前可用的三方支持包括jaxb,apache-xmlbeans;redis存储的数据将是xml工具。不过使用此策略,编程将会有些难度,而且效率最低;不建议使用。【需要spring-oxm模块的支持】

2.5.1 String类型常用方法

判断是否有key所对应的值,有则返回true,没有则返回false
redisTemplate.hasKey(key)
有则取出key值所对应的值
redisTemplate.opsForValue().get(key)
redisTemplate.opsForValue().set("key","val")
删除单个key值
redisTemplate.delete(key)
批量删除key
redisTemplate.delete(keys) //其中keys:Collection<K> keys
set void set(K key, V value);
    redisTemplate.opsForValue().set("num","123");
    redisTemplate.opsForValue().get("num")  输出结果为123
 
 
set void set(K key, V value, long timeout, TimeUnit unit); 
 
    redisTemplate.opsForValue().set("num","123",10, TimeUnit.SECONDS);
    redisTemplate.opsForValue().get("num")设置的是10秒失效,十秒之内查询有结果,十秒之后返回为null
​
​
            TimeUnit.DAYS          //天
            TimeUnit.HOURS         //小时
            TimeUnit.MINUTES       //分钟
            TimeUnit.SECONDS       //秒
            TimeUnit.MILLISECONDS  //毫秒 
 
 
set void set(K key, V value, long offset);
 
覆写(overwrite)给定 key 所储存的字符串值,从偏移量 offset 开始
​
​
    template.opsForValue().set("key","hello world");
    template.opsForValue().set("key","redis", 6);
    System.out.println("***************"+template.opsForValue().get("key"));
    结果:***************hello redis
 
 
get V get(Object key);
 
    template.opsForValue().set("key","hello world");
    System.out.println("***************"+template.opsForValue().get("key"));
    结果:***************hello world
 
 
getAndSet V getAndSet(K key, V value); 
 
设置键的字符串值并返回其旧值
 
    template.opsForValue().set("getSetTest","test");
    System.out.println(template.opsForValue().getAndSet("getSetTest","test2"));
    结果:test
 
append Integer append(K key, String value);
 
如果key已经存在并且是一个字符串,则该命令将该值追加到字符串的末尾。如果键不存在,则它被创建并设置为空字符串,因此APPEND在这种特殊情况下将类似于SET。
 
    template.opsForValue().append("test","Hello");
    System.out.println(template.opsForValue().get("test"));
    template.opsForValue().append("test","world");
    System.out.println(template.opsForValue().get("test"));
    Hello
    Helloworld
 
size Long size(K key);
返回key所对应的value值得长度
 
    template.opsForValue().set("key","hello world");
    System.out.println("***************"+template.opsForValue().size("key"));
    ***************11

2.5.2 List类型

通过索引获取列表中的元素
redisTemplate.opsForList().index(key, index)
    
获取列表指定范围内的元素(start开始位置, 0是开始位置,end 结束位置, -1返回所有)
redisTemplate.opsForList().range(key, start, end)
    
存储在list的头部,即添加一个就把它放在最前面的索引处
redisTemplate.opsForList().leftPush(key, value)  
    
把多个值存入List中(value可以是多个值,也可以是一个Collection value)
redisTemplate.opsForList().leftPushAll(key, value)
    
List存在的时候再加入
redisTemplate.opsForList().leftPushIfPresent(key, value)
    
如果pivot处值存在则在pivot前面添加
redisTemplate.opsForList().leftPush(key, pivot, value)
​
​
按照先进先出的顺序来添加(value可以是多个值,或者是Collection var2)
redisTemplate.opsForList().rightPush(key, value)
redisTemplate.opsForList().rightPushAll(key, value)
​
​
在pivot元素的右边添加值
redisTemplate.opsForList().rightPush(key, pivot, value)
​
​
设置指定索引处元素的值
redisTemplate.opsForList().set(key, index, value)
​
​
移除并获取列表中第一个元素(如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止)
redisTemplate.opsForList().leftPop(key)
redisTemplate.opsForList().leftPop(key, timeout, unit)
​
​
移除并获取列表最后一个元素
redisTemplate.opsForList().rightPop(key)
redisTemplate.opsForList().rightPop(key, timeout, unit)
​
​
从一个队列的右边弹出一个元素并将这个元素放入另一个指定队列的最左边
redisTemplate.opsForList().rightPopAndLeftPush(sourceKey, destinationKey)
redisTemplate.opsForList().rightPopAndLeftPush(sourceKey, destinationKey, timeout, unit)
​
​
删除集合中值等于value的元素(index=0, 删除所有值等于value的元素; index>0, 从头部开始删除第一个值等于value的元素; index<0, 从尾部开始删除第一个值等于value的元素)
redisTemplate.opsForList().remove(key, index, value)
​
​
将List列表进行剪裁
redisTemplate.opsForList().trim(key, start, end)
​
​
获取当前key的List列表长度
redisTemplate.opsForList().size(key)

2.5.3 Set类型

添加元素
redisTemplate.opsForSet().add(key, values)
    
移除元素(单个值、多个值)
redisTemplate.opsForSet().remove(key, values)
​
​
删除并且返回一个随机的元素
redisTemplate.opsForSet().pop(key)
​
​
获取集合的大小
redisTemplate.opsForSet().size(key)
​
​
判断集合是否包含value
redisTemplate.opsForSet().isMember(key, value)
​
​
获取两个集合的交集(key对应的无序集合与otherKey对应的无序集合求交集)
redisTemplate.opsForSet().intersect(key, otherKey)
​
​
获取多个集合的交集(Collection var2)
redisTemplate.opsForSet().intersect(key, otherKeys)
​
​
key集合与otherKey集合的交集存储到destKey集合中(其中otherKey可以为单个值或者集合)
redisTemplate.opsForSet().intersectAndStore(key, otherKey, destKey)
​
​
key集合与多个集合的交集存储到destKey无序集合中
redisTemplate.opsForSet().intersectAndStore(key, otherKeys, destKey)
​
​
获取两个或者多个集合的并集(otherKeys可以为单个值或者是集合)
redisTemplate.opsForSet().union(key, otherKeys)
​
​
key集合与otherKey集合的并集存储到destKey中(otherKeys可以为单个值或者是集合)
redisTemplate.opsForSet().unionAndStore(key, otherKey, destKey)
​
​
获取两个或者多个集合的差集(otherKeys可以为单个值或者是集合)
redisTemplate.opsForSet().difference(key, otherKeys)
​
​
差集存储到destKey中(otherKeys可以为单个值或者集合)
redisTemplate.opsForSet().differenceAndStore(key, otherKey, destKey)
​
​
随机获取集合中的一个元素
redisTemplate.opsForSet().randomMember(key)
​
​
获取集合中的所有元素
redisTemplate.opsForSet().members(key)
​
​
随机获取集合中count个元素
redisTemplate.opsForSet().randomMembers(key, count)
​
​
获取多个key无序集合中的元素(去重),count表示个数
redisTemplate.opsForSet().distinctRandomMembers(key, count)
​
​
遍历set类似于Interator(ScanOptions.NONE为显示所有的)
redisTemplate.opsForSet().scan(key, options)

2.5.4 Hash类型

Long delete(H key, Object... hashKeys);
删除给定的哈希hashKeys
 
    System.out.println(template.opsForHash().delete("redisHash","name"));
    System.out.println(template.opsForHash().entries("redisHash"));
    1
    {class=6, age=28.1}
 
Boolean hasKey(H key, Object hashKey);
确定哈希hashKey是否存在
    System.out.println(template.opsForHash().hasKey("redisHash","666"));
    System.out.println(template.opsForHash().hasKey("redisHash","777"));
    true
    false
 
HV get(H key, Object hashKey);
从键中的哈希获取给定hashKey的值
    System.out.println(template.opsForHash().get("redisHash","age"));
    26
 
 Set<HK> keys(H key);
获取key所对应的散列表的key
    System.out.println(template.opsForHash().keys("redisHash"));
    //redisHash所对应的散列表为{class=1, name=666, age=27}
    [name, class, age]
 
Long size(H key);
获取key所对应的散列表的大小个数
    System.out.println(template.opsForHash().size("redisHash"));
    //redisHash所对应的散列表为{class=1, name=666, age=27}
    3
 
 void putAll(H key, Map<? extends HK, ? extends HV> m);
使用m中提供的多个散列字段设置到key对应的散列表中
 
    Map<String,Object> testMap = new HashMap();
    testMap.put("name","666");
    testMap.put("age",27);
    testMap.put("class","1");
    template.opsForHash().putAll("redisHash1",testMap);
    System.out.println(template.opsForHash().entries("redisHash1"));
    {class=1, name=jack, age=27}
 
 void put(H key, HK hashKey, HV value);
设置散列hashKey的值
 
    template.opsForHash().put("redisHash","name","666");
    template.opsForHash().put("redisHash","age",26);
    template.opsForHash().put("redisHash","class","6");
    System.out.println(template.opsForHash().entries("redisHash"));
    {age=26, class=6, name=666}
 
 List<HV> values(H key);
获取整个哈希存储的值根据密钥
    System.out.println(template.opsForHash().values("redisHash"));
    [tom, 26, 6]
 
 Map<HK, HV> entries(H key);
获取整个哈希存储根据密钥
    System.out.println(template.opsForHash().entries("redisHash"));
    {age=26, class=6, name=tom}
​
 Cursor<Map.Entry<HK, HV>> scan(H key, ScanOptions options);
使用Cursor在key的hash中迭代,相当于迭代器。
    Cursor<Map.Entry<Object, Object>> curosr = template.opsForHash().scan("redisHash", 
      ScanOptions.ScanOptions.NONE);
        while(curosr.hasNext()){
            Map.Entry<Object, Object> entry = curosr.next();
            System.out.println(entry.getKey()+":"+entry.getValue());
        }
    age:27
    class:6
    name:666
命令操作返回值
hash.delete(H key, Object... hashKeys)删除,可以传入多个map的key【hdel】Long
hash.hasKey(key, hashKey)查看hash中的某个hashKey是否存在【hexists】Boolean
hash.get(key, hashKey)获取值【hget】Object(HV 泛型约束对象)
hash.multiGet(H key, Collection<HK> hashKeys)批量获取集合中的key对应的值【hmget】List<HV>
hash.increment(H key, HK hashKey, long delta)对值进行+(delta值)操作【】Long
hash.increment(H key, HK hashKey, double delta)~double
hash.keys(key)返回map内hashKey的集合【hkeys】Set<HK>
hash.lengthOfValue(H key, HK hashKey)返回查询键关联的值的长度,为null则返回0【hstrlen】Long
hash.size(H key)获取hashKey的个数【hlen】Long
hash.putAll(H key, Map<? extends HK, ? extends HV> m)相当于map的putAll【hmset】void
hash.put(H key, HK hashKey, HV value)设置值,添加hashKey-value,hashKay相当于map的key 【hset】void
hash.putIfAbsent(H key, HK hashKey, HV value)仅当hashKey不存在时设置值Boolean
hash.values(key)返回value的集合【hvals】List<HV>
hase.entries(H key)获取map【hgetall】Map<HK, HV>
hash.scan(H key, ScanOptions options)基于游标的迭代查询【hscan】Cursor<Map.Entry<HK, HV>>(返回的Cursor要手动关闭,见下面示例2)
hash.getOperations()返回RedisOperation,它就是redis操作的接口RedisOperations<H, ?>

2.5..5 ZSet类型

Boolean add(K key, V value, double score);
新增一个有序集合,存在的话为false,不存在的话为true
  
  System.out.println(template.opsForZSet().add("zset1","zset-1",1.0));
  true
 Long add(K key, Set<TypedTuple<V>> tuples);
新增一个有序集合
    ZSetOperations.TypedTuple<Object> objectTypedTuple1 = new DefaultTypedTuple<>("zset-5",9.6);
    ZSetOperations.TypedTuple<Object> objectTypedTuple2 = new DefaultTypedTuple<>("zset-6",9.9);
    Set<ZSetOperations.TypedTuple<Object>> tuples = new HashSet<ZSetOperations.TypedTuple<Object>>();
    tuples.add(objectTypedTuple1);
    tuples.add(objectTypedTuple2);
    System.out.println(template.opsForZSet().add("zset1",tuples));
    System.out.println(template.opsForZSet().range("zset1",0,-1));
    [zset-1, zset-2, zset-3, zset-4, zset-5, zset-6]
Long remove(K key, Object... values);
从有序集合中移除一个或者多个元素
   System.out.println(template.opsForZSet().range("zset1",0,-1));
   System.out.println(template.opsForZSet().remove("zset1","zset-6"));
    System.out.println(template.opsForZSet().range("zset1",0,-1));
    [zset-1, zset-2, zset-3, zset-4, zset-5, zset-6]
    1
    [zset-1, zset-2, zset-3, zset-4, zset-5]
 Long rank(K key, Object o);
返回有序集中指定成员的排名,其中有序集成员按分数值递增(从小到大)顺序排列
 
    System.out.println(template.opsForZSet().range("zset1",0,-1));
    System.out.println(template.opsForZSet().rank("zset1","zset-2"));
    [zset-2, zset-1, zset-3, zset-4, zset-5]
    0   //表明排名第一
 
Set<V> range(K key, long start, long end);
通过索引区间返回有序集合成指定区间内的成员,其中有序集成员按分数值递增(从小到大)顺序排列
    System.out.println(template.opsForZSet().range("zset1",0,-1));
    [zset-2, zset-1, zset-3, zset-4, zset-5]
Long count(K key, double min, double max);
通过分数返回有序集合指定区间内的成员个数
 
    System.out.println(template.opsForZSet().rangeByScore("zset1",0,5));
    System.out.println(template.opsForZSet().count("zset1",0,5));
    [zset-2, zset-1, zset-3]
    3
 
Long size(K key);
获取有序集合的成员数,内部调用的就是zCard方法
​
    System.out.println(template.opsForZSet().size("zset1"));
    6
​
 
 Double score(K key, Object o);
获取指定成员的score值
 
    System.out.println(template.opsForZSet().score("zset1","zset-1"));
    2.2
 
 
Long removeRange(K key, long start, long end);
移除指定索引位置的成员,其中有序集成员按分数值递增(从小到大)顺序排列
 
    System.out.println(template.opsForZSet().range("zset2",0,-1));
    System.out.println(template.opsForZSet().removeRange("zset2",1,2));
    System.out.println(template.opsForZSet().range("zset2",0,-1));
    [zset-1, zset-2, zset-3, zset-4]
    2
    [zset-1, zset-4]
 
 
Cursor<TypedTuple<V>> scan(K key, ScanOptions options);
遍历zset
    Cursor<ZSetOperations.TypedTuple<Object>> cursor = template.opsForZSet().scan("zzset1", ScanOptions.NONE);
        while (cursor.hasNext()){
           ZSetOperations.TypedTuple<Object> item = cursor.next();
           System.out.println(item.getValue() + ":" + item.getScore());
        }
    zset-1:1.0
    zset-2:2.0
    zset-3:3.0
    zset-4:6.0

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值