1️⃣业务场景分析
- 关注微博
- 登录首页展示了我关注的所有人发的微博,展示形式是列表
- 滚动有分页加载
2.个人微博
- 我发的微博展示在个人微博,展示形式也是列表
- 滚动有分页加载
2️⃣ 基于redis技术方案
发微博
文章
个人微博
关注微博
用户
服务器
采用map结构保存
采用list结构保存文章id
也采用list结构保存文章id
- 关注微博和个人微博的展示形式都是列表,用redis的
list
结构存储。 - 滚动有分页加载,用redis的
lrange key start stop
命令范围查询元素。
问题: 每个人都有2个list:一个是个人list,一个是关注list,而这2个list每次发微博都会push到个人list和关注list,这2个list存在性能的问题,就是这个list无线增长。时间久了redis数据持续膨胀。
最有效的解决方法是限制次数,常见的场景是
- qq群,微信群限定人数
- 百度搜索限定了搜索出来75页
- 限定个人微博和关注微博的长度为1000,即,发微博的时候,往个人list和关注list push完成后,把list的长度剪切为1000,具体的技术方案采用list 的ltrim命令来实现。
ltrim key start end
截取队列指定区间的元素,其余元素都删除
3️⃣代码编写
✏️ 发微博,写入个人主页
/**
* push到个人主页
*/
public void pushHomeList(Integer userId,Integer postId){
//个人微博列表key
String key="myPostBox:list:"+userId;
//把该微博文章id推送到队列
this.redisTemplate.opsForList().leftPush(key,postId);
//限定长度,防止膨胀,截取前1000条
if(this.redisTemplate.opsForList().size(key)>1000){
this.redisTemplate.opsForList().trim(key,0,1000);
}
}
复制代码
✏️ 发微博,批量推送给粉丝
/**
* 发一条微博,批量推送给所有粉丝
*/
private void pushFollower(int userId,int postId){
SetOperations<String, Integer> opsForSet = redisTemplate.opsForSet();
//读取粉丝集合,粉丝集合用set保存
String followerkey=Constants.CACHE_KEY_FOLLOWER+userId;
//千万不能取set集合的所有数据,如果数据量大的话,会卡死
// Set<Integer> sets= opsForSet.members(followerkey);
Cursor<Integer> cursor = opsForSet.scan(followerkey, ScanOptions.NONE);
try{
while (cursor.hasNext()){
//拿出粉丝的userid
Integer object = cursor.next();
//该粉丝的关注列表key
String key= "myAttentionBox:list:"+object;
//把该文章添加到队列
this.redisTemplate.opsForList().leftPush(key,postId);
}
}catch (Exception ex){
log.error("",ex);
}finally {
try {
cursor.close();
} catch (IOException e) {
log.error("",e);
}
}
}
复制代码
✏️ 查看个人列表
/**
* 获取个人主页列表
*/
public PageResult<Content> homeList(Integer userId,int page, int size){
//分页
PageResult<Content> pageResult=new PageResult();
List<Integer> list=null;
long start = (page - 1) * size;
long end = start + size - 1;
try {
String key= "myPostBox:list:"+userId;
//1.查询用户的总数
int total=this.redisTemplate.opsForList().size(key).intValue();
//设置总数
pageResult.setTotal(total);
//2.采用redis list数据结构的lrange命令实现分页查询。
list = this.redisTemplate.opsForList().range(key, start, end);
//3.根据文章id去redis查询明细,redis没有就去db拿
List<Content> contents=this.getContents(list);
pageResult.setRows(contents);
}catch (Exception e){
log.error("异常",e);
}
return pageResult;
}
复制代码
✏️ 查看关注列表
/**
* 获取关注列表
*/
public PageResult<Content> attentionList(Integer userId,int page, int size){
PageResult<Content> pageResult=new PageResult();
List<Integer> list=null;
long start = (page - 1) * size;
long end = start + size - 1;
try {
String key= "myAttentionBox:list:"+userId;
//1.设置总数
int total=this.redisTemplate.opsForList().size(key).intValue();
pageResult.setTotal(total);
//2.采用redis,list数据结构的lrange命令实现分页查询。
list = this.redisTemplate.opsForList().range(key, start, end);
//3.根据文章id去redis查询明细,redis没有就去db拿
List<Content> contents=this.getContents(list);
pageResult.setRows(contents);
}catch (Exception e){
log.error("异常",e);
}
return pageResult;
}
复制代码
⛳redis分布式缓存系列文章
上一章节: redis分布式缓存(三十)一一 🚀二级缓存的高并发文章PV解决方案
- 👍🏻:有收获的,点赞鼓励!
- ❤️:收藏文章,方便回看!
- 💬:评论交流,互相进步!