Set应用场景

  • set命令使用
  • 淘宝黑名单
    • 一、黑名单过滤器业务场景分析
    • 二 、解决的技术方案
    • 三、SpringBoot+redis模仿实现校验器
  • 京东京豆抽奖
    • 一、京东京豆抽奖的业务场景分析
    • 二、京东京豆抽奖的技术方案
    • 三、SpringBoot+Redis 实现京东京豆抽奖
  • 支付宝抽奖
    • 二、支付宝抽奖的技术方案
    • SpringBoot+Redis 实现支付宝抽奖
  • 微博榜单与QQ群的随机展示
    • 一、随机展示业务场景分析
    • 二、随机展示的redis技术方案
    • 三、 SpringBoot+Redis 实现高并发随机展示
  • 微博榜单的随机展示
    • 业务场景分析
    • SpringBoot+redis实现微博榜单随机展示
  • 帖子点赞
    • 一、微博点赞业务场景分析
  • 二、微博点赞的技术方案
    • 三、SpringBoot+Redis 实现微博点赞
  • 关注与粉丝
    • 一、微博关注与粉丝的业务场景分析
    • 二、微博关注与粉丝的redis技术方案
    • SpringBoot+Redis 实现微博关注与粉丝
  • 微关系计算
    • 一、计算好友关系业务场景分析
    • 二、计算好友关系的redis技术方案
    • SpringBoot+Redis 计算微博好友关系
  • HyperLogLog
    • 命令使用
      • HyperLogLog 命令详解
        • PFADD
        • PFCOUNT
        • PFMERGE
    • 基于Redis的UV计算

set命令使用

redis set 和java的set集合功能差不多的

  • 集合(Set) 的主要功能就是求并集、交集、差集。
A = {'a', 'b', 'c'}
B = {'a', 'e', 'i', 'o', 'u'}inter(x, y): 交集,在集合x和集合y中都存在的元素。
inter(A, B) = {'a'}union(x, y): 并集,在集合x中或集合y中的元素,如果一个元素在x和y中都出现,那只记录一次即可。
union(A,B) = {'a', 'b', 'c', 'e', 'i', 'o', 'u'}diff(x, y): 差集,在集合x中而不在集合y中的元素。
diff(A,B) = {'b', 'c'}card(x): 基数,一个集合中元素的数量。
card(A) = 3空集: 基数为0的集合。
  • sadd(key, member)
    向名称为key的set中添加元素member
  • smembers(key)
    返回名称为key的set的所有元素
127.0.0.1:6379> sadd users u1
(integer) 1
127.0.0.1:6379> sadd users u2
(integer) 1
127.0.0.1:6379> sadd users u3 u4
(integer) 2
127.0.0.1:6379> smembers users
1) "u2"
2) "u4"
3) "u1"
4) "u3"
  • srem(key, member) :
    删除名称为key的set中的元素member
127.0.0.1:6379> smembers users
1) "u2"
2) "u4"
3) "u1"
4) "u3"
127.0.0.1:6379> srem users u1
(integer) 1
127.0.0.1:6379> srem users u3 u4
(integer) 2
127.0.0.1:6379> smembers users
1) "u2"
  • sismember(key, member) :
    member是否是名称为key的set的元素
127.0.0.1:6379> smembers users
1) "u2"
127.0.0.1:6379> sismember users u2
(integer) 1
127.0.0.1:6379> sismember users u1
(integer) 0
  • scard(key) :
    返回名称为key的set的基数,一个集合中元素的数量。
127.0.0.1:6379> smembers users
1) "u2"
127.0.0.1:6379> scard users
(integer) 1
  • smove(srckey, dstkey, member) :
    将member元素从source集合移动到destination集合。
127.0.0.1:6379> smembers users
1) "u2"
2) "u4"
3) "u1"
4) "u3"
127.0.0.1:6379> smembers blacklist
(empty list or set)
127.0.0.1:6379> smove users blacklist u1
(integer) 1
127.0.0.1:6379> smembers users
1) "u2"
2) "u4"
3) "u3"
127.0.0.1:6379> smembers blacklist
1) "u1"
  • srandmember(key) :
    随机返回名称为key的set的一个元素
127.0.0.1:6379> smembers users
1) "u2"
2) "u4"
3) "u3"
127.0.0.1:6379> srandmember users
"u3"
127.0.0.1:6379> srandmember users 2
1) "u2"
2) "u3"
  • spop(key) :
    随机返回并删除名称为key的set中一个元素
