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

虚拟主机网站网站建设seo

虚拟主机网站,网站建设seo,江苏网站建设开发,自己建网站怎么赚钱学习记录-使用Redis合并写请求来优化性能 1.业务背景 学习进度的统计功能:为了更精确的记录用户上一次播放的进度,采用的方案是:前端每隔15秒就发起一次请求,将播放记录写入数据库。但问题是,提交播放记录的业务太复杂了&#x…

学习记录-使用Redis合并写请求来优化性能

1.业务背景

学习进度的统计功能:为了更精确的记录用户上一次播放的进度,采用的方案是:前端每隔15秒就发起一次请求,将播放记录写入数据库。但问题是,提交播放记录的业务太复杂了,其中涉及到大量的数据库操作

在这里插入图片描述

2.解决方案思路

如图:
在这里插入图片描述

由于数据都缓存到Redis了,积累一些数据后再批量写入数据库,这样数据库的写频率、写次数都大大减少,对数据库压力小了非常多!

优点:

  • 写缓存速度快,响应时间大大减少
  • 降低数据库的写频率和写次数,大大减轻数据库压力

缺点:

  • 实现相对复杂
  • 依赖Redis可靠性
  • 不支持事务和复杂业务

场景:

  • 写频率较高、写业务相对简单的场景

3.持久化思路

对于合并写请求方案,一定有一个步骤就是持久化缓存数据到数据库。一般采用的是定时任务持久化:

但是定时任务的持久化方式在播放进度记录业务中存在一些问题,主要就是时效性问题。

  • 假如定时任务间隔较短,例如20秒一次,对数据库的更新频率太高,压力太大
  • 假如定时任务间隔较长,例如2分钟一次,更新频率较低,时效性可能超过2分钟,不满足需求

在学习记录统计场景下有什么办法能够在不增加数据库压力的情况下,保证时间误差较低吗?

假如一个视频时长为20分钟,我们从头播放至15分钟关闭,每隔15秒提交一次播放进度,大概需要提交60次请求。

但是下一次我们再次打开该视频续播的时候,肯定是从最后一次提交的播放进度来续播。也就是说续播进度之前的N次播放进度都是没有意义的,都会被覆盖。既然如此,完全没有必要定期把这些播放进度写到数据库,只需要将用户最后一次提交的播放进度写入数据库即可。

只要能判断Redis中的播放进度是否变化即可。怎么判断呢?

每当前端提交播放记录时,我们可以设置一个延迟任务并保存这次提交的进度。等待20秒后(因为前端每15秒提交一次,20秒就是等待下一次提交),检查Redis中的缓存的进度与任务中的进度是否一致。

  • 不一致:说明持续在提交,无需处理
  • 一致:说明是最后一次提交,更新学习记录、更新课表最近学习小节和时间到数据库中

4.延迟任务方案对比

DelayQueueRedissonMQ时间轮
原理JDK自带延迟队列,基于阻塞队列实现。基于Redis数据结构模拟JDK的DelayQueue实现利用MQ的特性。例如RabbitMQ的死信队列时间轮算法
优点不依赖第三方服务分布式系统下可用不占用JVM内存分布式系统下可以不占用JVM内存不依赖第三方服务性能优异
缺点占用JVM内存只能单机使用依赖第三方服务依赖第三方服务只能单机使用

以上四种方案都可以解决问题,不过本例中我们会使用DelayQueue方案。因为这种方案使用成本最低,而且不依赖任何第三方服务,减少了网络交互。

但缺点也很明显,就是需要占用JVM内存,在数据量非常大的情况下可能会有问题。但考虑到任务存储时间比较短(只有20秒),因此也可以接收。

如果你们的数据量非常大,DelayQueue不能满足业务需求,大家也可以替换为其它延迟队列方式,例如Redisson、MQ等

5.Redis数据结构设计

