当前位置: 首页 > wzjs >正文

怎么做网站注册推广模板网站自助建站

怎么做网站注册推广,模板网站自助建站,做网站怎么赚钱 111,学校网站前置审批前言 为什么所用 redis 作为缓存来实现点赞服务, 而不是直接就使用 mysql 来完成? 使用 Redis 的集合数据结构来存储点赞用户的 ID,方便快速判断用户是否已点赞; 当用户频繁的点赞和取消点赞时, 无需操作数据库, 减轻服务器压力 Redis 可以承受高并发的读写操作…

前言

为什么所用 redis 作为缓存来实现点赞服务, 而不是直接就使用 mysql 来完成?

  1. 使用 Redis 的集合数据结构来存储点赞用户的 ID,方便快速判断用户是否已点赞;

  2. 当用户频繁的点赞和取消点赞时, 无需操作数据库, 减轻服务器压力

  3. Redis 可以承受高并发的读写操作。当大量用户同时点赞时,Redis 可以先将这些点赞请求缓存起来,然后由后台线程逐步将数据持久化到 MySQL

实现

查询流程

客户端
API Gateway
点赞服务
Redis缓存
MySQL持久化

数据存储格式

redis

# 存储用户点赞状态(SET)
SADD picture_likes:{picture_id} {user_id}  # 用户点赞图片
SREM picture_likes:{picture_id} {user_id}  # 取消点赞# 存储点赞计数器(String)
INCR picture_like_count:{picture_id}       # 点赞数+1
DECR picture_like_count:{picture_id}       # 点赞数-1# 存储用户点赞历史(ZSET)用于做增量同步
ZADD user_likes:{picture_id} {timestamp} {user_id}  # 按时间排序

mysql

下面是用户点赞关系表, 至于 likesCount 则作为字段添加进 picture 表

create table user_likes
(id           bigint auto_incrementprimary key,user_id      bigint                              not null,picture_id   bigint                              not null,created_time timestamp default CURRENT_TIMESTAMP not null,constraint uk_user_pictureunique (user_id, picture_id)
);

核心代码实现

点赞或取消点赞

相关参数构建

根据自己实际情况来

Long userId = userService.getLoginUser().getId();
String key = PICTURE_LIKE_PREFIX + request.getPictureId();
String userIdStr = userId.toString();
String likeCountKey = PICTURE_LIKE_COUNT_PREFIX + request.getPictureId();
String likeTimeKey = PICTURE_LIKE_TIME_PREFIX + request.getPictureId();// 查询用户是否已经点赞
boolean isLiked = Boolean.TRUE.equals(stringRedisTemplate.opsForSet().isMember(key, userId.toString())
);
操作 redis

为了防止指令穿插导致不一致性, 需要保证操作的原子性, 这里使用 管道 + MULTI 和 EXEC 命令来保证原子性和隔离性(也可以使用 lua 脚本来完成)

stringRedisTemplate.executePipelined((RedisCallback<Object>) connection -> {connection.multi(); // 开启事务if (!isLiked) {// 记录用户点赞关系connection.sAdd(key.getBytes(), userIdStr.getBytes());// 记录用户点赞时间connection.zAdd(likeTimeKey.getBytes(), System.currentTimeMillis() / 1000.0, userIdStr.getBytes());// 增加点赞计数connection.incr(likeCountKey.getBytes());} else {// 移除用户点赞关系connection.sRem(key.getBytes(), userIdStr.getBytes());// 用处用户赞时间connection.zRem(likeTimeKey.getBytes(), userIdStr.getBytes());// 减少点赞计数connection.decr(likeCountKey.getBytes());}return connection.exec();
});

补充: redis 的事务机制

当使用事务来执行多个指令时,通过 MULTI 命令开启事务,将多个指令放入一个事务中,然后使用 EXEC 命令来原子性地执行这些指令。在 EXEC 执行期间,Redis 会按照顺序依次执行事务中的指令,不会被其他客户端的命令打断,从而保证了事务内指令执行的原子性和顺序性,也就确保了两条指令执行的间隔内没有其他指令被执行。

查询点赞数据

这里需要查询出用户是否给图片点赞, 以及图片对应的点赞总数