127.0.0.1:6379> smembers users
1) "u2"
2) "u4"
3) "u3"
127.0.0.1:6379> spop users
"u3"
127.0.0.1:6379> smembers users
1) "u2"
2) "u4"
  • sinter(key1, key2,…key N) :
    求交集。
127.0.0.1:6379> smembers group1
1) "3"
2) "2"
3) "4"
4) "1"
5) "a"
127.0.0.1:6379> smembers group2
1) "b"
2) "a"
3) "1"
4) "c"
127.0.0.1:6379> sinter group1 group2
1) "a"
2) "1"
  • sinterstore(dstkey, (key2,…key N)) :
    求交集并将交集保存到dstkey的集合
127.0.0.1:6379> sinterstore group3 group1 group2
(integer) 2
127.0.0.1:6379> smembers group1
1) "3"
2) "4"
3) "1"
4) "2"
5) "a"
127.0.0.1:6379> smembers group2
1) "b"
2) "a"
3) "1"
4) "c"
127.0.0.1:6379> smembers group3
1) "a"
2) "1"
  • sunion(key1, (keys)) :
    求并集
127.0.0.1:6379> sunion group1 group2
1) "3"
2) "4"
3) "1"
4) "2"
5) "b"
6) "a"
7) "c"
  • sunionstore(dstkey, (keys)) :
    求并集并将并集保存到dstkey的集合
127.0.0.1:6379> sunionstore group4 group1 group2
(integer) 7
127.0.0.1:6379> smembers group4
1) "3"
2) "4"
3) "1"
4) "2"
5) "b"
6) "a"
7) "c"
  • sdiff(key1, (keys)) :
    求差集
127.0.0.1:6379> smembers group1
1) "3"
2) "4"
3) "1"
4) "2"
5) "a"
127.0.0.1:6379> smembers group2
1) "b"
2) "a"
3) "1"
4) "c"
127.0.0.1:6379> sdiff group1 group2
1) "2"
2) "3"
3) "4"
  • diffstore(dstkey, (keys)) :
    求差集并将差集保存到dstkey的集合
127.0.0.1:6379> sdiffstore group5 group1 group2
(integer) 3
127.0.0.1:6379> smembers group5
1) "2"
2) "3"
3) "4"

淘宝黑名单

一、黑名单过滤器业务场景分析

淘宝的商品评价功能,不是任何人就能评价的,有一种职业就是差评师,差评师就是勒索敲诈商家,
这种差评师在淘宝里面就被设置了黑名单,即使购买了商品,也评价不了。

二 、解决的技术方案

黑名单过滤器除了针对上文说的淘宝评价,针对用户黑名单外,其实还有ip黑名单、设备黑名单等。
在高并发的情况下,通过数据库过滤明显不符合要求,一般的做法都是通过Redis来实现的。
那redis那种数据结构适合做这种黑名单的呢?
答案是:set
步骤1:先把数据库的数据同步到redis的set集合中。
步骤2:评价的时候验证是否为黑名单,通过sismember命令来实现。

三、SpringBoot+redis模仿实现校验器

步骤1:提前先把数据刷新到redis缓存中。

/*** 提前先把数据刷新到redis缓存中*/
@PostConstruct
public void init(){log.info("启动初始化 ..........");List<Integer> blacklist=this.blacklist();//this.redisTemplate.delete(Constants.BLACKLIST_KEY);//TODO:异步插入数据库DBblacklist.forEach(t->this.redisTemplate.opsForSet().add(Constants.BLACKLIST_KEY,t));
}/*** 模拟100个黑名单*/
public List<Integer> blacklist() {List<Integer> list=new ArrayList<>();for (int i = 0; i < 100; i++) {list.add(i);}return list;
}

步骤2:编写黑名单校验器接口