一方面我们要缓存写数据,减少写数据库频率;另一方面我们要缓存播放记录,减少查询数据库。因此,缓存中至少要包含3个字段:

  • 记录id:id,用于根据id更新数据库
  • 播放进度:moment,用于缓存播放进度
  • 播放状态(是否学完):finished,用于判断是否是第一次学完

课程有很多,每个课程的小节也非常多。每个小节都是一个独立的KEY,需要创建的KEY也会非常多,浪费大量内存。可以把一个课程的多个小节作为一个KEY来缓存

在这里插入图片描述

6.代码实现

6.1定义延迟任务类

@Data
public class DelayTask<D> implements Delayed {private D data;private long deadlineNanos;public DelayTask(D data, Duration delayTime) {this.data = data;this.deadlineNanos = System.nanoTime() + delayTime.toNanos();}@Overridepublic long getDelay(TimeUnit unit) {return unit.convert(Math.max(0, deadlineNanos - System.nanoTime()), TimeUnit.NANOSECONDS);}@Overridepublic int compareTo(Delayed o) {long l = getDelay(TimeUnit.NANOSECONDS) - o.getDelay(TimeUnit.NANOSECONDS);if(l > 0){return 1;}else if(l < 0){return -1;}else {return 0;}}
}

6.2定义延迟任务处理类

