news 2026/4/28 2:27:42

Java 篇-项目实战-天机学堂(从0到1)-day8

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java 篇-项目实战-天机学堂(从0到1)-day8

java 篇: 1.基础地基 2.设计原理 3.项目实战


实时排行榜思路分析:

积分累加:

键名(相当于文件名)成员(行)分数(列)
z1u118
z1u225
z2u1100

查询 提供键和值

查询排行

从小到大排,从 0 开始

从大到小排,从 0 开始

开始编写代码:

先定义前缀

这里时间戳得格式化,所以需要在 common 包下的 utils 当中加个格式化器

这里没有":"了,因为前面没有 uid 了

接下来就是在 PointsRecordServiceImpl 当中,添加记录后在增加一步,累积积分数据到 Redis 的 SortedSet

进行测试:

重新签到一下

有记录,测试通过

实现学霸积分榜接口:

展示数据可以分成两部分,一个是我的积分和排名,另一个是榜单列表,将二者拼接返回。为啥要单独查我的呢,因为你不一定能上榜。

之后又得分查的是当前榜单,还是历史榜单。避免臃肿,以及达到逻辑清晰的目的,于是用 4 个方法来实现。

咱们先来实现查询当前页面:

对着表来做:

为了避免每次都传入 key,所以我们事先把 key 绑定住,之后我们只需要传入 userId,就可以查询用户的积分和排名了。最后封装的时候 rank 记得 +1.

接着就是查询榜单列表:

注意亮点:

①:记得带上 WithScores

②:并不是每页都是从 1 开始的,是从 from+1 开始的

再后面就是封装了

这没啥好说的,之前都写过很多个了,逻辑理清,就行。

测试一下,通过了

"萝丝",名字没有显示,是因为数据库没有

数据库的分区和分表(历史排行榜):

分区(数据存储方案):

表数据越多,.ibd 文件越大,小的时候可能只有几十兆,几百兆,当数据规模很大时,可能达到几个 GB,甚至 TB,占用的分区越多,做数据检索时,经历的磁盘 IO 次数也会越多,性能也会越差。

水平拆分:按赛季切

**优点:**

**提高数据检索性能:**现在一个赛季就放在.ibd 文件当中了,占用的分区也就小了,磁盘 IO 次数也就少了。

**提高统计性能:**对整张表做一种类似求和或者计数的操作,原来得在整张表进行操作,现在可以用多个线程并行处理多个分区的数据,最后汇总。

**打破磁盘容量限制:**对表做了分区以后,还可以给每个分区指定存储位置,不同的分区可以在不同的磁盘。那这就可以无限扩容。

**根据分区清理数据效率高:**比如说某个数据太旧了,根本没人看了,那你直接把对应的.ibd 文件删了,就行。比执行 Mysql 语句的效率高。

**对业务没有影响:**虽然在物理上拆出来多个表,但是在逻辑上还是一张表,意思说进行 CRUD 时,业务逻辑根本不用改

**缺点:**

**分区字段必须是索引的一部分:**比如将 season 字段作为分区字段,因为 season 字段有很多重复值,不可能单独建立索引,只能走联合索引,和主键绑在一起,mp 不支持联合主键,而它本身又是很多重复的,再建立索引,其实没啥意义。

**分区方式不够灵活:**① 按范围(range( , ))② 按枚举(男,女)③ 按哈希值(某个字段求哈希值对数量取模)

**只支持水平分区:**

分表(表设计方案):

**水平分表:**逻辑和实际都是多张表,需要通过业务逻辑判断,拆分灵活

**垂直分表:**宽表会导致单行数据量过多,那存储的数据量(行数)就会减少,查询性能下降。只想要当中某些字段,却把所有字段查出来了,性能差。再说,如果有个字段更改频繁,写频繁,那它会加锁,那就影响查了,性能变差。所以宽表不可取,得按字段进行拆分成不同表,但他们得有关联(外键关联),CRUD 时注意一下。

实际开发中,追求这种灵活性,以及垂直拆分的能力,因此都会采用分表的方案。

分库和集群:

这里的学习库集群是结构相同,但是数据不同,水平分表。

课程库不一样,是为了防止高并发,所以数据是相同的。

定时生成历史榜单表:

1.获取上月时间:当前时间减去一个月的时间,当然减去 2 天也可以。

2.查询赛季 id

3.创建表

le,ge 和 lt,gt 的区别,前者为闭区间后者为开区间。

`Optional` 是 Java 8 引入的容器类,用来优雅地处理可能为 `null` 的值

`oneOpt()` 的含义

  • one:查询一条记录
  • Opt:Optional 的缩写
  • 返回类型:`Optional`,而不是直接返回对象或 `null`