/***编写黑名单校验器接口* true=黑名单* false=不是黑名单*/
@GetMapping(value = "/isBlacklist")
public boolean isBlacklist(Integer userId) {boolean bo=false;try {//到set集合中去校验是否黑名单,bo = this.redisTemplate.opsForSet().isMember(Constants.BLACKLIST_KEY,userId);log.info("查询结果:{}", bo);} catch (Exception ex) {//这里的异常,一般是redis瘫痪 ,或 redis网络timeoutlog.error("exception:", ex);//TODO 走DB查询}return bo;
}

京东京豆抽奖

一、京东京豆抽奖的业务场景分析

  1. 可以无线抽,奖品是无限的,不同奖品的概率是不一样的;

二、京东京豆抽奖的技术方案

京豆抽奖一般是采用redis的set集合来操作的,那为什么是set集合适用于抽奖呢?
2个原因:
1.set集合的特点是元素不重复 存放1个 5个 10个京豆 谢谢参与
2.set集合支持随机读取
具体的技术方案是采用set集合的srandmember命令来实现,随机返回set的一个元素

三、SpringBoot+Redis 实现京东京豆抽奖

步骤1:奖品的初始化
由于set集合是不重复,故在奖品初始化的时候,要为每个奖品设置一个序列号。

    /***提前先把数据刷新到redis缓存中。*/@PostConstructpublic void init(){log.info("启动初始化..........");boolean bo=this.redisTemplate.hasKey(Constants.PRIZE_KEY);if(!bo){List<String> crowds=this.prize();crowds.forEach(t->this.redisTemplate.opsForSet().add(Constants.PRIZE_KEY,t));}}/***按一定的概率初始化奖品*/public List<String> prize() {List<String> list=new ArrayList<>();//10个京豆,概率10%for (int i = 0; i < 10; i++) {list.add("10-"+i);}//5个京豆,概率20%for (int i = 0; i < 20; i++) {list.add("5-"+i);}//1个京豆,概率60%for (int i = 0; i < 60; i++) {list.add("1-"+i);}//0个京豆,概率10%for (int i = 0; i < 10; i++) {list.add("0-"+i);}return list;}

步骤2:抽奖

  @GetMapping(value = "/prize")public String prize() {String result="";try {//随机取1次。String object = (String)this.redisTemplate.opsForSet().randomMember(Constants.PRIZE_KEY);if (!StringUtils.isEmpty(object)){//截取序列号 例如10-1int temp=object.indexOf('-');int no=Integer.valueOf(object.substring(0,temp));switch (no){case 0:result="谢谢参与";break;case 1:result="获得1个京豆";break;case 5:result="获得5个京豆";break;case 10:result="获得10个京豆";break;default:result="谢谢参与";}}log.info("查询结果:{}", object);} catch (Exception ex) {log.error("exception:", ex);}return result;}

支付宝抽奖

二、支付宝抽奖的技术方案

思考一个问题:支付宝的抽奖 和 京东京豆的抽奖有什么区别????

  1. 京豆抽奖:奖品是可以重复,例如抽5京豆可以再抽到5京豆,即京豆是无限量抽。
  2. 支付宝抽奖: 奖品不能重复抽,例如1万人抽1台华为手机;再给大家举一个熟悉的例子:
    例如公司年会,抽中奖品的人,下一轮就不能重复抽取,不然就会重复中奖。
  • 技术方案和京东的京豆类似,但是不同的是
    京东的京豆用了srandmember命令,即随机返回set的一个元素
  • 支付宝的抽奖要用spop命令,即随机返回并删除set中一个元素
    为什么呢?
  • 因为支付宝的奖品有限,不能重复抽,故抽奖完后,必须从集合中剔除中奖的人。
    再举个每个人都参与过的例子,年会抽奖,你公司1000人,年会抽奖3等奖500名100元,2等奖50名1000元,1等奖10名10000元,
    在抽奖的设计中就必须把已中奖的人剔除,不然就会出现重复中奖的概率。

SpringBoot+Redis 实现支付宝抽奖

步骤1:初始化抽奖数据

  /***提前先把数据刷新到redis缓存中。*/@PostConstructpublic void init(){log.info("启动初始化..........");boolean bo=this.redisTemplate.hasKey(Constants.PRIZE_KEY);if(!bo){List<Integer> crowds=this.prize();crowds.forEach(t->this.redisTemplate.opsForSet().add(Constants.PRIZE_KEY,t));}}/*** 模拟10个用户来抽奖 list存放的是用户id* 例如支付宝参与抽奖,就把用户id加入set集合中* 例如公司抽奖,把公司所有的员工,工号都加入到set集合中*/public List<Integer> prize() {List<Integer> list=new ArrayList<>();for(int i=1;i<=10;i++){list.add(i);}return list;}

步骤2:抽奖逻辑

    @GetMapping(value = "/prize")public List<Integer> prize(int num) {try {SetOperations<String, Integer> setOperations= this.redisTemplate.opsForSet();//spop命令,即随机返回并删除set中一个元素List<Integer> objs = setOperations.pop(Constants.PRIZE_KEY,num);log.info("查询结果:{}", objs);return  objs;} catch (Exception ex) {log.error("exception:", ex);}return null;}

微博榜单与QQ群的随机展示

一、随机展示业务场景分析

思考题:为什么要随机展示?
因为展示的区域有限啊,在那么小的地方展示全部数据是不可能的,通常的做法就是随机展示一批数据,然后用户点击“换一换”按钮,再随机展示另一批。

二、随机展示的redis技术方案

随机展示的原因就是区域有限,而区域有限的地方通常就是首页或频道页,这些位置通常都是访问量并发量非常高的,
一般是不可能采用数据库来实现的,通常都是Redis来实现。
redis的实现技术方案:
步骤1:先把数据准备好,把所有需要展示的内容存入redis的Set数据结构中。
步骤2:通过srandmember命令随机拿一批数据出来。

三、 SpringBoot+Redis 实现高并发随机展示

步骤1:提前先把数据刷新到redis缓存中

    /***提前先把数据刷新到redis缓存中。*/@PostConstructpublic void init(){log.info("启动初始化 群..........");List<String> crowds=this.crowd();this.redisTemplate.delete(Constants.CROWD_KEY);crowds.forEach(t->this.redisTemplate.opsForSet().add(Constants.CROWD_KEY,t));}/*** 模拟100个热门群,用于推荐*/public List<String> crowd() {List<String> list=new ArrayList<>();for (int i = 0; i < 100; i++) {Random rand = new Random();int id= rand.nextInt(10000);list.add("群"+id);}return list;}

步骤2:编写随机查询接口

@GetMapping(value = "/crowd")
public List<String> crowd() {List<String> list=null;try {//采用redis set数据结构,随机取出10条数据list = this.redisTemplate.opsForSet().randomMembers(Constants.CROWD_KEY,10);log.info("查询结果:{}", list);} catch (Exception ex) {//这里的异常,一般是redis瘫痪 ,或 redis网络timeoutlog.error("exception:", ex);//TODO 走DB查询}return list;
}

微博榜单的随机展示

业务场景分析


他这个换一换,可能包含了不同类型的数据,例如主播榜,美食榜等等、每个榜单里面包含三条数据;

SpringBoot+redis实现微博榜单随机展示

步骤1、创建实体类WeiBoRecommendList

@Data
@ToString
public class WeiBoRecommendList {public int id;public String name;public List<String> user;
}

步骤二、编写初始化数据到Redis中

    public final String COMMEND_KEY="recommend";public Object init() {ArrayList<WeiBoRecommendList> list = new ArrayList<>();for (int i=0; i<20; i++){WeiBoRecommendList item= new WeiBoRecommendList();item.setId(i);item.setName("榜单-"+i);Random random=new Random(1);ArrayList<String> users = new ArrayList<>();for (int index=0; index<3 ; index++){int userId = random.nextInt(10000);users.add("用户-"+userId);}item.setUser(users);list.add(item);}list.forEach( t -> redisTemplate.opsForSet().add(COMMEND_KEY,t) );return "init ok";}

步骤三、编写随机展示接口

@Api("微博推荐榜单")
@RestController
@RequestMapping("/wb")
public class WbRecommendController {@AutowiredWbRecommendService service;@PostMapping("/init")public Object init(){return service.init();}@PostMapping("/random")public Object random(Integer num) {Object random = service.random( num);return random;}@PostMapping("/getOne")public Object getOne() {Object random = service.getOne();return random;}
}@Service
public class WbRecommendService {@AutowiredRedisTemplate redisTemplate;public final String COMMEND_KEY="recommend";public Object init() {ArrayList<WeiBoRecommendList> list = new ArrayList<>();for (int i=0; i<20; i++){WeiBoRecommendList item= new WeiBoRecommendList();item.setId(i);item.setName("榜单-"+i);Random random=new Random(1);ArrayList<String> users = new ArrayList<>();for (int index=0; index<3 ; index++){int userId = random.nextInt(10000);users.add("用户-"+userId);}item.setUser(users);list.add(item);}list.forEach( t -> redisTemplate.opsForSet().add(COMMEND_KEY,t) );return "init ok";}public Object random(Integer num) {List list = redisTemplate.opsForSet().randomMembers(COMMEND_KEY, num);return list;}public Object getOne() {WeiBoRecommendList member = (WeiBoRecommendList) redisTemplate.opsForSet().randomMember(COMMEND_KEY);if (member != null){return member;}else {//TODO -->查询DBreturn null;}}
}

帖子点赞

一、微博点赞业务场景分析

梳理点赞的业务场景,它有2个接口:
第一个:点赞或取消点赞,用户点击功能
第二个接口:查看帖子信息:通过用户id 和帖子id,查看该帖子的点赞数、该用户是否点赞状态。

二、微博点赞的技术方案

点赞的关键技术就是要判断该用户是否点赞,已重复点赞的不允许再点赞,即过滤重复,虽然业务不复杂,可以采用数据库直接实现,
但是对于微博这种高并发的场景,不可能查数据库的,一般是缓存,即redis
第一个:点赞或取消点赞,用户点击功能采用的是redis的set数据结构,key=like:postid value={userid}

  • 采用sadd命令,添加点赞
127.0.0.1:6379> sadd like:1000 101
(integer) 1
127.0.0.1:6379> sadd like:1000 102
(integer) 1
127.0.0.1:6379> sadd like:1000 103
(integer) 1
127.0.0.1:6379> smembers like:1000
1) "101"
2) "102"
3) "103"
  • 采用srem命令,取消点赞
127.0.0.1:6379> srem like:1000 101
(integer) 1
127.0.0.1:6379> smembers like:1000
1) "102"
2) "103"
  • 第二个接口:查看帖子信息:通过用户id 和帖子id,查看该帖子的点赞数、该用户是否点赞状态。
    采用scard命令,点赞总数
127.0.0.1:6379> smembers like:1000
1) "102"
2) "103"
127.0.0.1:6379> scard like:1000
(integer) 2
  • 采用sismember命令,判断是否点赞
127.0.0.1:6379> smembers like:1000
1) "102"
2) "103"
127.0.0.1:6379> sismember like:1000 102
(integer) 1
127.0.0.1:6379> sismember like:1000 101
(integer) 0