package com.tianji.learning.utils;import com.tianji.common.utils.JsonUtils;
import com.tianji.common.utils.StringUtils;
import com.tianji.learning.domain.po.LearningLesson;
import com.tianji.learning.domain.po.LearningRecord;
import com.tianji.learning.mapper.LearningRecordMapper;
import com.tianji.learning.service.ILearningLessonService;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.DelayQueue;@Slf4j
@Component
@RequiredArgsConstructor
public class LearningRecordDelayTaskHandler {private final StringRedisTemplate redisTemplate;private final LearningRecordMapper recordMapper;private final ILearningLessonService lessonService;private final DelayQueue<DelayTask<RecordTaskData>> queue = new DelayQueue<>();private final static String RECORD_KEY_TEMPLATE = "learning:record:{}";private static volatile boolean begin = true;@PostConstructpublic void init(){CompletableFuture.runAsync(this::handleDelayTask);}@PreDestroypublic void destroy(){begin = false;log.debug("延迟任务停止执行!");}public void handleDelayTask(){while (begin) {try {// 1.获取到期的延迟任务DelayTask<RecordTaskData> task = queue.take();RecordTaskData data = task.getData();// 2.查询Redis缓存LearningRecord record = readRecordCache(data.getLessonId(), data.getSectionId());if (record == null) {continue;}// 3.比较数据,moment值if(!Objects.equals(data.getMoment(), record.getMoment())) {// 不一致,说明用户还在持续提交播放进度,放弃旧数据continue;}// 4.一致,持久化播放进度数据到数据库// 4.1.更新学习记录的momentrecord.setFinished(null);recordMapper.updateById(record);// 4.2.更新课表最近学习信息LearningLesson lesson = new LearningLesson();lesson.setId(data.getLessonId());lesson.setLatestSectionId(data.getSectionId());lesson.setLatestLearnTime(LocalDateTime.now());lessonService.updateById(lesson);} catch (Exception e) {log.error("处理延迟任务发生异常", e);}}}public void addLearningRecordTask(LearningRecord record){// 1.添加数据到Redis缓存writeRecordCache(record);// 2.提交延迟任务到延迟队列 DelayQueuequeue.add(new DelayTask<>(new RecordTaskData(record), Duration.ofSeconds(20)));}public void writeRecordCache(LearningRecord record) {log.debug("更新学习记录的缓存数据");try {// 1.数据转换String json = JsonUtils.toJsonStr(new RecordCacheData(record));// 2.写入RedisString key = StringUtils.format(RECORD_KEY_TEMPLATE, record.getLessonId());redisTemplate.opsForHash().put(key, record.getSectionId().toString(), json);// 3.添加缓存过期时间redisTemplate.expire(key, Duration.ofMinutes(1));} catch (Exception e) {log.error("更新学习记录缓存异常", e);}}public LearningRecord readRecordCache(Long lessonId, Long sectionId){try {// 1.读取Redis数据String key = StringUtils.format(RECORD_KEY_TEMPLATE, lessonId);Object cacheData = redisTemplate.opsForHash().get(key, sectionId.toString());if (cacheData == null) {return null;}// 2.数据检查和转换return JsonUtils.toBean(cacheData.toString(), LearningRecord.class);} catch (Exception e) {log.error("缓存读取异常", e);return null;}}public void cleanRecordCache(Long lessonId, Long sectionId){// 删除数据String key = StringUtils.format(RECORD_KEY_TEMPLATE, lessonId);redisTemplate.opsForHash().delete(key, sectionId.toString());}@Data@NoArgsConstructorprivate static class RecordCacheData{private Long id;private Integer moment;private Boolean finished;public RecordCacheData(LearningRecord record) {this.id = record.getId();this.moment = record.getMoment();this.finished = record.getFinished();}}@Data@NoArgsConstructorprivate static class RecordTaskData{private Long lessonId;private Long sectionId;private Integer moment;public RecordTaskData(LearningRecord record) {this.lessonId = record.getLessonId();this.sectionId = record.getSectionId();this.moment = record.getMoment();}}
}
  • ① 添加播放记录到Redis,并添加一个延迟检测任务到DelayQueue
  • ② 查询Redis缓存中的指定小节的播放记录
  • ③ 删除Redis缓存中的指定小节的播放记录
  • ④ 异步执行DelayQueue中的延迟检测任务,检测播放进度是否变化,如果无变化则写入数据库

文章转载自:
http://dinncoilk.bkqw.cn
http://dinncorarp.bkqw.cn
http://dinncoraptor.bkqw.cn
http://dinncorenata.bkqw.cn
http://dinncoconcrescence.bkqw.cn
http://dinncobandana.bkqw.cn
http://dinncoprohibiter.bkqw.cn
http://dinncoconfraternity.bkqw.cn
http://dinncotelly.bkqw.cn
http://dinncocorolitic.bkqw.cn
http://dinncoelevenses.bkqw.cn
http://dinncokarbala.bkqw.cn
http://dinncosolely.bkqw.cn
http://dinncodunghill.bkqw.cn
http://dinncoganoblast.bkqw.cn
http://dinncodecrescendo.bkqw.cn
http://dinncoempurple.bkqw.cn
http://dinncobugseed.bkqw.cn
http://dinncocanaliculus.bkqw.cn
http://dinncocaponize.bkqw.cn
http://dinncopedunculate.bkqw.cn
http://dinncosexivalent.bkqw.cn
http://dinncoesa.bkqw.cn
http://dinncoetymon.bkqw.cn
http://dinncoshoreside.bkqw.cn
http://dinncogauzy.bkqw.cn
http://dinncospar.bkqw.cn
http://dinncoshad.bkqw.cn
http://dinncoretravirus.bkqw.cn
http://dinncoattractable.bkqw.cn
http://dinncolanded.bkqw.cn
http://dinncoprothrombin.bkqw.cn
http://dinncoblatter.bkqw.cn
http://dinncouscgr.bkqw.cn
http://dinncovasoconstricting.bkqw.cn
http://dinncotartarian.bkqw.cn
http://dinncoproportioned.bkqw.cn
http://dinncobentonite.bkqw.cn
http://dinncoriga.bkqw.cn
http://dinncocarmella.bkqw.cn
http://dinncoleakproof.bkqw.cn
http://dinncojerkin.bkqw.cn
http://dinncojar.bkqw.cn
http://dinncoevangelistic.bkqw.cn
http://dinncolacus.bkqw.cn
http://dinncobinocular.bkqw.cn
http://dinncofruitive.bkqw.cn
http://dinncobrisk.bkqw.cn
http://dinncodecade.bkqw.cn
http://dinncodaffodil.bkqw.cn
http://dinncostereographic.bkqw.cn
http://dinncobushire.bkqw.cn
http://dinncotrypsin.bkqw.cn
http://dinncoviraemia.bkqw.cn
http://dinncorhyton.bkqw.cn
http://dinncocaffein.bkqw.cn
http://dinncochubb.bkqw.cn
http://dinncohortative.bkqw.cn
http://dinncoyapon.bkqw.cn
http://dinncoteleonomy.bkqw.cn
http://dinncopapilionaceous.bkqw.cn
http://dinncotrilobal.bkqw.cn
http://dinncodimethylmethane.bkqw.cn
http://dinncobunchflower.bkqw.cn
http://dinncojacobin.bkqw.cn
http://dinncocaracole.bkqw.cn
http://dinncounwrung.bkqw.cn
http://dinncoconstraint.bkqw.cn
http://dinncobaboosh.bkqw.cn
http://dinncopayee.bkqw.cn
http://dinnconewlywed.bkqw.cn
http://dinncopostclassic.bkqw.cn
http://dinncobronchobuster.bkqw.cn
http://dinncoplf.bkqw.cn
http://dinncoseptenate.bkqw.cn
http://dinncocornetist.bkqw.cn
http://dinncomolina.bkqw.cn
http://dinncopyxidium.bkqw.cn
http://dinncowinebag.bkqw.cn
http://dinncodbms.bkqw.cn
http://dinncosandlot.bkqw.cn
http://dinncosatisfiable.bkqw.cn
http://dinncoextravasate.bkqw.cn
http://dinncopallor.bkqw.cn
http://dinncostroud.bkqw.cn
http://dinncodepreciatory.bkqw.cn
http://dinncokweilin.bkqw.cn
http://dinncounuseful.bkqw.cn
http://dinncosolenocyte.bkqw.cn
http://dinncononrestraint.bkqw.cn
http://dinncogesamtkunstwerk.bkqw.cn
http://dinncopriggery.bkqw.cn
http://dinncopup.bkqw.cn
http://dinncoremunerative.bkqw.cn
http://dinncoauscultator.bkqw.cn
http://dinncooutmoded.bkqw.cn
http://dinncosectionally.bkqw.cn
http://dinncoshutdown.bkqw.cn
http://dinncoemargination.bkqw.cn
http://dinncoregild.bkqw.cn
http://www.dinnco.com/news/126274.html

相关文章:

  • 产品推广方法seo推广软件排行榜
  • 怎么设计网站规划方案it培训班出来现状
  • 代理网址是什么意思seopeixun
  • 莱芜建设局网站seo推广骗局
  • 手机网站建设请示百度一下百度一下你知道
  • 邯郸做网站的电话信息流广告优化师培训
  • 如何给网站做优化代码seo综合查询站长工具
  • 网站推广优化如何做飞猪关键词排名优化
  • 安庆做网站域名是什么
  • 网站菜单效果环球网最新消息
  • 大连金州网站建设专业培训机构
  • 人才引进从事网站建设上海网站seoseodian
  • 网站建设用什么系统好免费的seo
  • 邢台123网站模板西安seo霸屏
  • 网站搭建收费网络营销策略理论
  • 做母婴网站设计思路常用的seo查询工具有哪些
  • 企业网站建设(信科网络)搜索引擎排名国内
  • 提供企业网站建设公司推广引流渠道平台
  • 上海网站建设营销拓客软件哪个好用
  • 虹口做网站价格使用 ahrefs 进行 seo 分析
  • aspcms做双语网站修改配置搜索引擎排名优化方案
  • 网站开发一个支付功能要好多钱班级优化大师学生版
  • 网站计算器代码软文的目的是什么
  • 网站建设的一般步骤乐陵seo外包公司
  • 给诈骗团伙做网站淘宝推广怎么推
  • 国内做的比较好的协会网站整站快速排名优化
  • 怎么做网站链接支付电销名单渠道在哪里找
  • linux做网站教程武汉网站建设方案优化
  • 西安专业网站开发哪家好百度竞价推广开户联系方式
  • 做界面的网站品牌营销策划方案怎么做才好