public List<LikeInfoVO> batchCheckLikeStatus(Long userId, List<Long> pictureIds) {// 1. 使用Pipeline同时查询两种数据List<Object> results = stringRedisTemplate.executePipelined((RedisCallback<Object>) connection -> {// 查询用户点赞状态for (Long picId : pictureIds) {connection.sIsMember((PICTURE_LIKE_PREFIX + picId).getBytes(),userId.toString().getBytes());}// 查询点赞总数// 未匹配的项会返回空for (Long picId : pictureIds) {connection.get((PICTURE_LIKE_COUNT_PREFIX + picId).getBytes());}return null;});// 2. 处理混合结果List<LikeInfoVO> resultMap = new ArrayList<>();int halfSize = results.size() / 2;for (int i = 0; i < halfSize; i++) {Boolean isLiked = (Boolean) results.get(i);Integer likeCount = results.get(i + halfSize) != null ?Integer.parseInt((String) results.get(i + halfSize)) : 0;resultMap.add(new LikeInfoVO(isLiked, likeCount));}return resultMap;
}

将点赞数据封装进响应结果

List<LikeInfoVO> likeStatus = batchCheckLikeStatus(userId, pictureIds);List<PictureVO> vos = IntStream.range(0, records.size()).mapToObj(i -> {PictureVO pictureVO = new PictureVO();BeanUtils.copyProperties(records.get(i), pictureVO);pictureVO.setLike(likeStatus.get(i).getIsLike());pictureVO.setLikesCount(likeStatus.get(i).getLikesCount());return pictureVO;
}).collect(Collectors.toList());

数据同步

增量同步 or 全量同步

使用定时任务来完成将 redis 中数据同步到 mysql, 这里就不使用全量同步了, 一方面全量同步性能底, 再未处理 redis 删除的点赞关系

代码