三、SpringBoot+Redis 实现微博点赞

步骤1:点赞逻辑


@Service
public class LikeService {@AutowiredRedisTemplate redisTemplate;public final String postKey="post::like::";@Transactionalpublic Boolean postLike(Integer userId, Integer postId) {String key=postKey+postId;//TODO 异步插入数据库表;Boolean flag = redisTemplate.opsForSet().isMember(key, userId);if (!flag){redisTemplate.opsForSet().add(key,userId);return true;}else {return false;}}@Transactionalpublic Long postUnlike(Integer userId, Integer postId) {//TODO 异步删除数据表点赞记录;String key=postKey+postId;Long flag = redisTemplate.opsForSet().remove(key, userId);return flag;}@Transactionalpublic Map getPost(Integer postId, Integer userId) {String key=postKey+postId;// 获取帖子的内容 ==> Map map=redisTemplate.opsForHash().entries(key)//这里偏向Set的使用// 获取微博的阅读量==> Long readNum= redisTemplate.opsForValue().get(key)//点赞数Long likeSize = redisTemplate.opsForSet().size(key);//用户是否点赞Boolean isLike = redisTemplate.opsForSet().isMember(key, userId);HashMap<String, Object> post = new HashMap<>();post.put("isLike",isLike);post.put("likeSize",likeSize);//post.put("title",map.get("title");//post.put("content",map.get("content");//post.put("readNum",readNum);return  post;}
}//接口类@Api("用户点赞")
@RestController
@RequestMapping("/like")
public class LikeController {@AutowiredLikeService likeService;@PostMapping("/postLike")public Object postLike(Integer userId , Integer postId){Boolean f = likeService.postLike(userId, postId);if (f){return "post OK";}else {return "已经点赞了,无法重复点了";}}@PostMapping("/postUnlike")public Object postUnlike(Integer userId , Integer postId){Long f = likeService.postUnlike(userId, postId);if (f > 0){return "unlike OK";}else {return "你还没有点赞或者重复点赞";}}@PostMapping("/getPost")public Object getPost(Integer postId,Integer userId){Map f = likeService.getPost(postId,userId);if (f!=null){return f;}else {//TODO :去DB查询,查询后插入redis;// likeService.getPostFromDB(postId,userId);return null;}}
}

关注与粉丝

一、微博关注与粉丝的业务场景分析

我关注了雷军:我就是雷军的粉丝follower;
雷军被阿甘关注:雷军就是阿甘的关注followee;

二、微博关注与粉丝的redis技术方案

技术方案:每个用户都有2个集合:关注集合和粉丝集合
例如 我关注了雷军,做了2个动作

1.把我的userid加入雷军的粉丝follower集合set
2.把雷军的userid加入我的关注followee集合set
集合key设计
我的关注集合 key=followee:我的userid
雷军的粉丝集合 key=follower:雷军userid

SpringBoot+Redis 实现微博关注与粉丝

功能1:关注