查询结果与 Optional 的对应关系

  • `Optional` 是一个包装器,明确告诉调用者"这个值可能不存在"
  • 强制你处理空值情况,避免 `NullPointerException`
  • 配合 `orElse()`、`ifPresent()`、`orElseThrow()` 等方法使用更安全

return 注释了,两者写法都可以,不过没注释的优雅一点。

创建表部分:

和 本质是 的特例,用 delete 标签都能跑

网友提问:为什么每次判断 null 值的时候有的时候是 return 有的是返回一个空的集合,有的时候又是抛出异常,怎么区别啊?

判断 null 后如何处理,取决于业务语义和调用方的期望。

**1. 返回(静默处理) - 正常业务场景**

当 null 是预期内的正常情况,不需要调用方关心时:

适用场景:

  • 批量处理中跳过无效数据
  • 定时任务、触发器
  • 可选功能(有就执行,没有就算了)
  • 查询列表为空也算正常结果

**2. 返回空集合 - 查询结果的正常情况**

当方法契约明确表示会返回集合,空集合比 null 更合理:

适用场景:

  • 查询方法返回集合类型
  • 避免调用方判空,可以直接 foreach

**3. 抛出异常 - 异常业务场景**

当 null 代表程序无法继续的致命错误时:

适用场景:

  • 前置条件不满足(参数校验失败)
  • 依赖的核心数据不存在
  • 配置缺失导致系统无法运行
  • API 接口参数错误

总结口诀

  • 正常可预期 → return / 空集合
  • 错误不应当 → 抛异常
  • 可选返回值 → Optional
  • 批量循环 → 跳过(continue/return)
  • 核心逻辑 → 必须抛异常

关键是保持一致性:同一个项目中类似场景要用相同处理方式,否则会混乱。

学习服务压力很大,所以不可能单点去部署,那肯定会负载均衡,水平扩展,部署成多个实例。那这样就会有多个定时任务,那其实只需要一个就够了。然后表创建完了,还需要将 Redis 当中的数据持久化,那就需要后续的定时任务去做了,那这两个定时任务需要保证先后顺序。

分布式任务调度的常见技术:

XXL-JOB 快速入门:

adminAddresses:从.env 文件当中读取,填虚拟机的地址和端口就行。

appname:注册到调度中心的这个应用的名称,一般都用当前微服务的名称,如 learning service

ip 端口:不用配,默认自己读取

accessToken:访问令牌提前在调度中心配好了,作为访问授权的一个密钥

logPath:运行时保存的一个目录

logRetentionDays:日志保存的有效期

属性配置:

这是一个 Spring Boot 配置属性绑定类,用于将配置文件(`application.yml` 或 `application.properties`)中以 `tj.xxl-job` 开头的配置项自动绑定到这个 Java 对象中。

主要就写划红线的字段,从黄色划横线的配置文件当中去读。

读完了之后,配置执行器,把东西一个个塞进去。这就完成了自动装配了。

需要我们做的是在 yml 文件里,写配置的属性。当然这里也不用了,nacos 当中已经共享配置了

橙色划线保持一致

把原来的 @Scheduled 换成 @XxlJob,定义好任务。接着把执行器注册到调度中心,把任务注册到调度中心。启动之后,执行器会自动注册到调度中心,之后在管理页面填一下信息就完了,任务也是在管理页面当中去填。

自动注册不需要填机器地址,手动需要。

名称得跟微服务名称保持一致

IDE 上启动服务就有了地址(本机)

接下来就是注册任务

想测试就

直接点击保存

控制台就有 sql 语句输出

咦,怎么没看到建表语句

哦,原来

这个文件放在别的模块下了,cc 还是牛的

查看调度日志

MybatisPlus 的动态表名插件:

定义的表名处理器,只有一个方法,如果一个接口里只有一个方法,属于函数式接口,那就可以用 lambda 表达式代替

概念含义示例
逻辑表名(旧表)代码中写的表名(模板)`points_board`
真实表名(新表)数据库实际存在的表名`points_board_2025`、`points_board_2026`

TableNameHandler 接口

  • `sql`:要执行的 SQL 语句(通常用不到)
  • `tableName`:逻辑表名(这里是 `points_board`)
  • 返回值:替换后的真实表名

欧克,拦截器定义好后,然后注册到 mybatis-plus

`@ConditionalOnMissingBean` 的作用:

  • 允许你自定义 `MybatisPlusInterceptor`
  • 如果没有自定义的,就用这个默认配置
  • 常见于框架/组件库中,提供默认实现

`@Autowired(required = false)` 是 Spring 的依赖注入注解,意思是:"尝试注入这个依赖,如果找不到就赋值为 null,不要报错"。

写法找不到依赖时的行为
`@Autowired`❌ 抛出异常,启动失败
`@Autowired(required = false)`✅ 赋值为 `null`,正常启动

