迎关注我的头条号:Wooola,10 年 Java 软件开发及架构设计经验,专注于 Java、Go 语言、微服务架构,致力于每天分享原创文章、快乐编码和开源技术。
前言
本项目基于springboot+ spring-boot-starter-data-redis+redisson
简单的redis demo可以参考:SpringBoot整合redis
https://www.jianshu.com/p/8e71737a1101
github地址:
Redisson:https://github.com/weiess/redis-and-Redisson.git
源码地址:https://github.com/weiess/redis-and-Redisson.git
redis大家工作的时候都很多,笔者根据自己经验总结下redis的几个数据类型,常用场景,也欢迎大家留言总结自己的经验。
hash
hash在redis里可以存储对象,当然string也可以,只不过hash相比较string,效率更高一点,而且功能也很强大,可以实现简单的购物车:
下面先给大家看下简单的存储对象hash实现:
/* * hash实现存储对象 * */ @Test public void testHsetpojo(){ User user = new User(); user.setId(123); user.setAge(20); user.setAddr("北京"); user.setName("yang"); Map map = BeanUtils.beanToMap(user); String key = "user"; redisUtil.hmset(key,map); System.out.println(redisUtil.hmget(key)); System.out.println("id="+redisUtil.hget(key,"id")); String key2 = "user:"+user.getId(); redisUtil.hmset(key2,map); System.out.println(redisUtil.hmget(key2)); }
这里的redisUtil是笔者封好的工具类,源码在文章最底下。
hash存储对象的时候可以把user当作key,例如代码中的key,因为hash是 key item value三种结构,可以把后面的item和value看成一个map,这样结构就是key map,方便理解。
实际项目中可以给key加个标示符,比如key = userId:a123456,这个大家可以根据项目来定义。
可以用hash实现购物车功能:
例如:
hash.jpg
代码如下:
/* * hash实现购物车 * */ @Test public void testcar(){ String key ="carUser:123456"; redisUtil.del(key); Map map = new HashMap(); map.put("book:a11111",1); map.put("book:a11112",2); map.put("book:a11113",3); boolean b = redisUtil.hmset(key,map); System.out.println("key = "+redisUtil.hmget(key)); //增加book:a11111的数量 redisUtil.hincr(key,"book:a11111",1); System.out.println(redisUtil.hmget(key)); //减少book:a11112的数量 redisUtil.hincr(key,"book:a11112",-3); //或者redisUtil.hdecr(key,"book:a11111",1); System.out.println(redisUtil.hmget(key)); //获取所有key1的field的值 System.out.println("hegetall="+redisUtil.hmget(key)); //获取key下面的map数量 System.out.println("length="+redisUtil.hlen(key)); //删除某个key下的map redisUtil.hdel(key,"book:a11112"); System.out.println(redisUtil.hmget(key)); }
hash里的key就是当前用户的购物车,map就是商品(map里的key是商品id,map的v是数量)
功能实现注释都有。注意这里的减操作是可以为负数的,所以大家一定要注意判断负数的情况。
list
常见的list可以分为下面三个数据结构方便大家理解:
stack(栈)= LPUSH + LPOP Queue(队列)= LPUSH + RPOP BlockingMQ(阻塞队列)= LPUSH + BRPOP
@Test public void testList(){ String key = "a123456"; redisUtil.del(key); String v1 = "aaaaa"; String v2 = "bbbbb"; String v3 = "ccccc"; List list = new ArrayList(); list.add(v1); list.add(v2); list.add(v3); boolean b1 = redisUtil.lSet(key,list); System.out.println(redisUtil.lGet(key,0,-1)); System.out.println(redisUtil.lGetIndex(key,0)); System.out.println(redisUtil.lpop(key)); System.out.println(redisUtil.rpop(key)); System.out.println(redisUtil.lGet(key,0,-1)); redisUtil.del(key); redisUtil.rpush(key,v1); System.out.println(redisUtil.lGet(key,0,-1)); redisUtil.rpush(key,v2); System.out.println(redisUtil.lGet(key,0,-1)); redisUtil.lpush(key,v3); System.out.println(redisUtil.lGet(key,0,-1)); }
实际工作中常用于消息推送,比如vx订阅号推送,微博推送
代码实现:
@Test public void testVX(){ String key = "VXuser:a123456"; redisUtil.del(key); String message1 = "a1"; String message2 = "b2"; String message3 = "c3"; //订阅号a发表了一片文章,文章id是a1 redisUtil.lpush(key,message1); //订阅号b发表了一片文章,文章id是b2 redisUtil.lpush(key,message2); //订阅号b发表了一片文章,文章id是c3 redisUtil.lpush(key,message3); //用户获取 System.out.println(redisUtil.lGet(key,0,-1)); }
比如user:a23456订阅了这个订阅号a,订阅号a写了一片内容,只需要加入user:a123456的队列中,user就能查看到,这里的redisUtil.lGet(key,0,-1)表示获取key下的所有v。
set
set常用于一些数学运算,他有求交集,并集,差集的功能:
@Test public void testset(){ String key1 = "a1"; redisUtil.del(key1); String key2 = "a2"; redisUtil.del(key2); redisUtil.sSet(key1,1,2,3,4,5); System.out.println("key1="+redisUtil.sGet(key1)); redisUtil.sSet(key2,1,2,5,6,7); System.out.println("key1="+redisUtil.sGet(key2)); //获取key的数量 System.out.println("length="+redisUtil.sGetSetSize(key1)); //取key1和key2的交集 System.out.println("交集="+redisUtil.sIntersect(key1,key2)); //取key1和key2的差集 System.out.println("差集="+redisUtil.sDifference(key1,key2)); //取key1和key2的并集 System.out.println("并集="+redisUtil.sUnion(key1,key2)); //取key1的随机一个数 System.out.println("随机数="+redisUtil.sRandom(key1)); System.out.println("key1="+redisUtil.sGet(key1)); //取key1的随机一个数,并且这个值在key中删除 System.out.println("随机数="+redisUtil.spop(key1)); System.out.println("key1="+redisUtil.sGet(key1)); }
实际工作中可以实现抽奖,共同关注,推荐好友等功能:
@Test public void testSet2(){ String key ="act:123456"; redisUtil.del(key); long l = redisUtil.sSet(key,"a1","a2","a3","a4","a5"); System.out.println(redisUtil.sGet(key)); //抽奖 System.out.println(redisUtil.spop(key)); String user1 = "vxuser:a123456"; String user2 = "vxuser:b123456"; String user3 = "vxuser:c123456"; String user4 = "vxuser:d123456"; String user5 = "vxuser:e123456"; String user6 = "vxuser:f123456"; redisUtil.del("gzuser1"); redisUtil.del("gzuser2"); //gzuser1关注user2,user3,user6 redisUtil.sSet("gzuser1",user2,user3,user6); //gzuser2关注user1,user3,user4,user5 redisUtil.sSet("gzuser2",user1,user3,user4,user5); //共同好友 System.out.println("共同好友"+redisUtil.sIntersect("gzuser1","gzuser2")); //你关注的好友也关注了他 Set set = redisUtil.sUnion(user1,user2); //这里取并集,放在中间表bj里 for (String s:set){ redisUtil.sSet("bj",s); } System.out.println(redisUtil.sGet("bj")); System.out.println("你关注的好友也关注了他"+redisUtil.sDifference("bj","gzuser1")); }
zset
zset相比较set一个有序,一个无序,具体功能大同小异,我就不多说。
string
简单的string基本功能我就不多说了,这里要说的是分布式常见的setnx命令实现分布式锁:
setnx命令其实表示『SET if Not eXists』(如果不存在,则 SET)的简写。设置成功,返回 1 ;设置失败,返回 0 。
如果set 的key已经在redis里了,你再setnx,就会失败,但是你继续用set命令,是会覆盖当前的key,且返回成功。
/* * setnx 实现最简单的分布式锁 * */ @Test public void setlock() { DefaultRedisScript script = new DefaultRedisScript(); script.setScriptSource(new ResourceScriptSource(new ClassPathResource("lua/deleteLua.lua"))); script.setResultType(Long.class); String key ="product:001"; String value = Thread.currentThread().getId()+""; try { boolean result = redisUtil.setnx(key,value,100); if(!result){ System.out.println("系统繁忙中"); } else { System.out.println("这里是你业务代码"); } }catch (Exception e){ e.printStackTrace(); }finally { Object result = redisUtil.execute(script,Collections.singletonList(key),value); System.out.println(result);// if (value.equals(redisUtil.get(key))){// redisUtil.del(key); } } }
这是最简单的分布式锁实现,我给大家简单解释下:
首先,DefaultRedisScript这个类是为了读取lua脚本,这里笔者的finally代码块用了lua脚本删除redis的key,其实
Object result = redisUtil.execute(script,Collections.singletonList(key),value);
这行代码等价于下面这行代码
if (value.equals(redisUtil.get(key))){ redisUtil.del(key); }
为什么要用lua脚本呢,更多的是为了原子性,如果用if操作,要执行两部,在大型的环境下很容易出问题,而lua脚本就不会,他把两个步骤合成一个步骤去执行,这样保证原子性。