    /*** 我关注了雷军,我即使雷军的粉丝 follower* userId=我* followeeId=雷军*/@ApiOperation(value="关注")@PostMapping(value = "/follow")public void follow(Integer userId,Integer followeeId)   {relationService.follow(userId,followeeId);}
 /*** 我关注了雷军*/public void follow(Integer userId,Integer followeeId){SetOperations<String, Integer> opsForSet = redisTemplate.opsForSet();//我的关注集合String followeekey = Constants.CACHE_KEY_FOLLOWEE + userId;//把雷军的followeeId,加入我的关注集合中opsForSet.add(followeekey,followeeId);//TODO:异步插入数据库DB;//雷军的粉丝集合String followerkey=Constants.CACHE_KEY_FOLLOWER+followeeId;//把我的userid加入雷军的粉丝follower集合setopsForSet.add(followerkey,userId);//TODO:异步插入数据库DB;}

功能2:我的关注

    @ApiOperation(value="查看我的关注")@GetMapping(value = "/myFollowee")public List<UserVO> myFollowee(Integer userId){return this.relationService.myFollowee(userId);}/*** 查看我的关注*/public List<UserVO> myFollowee(Integer userId){SetOperations<String, Integer> opsForSet = redisTemplate.opsForSet();//关注集合String followeekey = Constants.CACHE_KEY_FOLLOWEE + userId;Set<Integer> sets= opsForSet.members(followeekey);//查询不到-->就去DB查询;return this.getUserInfo(sets);}

功能3:我的粉丝

    @ApiOperation(value="查看我的粉丝")@GetMapping(value = "/myFollower")public List<UserVO> myFollower(Integer userId){return this.relationService.myFollower(userId);}
    /*** 查看我的粉丝*/public List<UserVO> myFollower(Integer userId){SetOperations<String, Integer> opsForSet = redisTemplate.opsForSet();//粉丝集合String followerkey=Constants.CACHE_KEY_FOLLOWER+userId;Set<Integer> sets= opsForSet.members(followerkey);//sets==null,查询不到就去DB查return this.getUserInfo(sets);}

功能4:获取用户信息

  private Object getUserInfo(Collection<Integer> members) {ArrayList<Object> list = new ArrayList<>();for (Integer userId : members){Map map = redisTemplate.opsForHash().entries(USER_KEY + userId);User user = new User();user.setUsername((String) map.get("username"));user.setSex((Integer) map.get("sex"));user.setId((Integer) map.get("id"));list.add(user);}return list;}

微关系计算

一、计算好友关系业务场景分析

微博微关系:
共同关注:是计算出我和雷军共同关注的人有哪些?
我关注的人也关注他:是计算出我关注的人群中,有哪些人同时和我一样关注了雷军

二、计算好友关系的redis技术方案

思考题:如果是采用数据库来实现用户的关系,一般SQL怎么写? 例如 阿甘关注10个人,雷军关注100个人,让你计算2人的共同关注那些人?

SQL的写法,一般是采用in 或 not in 来实现。但是对于互联网高并发的系统来说,in not in 明显不适合。
一般的做法是采用redis的set集合来实现。
Redis Set数据结构,非常适合存储好友、关注、粉丝、感兴趣的人的集合。然后采用set的命令就能得出我们想要的数据。
1. sinter命令:获得A和B两个用户的共同好友
2. sismember命令:判断C是否为B的好友
3. scard命令:获取好友数量

SpringBoot+Redis 计算微博好友关系

功能一:共同关注

@ApiOperation(value="求2个用户的关注交集")
@GetMapping(value = "/intersect")
public List<UserVO> intersect(Integer userId1, Integer userId2){return  this.relationService.intersect(userId1,userId2);
}
public List<UserVO> intersect(Integer userId1,Integer userId2){SetOperations<String, Integer> opsForSet = redisTemplate.opsForSet();String followeekey1 = Constants.CACHE_KEY_FOLLOWEE + userId1;String followeekey2 = Constants.CACHE_KEY_FOLLOWEE + userId2;//求2个集合的交集Set<Integer> sets= opsForSet.intersect(followeekey1,followeekey2);return this.getUserInfo(sets);
}

我关注的人也关注他

    @ApiOperation("我关注的人也关注他")@PostMapping("/getRelation2")public Object getRelation2(Integer userId, Integer otherUserId){return service.getRelation2(userId,otherUserId);}@Transactionalpublic Object getRelation2(Integer userId, Integer otherUserId) {String concernKey=CONCERN_KEY+userId;Set<Integer> concernList = redisTemplate.opsForSet().members(concernKey);List<Integer> resultIds = new ArrayList<>();for (Integer concernId : concernList){Boolean flag = redisTemplate.opsForSet().isMember(CONCERN_KEY + concernId, otherUserId);if (flag){resultIds.add(concernId);}}return this.getUserInfo(resultIds);}

HyperLogLog

命令使用

HyperLogLog 命令详解

HyperLogLog 目前只支持3个命令,PFADD、PFCOUNT、PFMERGE

PFADD

将元素加入到HyperLogLog数据结构中,如果 HyperLogLog 的基数估算值在命令执行之后出现了变化,那么命令返回1,否则返回0。

PFCOUNT

返回给定 HyperLogLog 的基数估算值。

127.0.0.1:6379> pfadd uv 192.168.1.100 192.168.1.101 192.168.1.102 192.168.1.103
(integer) 1
127.0.0.1:6379> pfadd uv 192.168.1.100
(integer) 0
127.0.0.1:6379> pfcount uv
(integer) 4

PFMERGE

把多个HyperLogLog合并为一个HyperLogLog,合并后的HyperLogLog的基数是通过所有的HyperLogLog进行并集后,得出来的。

127.0.0.1:6379> pfadd uv1 192.168.1.100 192.168.1.101 192.168.1.102 192.168.1.103
(integer) 1
127.0.0.1:6379> pfcount uv1
(integer) 4
127.0.0.1:6379> pfadd uv2 192.168.1.100 192.168.2.101 192.168.2.102 192.168.2.103
(integer) 1
127.0.0.1:6379> pfadd uv3 192.168.1.100 192.168.3.101 192.168.3.102 192.168.3.103
(integer) 1
127.0.0.1:6379> pfmerge aganuv uv1 uv2 uv3
OK
127.0.0.1:6379> pfcount aganuv
(integer) 10

这个命令很重要,例如你可以统计一周 或 一个月的uv 就可以使用此命令来轻易实现。

基于Redis的UV计算

步骤1:模拟UV访问

@Service
@Slf4j
public class TaskService {@Autowiredprivate RedisTemplate redisTemplate;/***模拟UV访问*/@PostConstructpublic void init(){log.info("模拟UV访问 ..........");new Thread(()->this.refreshData()).start();}/*** 刷新当天的统计数据*/public void refreshDay(){Random rand = new Random();String ip=rand.nextInt(999)+"."+rand.nextInt(999)+"."+rand.nextInt(999)+"."+rand.nextInt(999);//redis 命令 pfaddlong n=this.redisTemplate.opsForHyperLogLog().add(Constants.PV_KEY,ip);log.debug("ip={} , returen={}",ip,n);}/*** 按天模拟UV统计*/public void refreshData(){while (true){this.refreshDay();//TODO 在分布式系统中,建议用xxljob来实现定时try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}}}}

步骤2:实现UV统计功能

@RestController
@Slf4j
public class Controller {@Autowiredprivate RedisTemplate redisTemplate;@GetMapping(value = "/uv")public long uv() {//redis命令 pfcountlong size=this.redisTemplate.opsForHyperLogLog().size(Constants.PV_KEY);return size;}}

Redis数据结构Set应用场景--黑名单校验器、京东与支付宝抽奖、微博榜单与QQ群的随机展示、帖子点赞、关注与粉丝、微关系计算、HyperLogLog的入门使用相关推荐

  1. redis数据结构及使用场景

    redis数据结构及使用场景 1.字符串(String) String是最常用的一种数据类型,普通的k/v存储都可以归为此类.redis是使用C语言开发,但C中并没有字符串类型,只能使用指针或符数组的 ...

  2. 玩玩Redis系列(八)--redis数据结构及使用场景

    redis数据结构及使用场景 数据结构 String 相关命令 使用场景 List 相关命令 使用场景 Set 相关命令 使用场景 Hash 相关命令 使用场景 ZSet 相关命令 使用场景 Hype ...

  3. Redis数据结构Hash应用场景-存储商品、购物车、淘宝短链接、分布式Session、用户注册、发微博功能

    Hash应用场景 Hash Hash应用场景 redis存储java对象常用String,那为什么还要用hash来存储? SpringBoot+redis+hash存储商品数据 短链接 场景1:淘宝短 ...

  4. 【带你重拾Redis】Redis数据结构及使用场景

    Redis数据结构 Redis有着非常丰富的数据结构,这些数据结构可以满足非常多的应用场景, 如果对这些数据结构有一个比较清晰的认知,使用Redis也会更加得心应手. Redis主要支持以下数据结构: ...

  5. redis数据结构及其应用场景

    redis 五大数据结构 redis中存储数据是以key-value的形式去存储的,其中key为String字符串类型,value的数据类型有string.hash.list.set.zset等这五种 ...

  6. 六大真实场景赛题,ECV2022极市计算机视觉开发者榜单大赛预报名开启

    算法江湖,风云再起 强者集结,大战在即 如果你算力超群又有一颗勇于挑战的心 速来参加ECV2022极市计算机视觉开发者榜单大赛,一展身手! 进入ECV2022官网报名:http://mtw.so/5N ...

  7. redis 获取所有的key_在微博微信场景下学习Redis数据结构

    Redis安装 下载地址:http://redis.io/download安装步骤:1.yum install gcc2.wget http://download.redis.io/releases/ ...

  8. Redis核心数据结构List应用场景-商品列表、缓存击穿、PV阅读量、抢红包、推送帖子、普通分布式锁、Redis可重入锁与红锁

    List应用场景 Redis之List 一. Redis list命令实战 二.商品列表 高并发的淘宝聚划算实现技术方案 SpringBoot+Redis实现商品列表功能 二.缓存击穿 什么是缓存击穿 ...

  9. Redis五种数据结构及其使用场景

    Redis五种数据结构及其使用场景 先有个概念,redis 数据库其实就是一个大的 map,它容纳了所有的 key, key 都是 string 类型,而 value 则有 string, list, ...

最新文章

  1. Linux命令CURL用法
  2. oracle之 11g RAC R2 体系结构---Grid
  3. 科大星云诗社动态20220106
  4. 高等数学下-赵立军-北京大学出版社-题解-练习10.2
  5. 块元素与行内元素转化(display属性)
  6. 计算机怎么禁用软件网络访问,Windows10系统下禁止软件联网的两种方法
  7. java 扫描tcp端口号_Java 端口扫描器 TCP的实现方法
  8. 学生管理系统IPO图_关于继续开展2019年度辅导员工作考核学生评议的通知
  9. 目标追踪拍摄?目标遮挡拍摄?拥有19亿安装量的花瓣app,究竟有什么别出心裁的功能如此吸引用户?
  10. 带你走进神经网络的“前世今生”
  11. 解决github官网进不去
  12. 武汉的二本计算机学校有哪些,武汉二本大学有哪些学校
  13. Educational Codeforces Round 117 (Rated for Div. 2)题解(A~D)
  14. 李成名:科学就是较真 数字城市/智慧城市就是跑马圈地
  15. ioctl(sock, SIOCGIFHWADDR, ifr)获取网卡mac地址
  16. 数字麦克风PDM信号采集与STM32 I2S接口应用(一)
  17. 使用向量叉乘判断点是否在三角形内
  18. 塔望食品品牌策划:预制菜主要竞争品牌分析及研究
  19. 区块链平台免费试用版上新功能了!
  20. 操作系统 —— (内存)内存管理

热门文章

  1. Twitter新任80后印度裔CEO,为啥是他?
  2. 汇编中esp和ebp在函数栈空间的保存和变化 call的参数和局部变量的关系详解
  3. feifei_about
  4. 后台ui大全(有这些你就够了)
  5. 太少的相濡以沫,太多的相忘江湖
  6. 查询vue版本、脚手架版本
  7. 华为2288XV5服务器配置RAID,安装ESXI虚拟机
  8. 利用OneDrive网盘建站
  9. Linux I2C驱动
  10. Excel行高列宽使用单位为磅(1cm=28.6磅)