@Scheduled(fixedRate = 300000)
public void syncIncrementally() {log.info("开始: 增量同步图片用户点赞关系到数据库, 并处理Redis已删除的点赞关系");// 1. 获取Redis最后同步时间戳long lastSyncTime = getLastSyncTimestamp();// 2. 同步新增点赞syncNewLikes(lastSyncTime);// 3. 同步取消点赞syncUnlikes();// 5. 同步图片点赞数量syncPicLikesCount();// 4. 更新同步时间updateSyncTimestamp();log.info("结束: 增量同步图片用户点赞关系到数据库, 并处理Redis已删除的点赞关系");
}
设置同步时间和获取最后同步时间
// 获取最后同步时间(默认返回24小时前的时间戳)
private long getLastSyncTimestamp() {String timestampStr = stringRedisTemplate.opsForValue().get(LAST_SYNC_KEY);return timestampStr != null ?Long.parseLong(timestampStr) :System.currentTimeMillis() - 86400_000; // 默认24小时前
}// 更新同步时间戳为当前时间
private void updateSyncTimestamp() {stringRedisTemplate.opsForValue().set(LAST_SYNC_KEY,String.valueOf(System.currentTimeMillis()));
}
同步新增点赞
private void syncNewLikes(long sinceTime) {// 使用ZSET记录点赞时间戳Set<String> newLikeKeys = stringRedisTemplate.keys(PICTURE_LIKE_PREFIX + "*");Objects.requireNonNull(newLikeKeys).forEach(key -> {long pictureId = Long.parseLong(StrUtil.removePrefix(key, PICTURE_LIKE_PREFIX));// 获取新增点赞用户(ZRANGEBYSCORE)Set<String> newUserIds = stringRedisTemplate.opsForZSet().rangeByScore(PICTURE_LIKE_TIME_PREFIX + pictureId, sinceTime, Double.MAX_VALUE);List<UserLikes> entityList = Objects.requireNonNull(newUserIds).stream().map(userId -> {UserLikes userLikes = new UserLikes();userLikes.setPictureId(pictureId);userLikes.setUserId(Long.parseLong(userId));return userLikes;}).collect(Collectors.toList());// 批量插入并忽略重复的元素if (CollUtil.isNotEmpty(entityList)) {userLikesMapper.insertIgnoreBatch(entityList);}});
}
同步取消点赞
private void syncUnlikes() {Set<String> likeKeys = stringRedisTemplate.keys(PICTURE_LIKE_PREFIX + "*");Objects.requireNonNull(likeKeys).forEach(key -> {Long pictureId = Long.parseLong(key.substring(PICTURE_LIKE_PREFIX.length()));Set<String> members = stringRedisTemplate.opsForSet().members(key);Set<Long> redisUserIds = Objects.requireNonNull(members).stream().map(Long::parseLong).collect(Collectors.toSet());QueryWrapper<UserLikes> wrapper = new QueryWrapper<>();wrapper.select("user_id").eq("picture_id", pictureId);Set<Long> mysqlUserIds = userLikesMapper.selectList(wrapper).stream().map(UserLikes::getUserId).collect(Collectors.toSet());// 找出MySQL有但Redis没有的记录mysqlUserIds.removeAll(redisUserIds);if (CollUtil.isNotEmpty(mysqlUserIds)) {UpdateWrapper<UserLikes> userLikesUpdateWrapper = new UpdateWrapper<>();userLikesUpdateWrapper.eq("picture_id", pictureId).in("user_id", mysqlUserIds);userLikesMapper.delete(userLikesUpdateWrapper);}});
}

文章转载自:

http://Y2KFACAf.gygjk.cn
http://cH7rvRHp.gygjk.cn
http://a4iP2AKd.gygjk.cn
http://Ti9bfhNH.gygjk.cn
http://jQZ5g0pb.gygjk.cn
http://w3ShLuk3.gygjk.cn
http://KuzGskFd.gygjk.cn
http://L45nF2ky.gygjk.cn
http://hUAA2MTL.gygjk.cn
http://EaMJhTYS.gygjk.cn
http://K9eXCWL9.gygjk.cn
http://UkB6FN7N.gygjk.cn
http://sCdRDxks.gygjk.cn
http://dS1u7Kg7.gygjk.cn
http://39hLdQEh.gygjk.cn
http://nnXdPF2i.gygjk.cn
http://9F4fl2sM.gygjk.cn
http://hnr17tNy.gygjk.cn
http://Hy3hPNMu.gygjk.cn
http://MVPsiV5P.gygjk.cn
http://JBgnvvoT.gygjk.cn
http://2zvqBcLw.gygjk.cn
http://8SvTscGb.gygjk.cn
http://lftX9e6q.gygjk.cn
http://YwyLgLKa.gygjk.cn
http://1GhU0KKd.gygjk.cn
http://cZMlSkCa.gygjk.cn
http://W9P97IBN.gygjk.cn
http://7vmwWwcQ.gygjk.cn
http://kNmssWPT.gygjk.cn
http://www.dtcms.com/wzjs/714956.html

相关文章:

  • 有什么好的网站做旅行计划施工企业招标领导小组组长的职责
  • 网站后台教程福建省城乡建设官方网站
  • 免费下载ppt模板网站哪个好做网站怎么安装数据库
  • 自营店网站建设移动互联网应用程序个人信息保护管理暂行规定
  • 网站建设服务商有哪些上海做网站推荐
  • 郑州公共住宅建设投资有限公司网站如何建设网站视频
  • 吴江网站建设收费广告软文营销平台
  • 恩做网站动态页面好南昌网站建设方案服务
  • 七牛直播网站怎么做宁波本地模板网站建设平台
  • 公司网站如何注册宜兴做网站的公司
  • 显示网站目录wordpress开发sns
  • 不备案的网站能打开吗wordpress情侣
  • 惠州电商网站建设中国制造网外贸网网站
  • 杭州 网站开发公司门户网站建设工作方案
  • 怎样用云服务器做网站super cache wordpress
  • 音乐网站的色彩搭配仿百度 wordpress
  • 企业网站后台管理模板wordpress登录返回
  • wordpress漫画站简约网站欣赏
  • 网站建设能引流网站建设
  • 福州网站建设发布网站运营建设方案
  • 专业网站开发公司nike建设网站的目的
  • 网站模板 首饰预定网站后台栏目根据什么做的
  • 一个最简单的产品展示的asp网站应该如何做教育咨询
  • 企业网站找谁做优化关键词软件
  • 一个网站的二级目录在另一台服务器上_怎么做17做网店广州货源网
  • 大庆油田建设集团网站零代码自助建站平台
  • 上海网站建设__永灿品牌建站国外购买域名网站
  • 如何购买虚拟主机做网站网站建设 指标
  • 如何用自己电脑做网站服务器营销型企业网站的功能有哪些
  • 网站开发建设准备工作企业网站手机版模板