动态表名拦截器(可选)

  • 判断是否有动态表名拦截器(就是你之前配的那个)
  • 如果有就添加到拦截器链中
  • 如果没有就跳过,不影响其他功能

注意注册顺序:

榜单持久化以及 XXLJob 工作流:

① 建表 ② 刷盘 ③ 清理 Redis 数据 ,三个分开,不能一起,如果后面失败,又得重新建表了。

①:

②:

当中分页查询方法之前写过

这里只需要将 private 换成 public,上面加上 @Override,然后父接口添加这个方法。

得到的 PointsBoard 类

包含这么多属性,但是我们只需要下面这几个

So

这里得把自增改成手动添加,

然后把排名信息写入 id

season 就没赋过值,不用管。

查到 1000 条就刷盘,别堆到一起,不如内存要爆了。

③:

DEL vs UNLINK

特性`DEL``UNLINK`
删除方式同步删除异步删除
阻塞❌ 会阻塞 Redis✅ 不阻塞 Redis
返回结果删除的 key 数量删除的 key 数量
适用场景小数据量大数据量
版本要求所有版本Redis 4.0+

DEL - 同步删除

UNLINK - 异步删除

数据跑批业务和 XXL Job 的分片广播:

刷盘之后记得 remove 线程域

ctl + d,新建实例,改下端口

改造以下地方

方法返回值含义示例值
`getShardIndex()`int当前机器的分片序号0, 1, 2...
`getShardTotal()`int总分片数(机器总数)3

把两个启动之后:

持久化榜单数据,这里把轮询改为分片广播

清理 Redis 中的历史榜单和创建历史榜单表不需要动,就一个人去干就完了。

后面进行测试,这里我没做,图是视频当中的:

理论上是只有一个后台会显示建表 create 语句,但是 insert 都有

数据库数据有了

Redis 当中的没了

网友疑问:当一个实例完成后触发子任务时 子任务会导致 redis 数据删除 会使另一个没有完成的实例得不到 redis 数据 所以应该加个分布式锁吧

确实存在这个竞态问题。但普通分布式锁解决不了这个问题,因为这不是互斥访问的问题,而是"所有分片都完成后才能删除"的问题。


如果对你有帮助的话,请点赞,关注,收藏。热爱可抵一切!👍 ❤️ 🔥

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/28 2:27:42

计算机使用代理技术:从视觉理解到自动化实践

1. 计算机使用代理的演进历程 计算机使用代理(Computer-Use Agents)的发展并非一蹴而就,而是经历了从概念验证到主流应用的完整技术演进。2016年OpenAI发布的Universe平台首次尝试让AI通过虚拟键盘和鼠标控制应用程序,但受限于当时…

作者头像 李华
网站建设 2026/4/28 2:27:35

ServerlessClaw:基于AWS无服务器架构的AI智能体集群设计与部署

1. 项目概述:当AI智能体遇上无服务器架构在AI智能体(AI Agent)领域,我们正处在一个激动人心的转折点。过去,运行一个能够自主思考、执行复杂任务的AI系统,往往意味着你需要租用一台或多台24小时不间断运行的…

作者头像 李华
网站建设 2026/4/28 2:26:24

3D动漫发型生成技术CHARM框架解析与应用

1. 3D动漫发型生成的技术挑战与行业需求在动漫和游戏产业中,角色发型设计一直是个既关键又耗时的环节。传统手工建模一个复杂的动漫发型可能需要资深美术师数天时间,而CHARM框架的出现将这个流程缩短到了分钟级。这背后的技术突破源自对动漫发型特殊性的…

作者头像 李华
网站建设 2026/4/28 2:23:20

AI绘画提示词工程实战:从入门到精通,解锁高质量图像生成

1. 项目概述:一个AI绘画提示词的“兵器库” 如果你玩过AI绘画,无论是Midjourney、Stable Diffusion还是DALL-E,那你一定经历过这样的时刻:脑子里有个绝妙的画面,但输入提示词后,AI生成的却总是不尽人意&…

作者头像 李华
网站建设 2026/4/28 2:22:21

有限状态机在Web自动化测试中的实践与优化

1. 有限状态机(FSM)在Web自动化中的核心价值1.1 传统Web自动化训练的痛点当前基于真实网站的训练数据收集存在三个根本性缺陷:状态不可观测性:代理只能获取UI渲染结果(如截图),无法直接访问底层…

作者头像 李华
网站建设 2026/4/28 2:17:41

企业采购项目管理系统,为什么不能只看人均单价?6款方案解析

本文将深入比较6款企业项目管理系统与协作方案:PingCode、Worktile、Jira/Confluence、monday.com、Asana、ClickUp。一、企业采购项目管理系统,为什么不能只看人均单价1、单价只是报价入口,不是最终成本很多采购动作之所以后期容易失控&…

作者头像 李华