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

用java和redis实现考试成绩排行榜

一、引言

在各类考试场景中,无论是学校里的学业测试,还是线上培训课程的考核,亦或是各类竞赛的选拔,成绩排行榜都是大家颇为关注的一个元素。它不仅能直观地展示考生之间的成绩差异,激发大家的竞争意识,还能让考生快速了解自己所处的位置。而在技术实现层面,如何高效、准确地构建这样一个考试成绩排行榜呢?今天,作为一名有着丰富经验的 Java 技术专家(阿里 P8 级别哦),就来和大家深入探讨一下如何利用 Java 和 Redis 这对 “黄金搭档” 来实现考试成绩排行榜,从原理剖析到代码示例,带大家一步步领略其中的奥秘,相信读完这篇文章后,你也能轻松打造属于自己的成绩排行榜系统啦!

二、技术选型背景

(一)为什么选择 Java

Java 作为一门广泛应用于企业级开发的编程语言,有着诸多优势。它具备强大的面向对象特性,代码结构清晰、易于维护和扩展。在处理与数据库交互、网络通信以及复杂业务逻辑方面有着成熟且丰富的类库支持。对于构建考试成绩排行榜这样的应用场景,我们可能需要从不同的数据源获取成绩数据(比如从数据库中查询考生成绩),然后进行各种数据处理、排序等操作,Java 的高性能和稳定性能够确保整个过程流畅进行,而且 Java 生态系统中的各种框架(如 Spring 等)可以进一步帮助我们简化开发流程,提高开发效率,所以选择 Java 作为基础开发语言是非常合适的。

(二)Redis 的独特魅力

Redis 是一款高性能的键值对存储数据库,常被用作缓存、消息队列以及数据存储等多种用途。在构建考试成绩排行榜场景中,它的优势尤为突出:

  • 快速读写性能:Redis 的数据存储在内存中,相比于传统的基于磁盘的数据库(如 MySQL 等),读写速度极快,能够在短时间内处理大量的成绩数据插入、更新以及查询操作,满足排行榜实时更新和查询的需求。
  • 数据结构丰富:Redis 提供了多种实用的数据结构,像有序集合(Sorted Set)就特别适合用来构建排行榜。有序集合中的每个元素都带有一个分数(Score),我们可以将考生的成绩作为分数,考生的唯一标识(比如学号、用户名等)作为元素,这样 Redis 就能根据成绩自动对元素进行排序,轻松实现排行榜功能,而且可以方便地进行范围查询、获取排名等操作。
  • 原子性操作:Redis 支持很多原子性操作,比如对有序集合元素的添加、分数的更新等操作都是原子性的,这意味着在高并发环境下(比如很多考生同时提交成绩或者大量用户同时查询排行榜),数据的一致性能够得到很好的保证,不会出现数据不一致的混乱情况。

三、整体架构设计

(一)系统模块划分

  1. 成绩数据获取模块
    这个模块主要负责从数据源(如数据库中的考试成绩表)获取考生的成绩数据。可以通过 Java 的 JDBC(Java Database Connectivity)或者使用一些更高级的数据库访问框架(如 MyBatis、Hibernate 等,结合 Spring 框架来配置和使用会更加方便)与数据库建立连接,编写 SQL 查询语句来获取成绩信息,例如从名为 exam_scores 的表中查询出考生的学号、姓名以及对应的考试成绩等字段内容。

  2. 数据处理与存储模块
    获取到成绩数据后,需要对数据进行一定的处理,比如数据格式的校验、异常数据的过滤等,然后将处理好的数据存储到 Redis 中构建排行榜。利用 Redis 的 Java 客户端(如 Jedis、Lettuce 等,这里以 Jedis 为例进行讲解),将考生的学号作为有序集合的成员,考试成绩作为对应的分数,调用相应的 API(Application Programming Interface)将数据添加到有序集合中,实现排行榜数据的初始化存储。

  3. 排行榜查询与展示模块
    当用户(比如考生、老师或者管理员等)想要查看排行榜时,通过 Java 代码向 Redis 发送查询请求,获取排行榜相关的数据,比如获取排名前 N 的考生信息、查询某个考生的具体排名等操作,然后将这些数据进行适当的格式转换和处理,通过 Web 页面(可以使用 Java Web 相关技术,如 Servlet、JSP 等进行页面构建和展示,或者采用更现代的前后端分离框架,如 Spring Boot 结合 Vue.js 等进行展示,这里先以简单的 Servlet 示例来讲)展示给用户,让用户直观地看到成绩排名情况。

(二)模块之间的交互流程

  1. 初始化阶段
    首先,成绩数据获取模块启动,从数据库中查询出所有考生的成绩数据,将数据传递给数据处理与存储模块。数据处理与存储模块对数据进行检查和处理后,使用 Jedis 客户端与 Redis 建立连接,将成绩数据存储到 Redis 的有序集合中,完成排行榜的初始化构建。

  2. 查询阶段
    当用户在前端页面发起排行榜查询请求时(比如点击 “查看排行榜” 按钮),请求会发送到后端的 Java 程序(如 Servlet),排行榜查询与展示模块接收到请求后,通过 Jedis 客户端向 Redis 发送相应的查询命令(如获取排名前 10 的考生信息等),Redis 返回查询结果,该模块再将结果进行处理并反馈给前端页面进行展示,整个交互流程清晰明了,确保了排行榜功能的正常运作。

四、环境搭建与准备

(一)Java 开发环境配置

  1. 安装 JDK(Java Development Kit)
    确保你的开发机器上安装了合适版本的 JDK,推荐使用较新且稳定的版本,比如 JDK 11 或 JDK 17。你可以从 Oracle 官方网站或者 OpenJDK 官方网站下载对应操作系统版本的 JDK 安装包,按照安装向导进行安装,安装完成后,通过在命令行中输入 java -version 命令来验证是否安装成功以及查看安装的版本信息。

  2. 选择集成开发环境(IDE)
    常用的 Java IDE 有 Intellij IDEA 和 Eclipse 等,这里以 Intellij IDEA 为例进行介绍。下载并安装 Intellij IDEA,安装完成后打开,创建一个新的 Java 项目(可以选择合适的项目模板,比如 Java Web 项目模板,如果后续要进行 Web 相关开发用于展示排行榜的话)。

(二)Redis 安装与配置

  1. 下载与安装 Redis
    根据你的操作系统,从 Redis 官方网站下载对应的 Redis 安装包。对于 Linux 系统,可以通过解压下载的压缩包,进入解压后的目录,使用 make 命令进行编译安装;对于 Windows 系统,可以直接运行安装程序进行安装。安装完成后,启动 Redis 服务,在 Linux 系统下可以通过在 Redis 安装目录下执行 ./redis-server 命令来启动服务(默认端口为 6379,可根据需要修改配置文件调整端口等参数),在 Windows 系统下可以找到安装目录下的 redis-server.exe 文件双击启动服务。

  2. Redis 配置(可选的常用配置调整)
    可以打开 Redis 的配置文件(通常名为 redis.conf),根据实际需求进行一些配置调整,比如设置最大内存限制(通过 maxmemory 参数),避免 Redis 占用过多的系统内存;设置密码认证(通过 requirepass 参数),增强 Redis 的安全性等,配置完成后需要重启 Redis 服务使配置生效。

(三)添加项目依赖(以 Maven 项目为例)

在 Java 项目中,如果要使用 Jedis 来操作 Redis,需要在项目的 pom.xml 文件中添加 Jedis 的依赖,示例如下:

<dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>4.4.1</version>
</dependency>

如果项目还涉及到数据库访问(比如从数据库获取成绩数据),并且使用 MyBatis 框架,还需要添加 MyBatis 相关依赖以及对应数据库的驱动依赖(假设使用 MySQL 数据库),示例如下:

<dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.11</version>
</dependency>
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.30</version>
</dependency>

五、成绩数据获取模块实现

(一)数据库设计(以简单的 MySQL 数据库为例)

假设我们有一个名为 exam_system 的数据库,里面创建了一张 exam_scores 表来存储考试成绩信息,表结构设计如下:

字段名类型描述
idint成绩记录的唯一标识,主键,自增
student_idvarchar(20)考生的学号,唯一标识考生
student_namevarchar(50)考生的姓名
exam_subjectvarchar(50)考试科目
scoreint考试成绩,取值范围根据实际考试设定,比如 0 - 100 分

可以使用以下 SQL 语句创建该表:

CREATE TABLE exam_scores (id int AUTO_INCREMENT PRIMARY KEY,student_id varchar(20) NOT NULL,student_name varchar(50) NOT NULL,exam_subject varchar(50) NOT NULL,score int NOT NULL
);

(二)使用 JDBC 获取成绩数据示例

在 Java 中,使用 JDBC 来连接数据库并获取成绩数据,以下是一个简单的示例代码,创建一个名为 ScoreDataGetter 的类(这里省略了一些异常处理等细节,实际应用中要完善异常处理逻辑):

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;public class ScoreDataGetter {private static final String DB_URL = "jdbc:mysql://localhost:3306/exam_system";private static final String DB_USER = "root";private static final String DB_PASSWORD = "your_password";public static List<ScoreRecord> getScoreData() {List<ScoreRecord> scoreRecords = new ArrayList<>();try (Connection connection = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD);PreparedStatement preparedStatement = connection.prepareStatement("SELECT student_id, student_name, score FROM exam_scores");ResultSet resultSet = preparedStatement.executeQuery()) {while (resultSet.next()) {ScoreRecord record = new ScoreRecord();record.setStudentId(resultSet.getString("student_id"));record.setStudentName(resultSet.getString("student_name"));record.setScore(resultSet.getInt("score"));scoreRecords.add(record);}} catch (SQLException e) {e.printStackTrace();}return scoreRecords;}static class ScoreRecord {private String studentId;private String studentName;private int score;// Getter and Setter methodspublic String getStudentId() {return studentId;}public void setStudentId(String studentId) {this.studentId = studentId;}public String getStudentName() {return studentName;}public void setStudentName(String studentName) {this.studentName = studentName;}public int getScore() {return score;}public void setScore(int score) {this.score = score;}}
}

在上述代码中,通过 DriverManager 建立与 MySQL 数据库的连接,使用 PreparedStatement 执行查询语句,从 exam_scores 表中获取考生的学号、姓名和成绩信息,将每条记录封装成 ScoreRecord 对象,并添加到 List 集合中返回,这样就完成了成绩数据的初步获取工作。

(三)使用 MyBatis 简化数据获取(可选的优化方式)

如果觉得直接使用 JDBC 代码比较繁琐,也可以使用 MyBatis 框架来简化数据库操作。首先,在项目的 resources 目录下创建 mybatis-config.xml 配置文件,示例内容如下(这里是一个简单配置,可根据实际情况完善):

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><environments default="development"><environment id="development"><transactionManager type="JDBC"/><dataSource type="POOLED"><property name="driver" value="com.mysql.cj.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/exam_system"/><property name="username" value="root"/><property name="password" value="your_password"/></dataSource></environment></environments><mappers><mapper resource="com/example/mapper/ScoreMapper.xml"/></mappers>
</configuration>

然后创建 ScoreMapper.xml 文件(放在 com/example/mapper 目录下,需根据项目实际包结构调整),定义查询语句,示例如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.ScoreMapper"><select id="getScoreData" resultMap="ScoreRecordResultMap">SELECT student_id, student_name, score FROM exam_scores</select><resultMap id="ScoreRecordResultMap" type="com.example.ScoreDataGetter.ScoreRecord"><result property="studentId" column="student_id"/><result property="studentName" column="student_name"/><result property="score" column="score"/></resultMap>
</mapper>

再创建 ScoreMapper 接口,代码如下:

import java.util.List;
import com.example.ScoreDataGetter.ScoreRecord;
import org.apache.ibatis.annotations.Mapper;@Mapper
public interface ScoreMapper {List<ScoreRecord> getScoreData();
}

最后,在 ScoreDataGetter 类中可以通过注入 ScoreMapper 来获取成绩数据,修改后的代码如下:

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;public class ScoreDataGetter {public static List<ScoreRecord> getScoreData() {try {InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);SqlSession sqlSession = sqlSessionFactory.openSession();ScoreMapper scoreMapper = sqlSession.getMapper(ScoreMapper.class);return scoreMapper.getScoreData();} catch (IOException e) {e.printStackTrace();return null;}}// ScoreRecord类定义不变,省略重复代码}

通过 MyBatis 框架,将 SQL 语句和 Java 代码进行了分离,使得代码结构更加清晰,更易于维护和扩展,方便获取成绩数据用于后续的排行榜构建。

六、数据处理与存储模块实现

(一)Jedis 客户端基本使用介绍

Jedis 是 Redis 官方推荐的 Java 客户端,使用它可以方便地与 Redis 进行交互。首先,需要在 Java 代码中创建 Jedis 对象来建立与 Redis 服务器的连接,示例如下:

import redis.clients.jedis.Jedis;public class JedisExample {public static void main(String[] args) {// 创建Jedis对象,默认连接本地Redis服务器(端口6379),如果Redis服务器在其他主机或者端口不同,需要传入相应参数Jedis jedis = new Jedis();// 可以执行一些简单的Redis命令,比如设置一个键值对jedis.set("key", "value");// 获取键对应的值String value = jedis.get("key");System.out.println("获取到的值为: " + value);// 关闭连接jedis.close();}
}

在上述代码中,通过 Jedis 类创建了一个与本地 Redis 服务器的连接,然后使用 set 方法设置了一个键值对,再通过 get 方法获取该键对应的值并打印出来,最后关闭了连接,展示了最基本的 Jedis 使用方式。

(二)将成绩数据存储到 Redis 有序集合

获取到成绩数据(通过前面的 ScoreDataGetter 类获取的 List<ScoreRecord> 集合)后,要将这些数据存储到 Redis 的有序集合中,以构建考试成绩排行榜。以下是示例代码,创建一个名为 ScoreDataStorage 的类:

import redis.clients.jedis.Jedis;
import java.util.List;public class ScoreDataStorage {private static final String REDIS_KEY = "exam_scores_ranking";public static void storeScoreData(List<ScoreRecord> scoreRecords) {try (Jedis jedis = new Jedis()) {for (ScoreRecord record : scoreRecords) {// 将考生的学号作为有序集合的成员,成绩作为分数,添加到有序集合中jedis.zadd(REDIS_KEY, record.getScore(), record.getStudentId());}}}static class ScoreRecord {private String studentId;private String studentName;private int score;// Getter and Setter methodspublic String getStudentId() {return studentId;}public void setStudentId(String studentId) {this.studentId = studentId;}public String getStudentName() {return studentName;}public void setStudentName(String studentName) {this.studentName = studentName;}public int getScore() {return score;}public void setScore(int score) {this.score = score;}}
}

在上述代码中,定义了一个常量 REDIS_KEY 作为存储成绩排行榜数据的有序集合在 Redis 中的键名。然后在 storeScoreData 方法里,通过 Jedis 客户端与 Redis 建立连接,遍历获取到的成绩记录列表,针对每条记录,使用 zadd 方法将考生的学号(record.getStudentId())作为有序集合的成员,考试成绩(record.getScore())作为对应的分数添加到名为 exam_scores_ranking 的有序集合中,这样就完成了成绩数据到 Redis 的存储,使得 Redis 根据成绩自动对考生进行排序,初步构建好了成绩排行榜的数据基础。

(三)数据校验与异常处理

在将成绩数据存储到 Redis 之前,进行数据校验是很有必要的,这样可以避免一些不符合要求的数据进入排行榜,影响数据的准确性和合理性。比如可以检查成绩是否在合理的取值范围内(例如考试成绩一般是 0 到 100 分之间,如果超出这个范围可能就是数据录入错误等情况),考生学号是否符合规范(比如长度、格式等是否正确)等。以下是在 ScoreDataStorage 类中添加数据校验逻辑后的示例代码:

import redis.clients.jedis.Jedis;
import java.util.List;public class ScoreDataStorage {private static final String REDIS_KEY = "exam_scores_ranking";public static void storeScoreData(List<ScoreRecord> scoreRecords) {try (Jedis jedis = new Jedis()) {for (ScoreRecord record : scoreRecords) {// 数据校验,检查成绩是否在合理范围(0 - 100分)if (record.getScore() < 0 || record.getScore() > 100) {System.err.println("成绩数据异常,学号为 " + record.getStudentId() + " 的成绩超出合理范围,成绩值为: " + record.getScore());continue;}// 这里可以添加更多校验逻辑,比如检查学号格式等// 将校验通过的数据存储到Redis有序集合中jedis.zadd(REDIS_KEY, record.getScore(), record.getStudentId());}} catch (Exception e) {e.printStackTrace();System.err.println("存储成绩数据到Redis时出现异常: " + e.getMessage());}}static class ScoreRecord {private String studentId;private String studentName;private int score;// Getter and Setter methodspublic String getStudentId() {return studentId;}public void setStudentId(String studentId) {this.studentId = studentId;}public String getStudentName() {return studentName;}public void setStudentName(String studentName) {this.studentName = studentName;}public int getScore() {return score;}public void setScore(int score) {this.score = score;}}
}

在上述代码中,在往 Redis 存储数据的循环里,添加了对成绩范围的校验逻辑,如果成绩不在 0 到 100 分这个合理区间内,就在控制台输出错误提示信息,并通过 continue 语句跳过这条异常数据,不将其添加到 Redis 中。同时,添加了更通用的异常处理块,用于捕获在与 Redis 交互过程中(如 zadd 操作等)可能出现的其他异常情况,将异常信息打印出来方便排查问题,确保整个数据存储过程更加健壮和可靠。

七、排行榜查询与展示模块实现

(一)查询排名前 N 的考生信息

使用 Jedis 可以方便地从 Redis 的有序集合中查询排名前 N 的考生信息。以下是一个示例代码,创建一个名为 RankingQuery 的类,用于实现这个功能:

import redis.clients.jedis.Jedis;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;public class RankingQuery {private static final String REDIS_KEY = "exam_scores_ranking";public static List<String> getTopNRankings(int n) {List<String> topNStudents = new ArrayList<>();try (Jedis jedis = new Jedis()) {// 使用zrevrange命令获取排名前N的成员(即考生学号),按照分数从高到低排序List<String> studentIds = jedis.zrevrange(REDIS_KEY, 0, n - 1);if (studentIds!= null &&!studentIds.isEmpty()) {// 根据获取到的考生学号,从数据库或者其他数据源(这里假设可以从数据库获取完整信息,实际可按需调整)获取考生的详细信息(如姓名等),这里先简单打印学号示例for (String studentId : studentIds) {topNStudents.add(studentId);// 以下是模拟从数据库获取详细信息并添加到列表中,实际要替换为真实的数据库查询等操作// 比如可以通过前面介绍的ScoreDataGetter或者其他数据库访问方式获取该学号对应的姓名等信息后添加进来// String studentName = getStudentNameFromDB(studentId);// topNStudents.add(studentId + ": " + studentName);}}}return topNStudents;}// 模拟从数据库获取学生姓名的方法(实际需完善数据库连接和查询逻辑)private static String getStudentNameFromDB(String studentId) {// 这里暂时返回空字符串,实际要根据数据库查询结果返回真实姓名return "";}
}

在上述代码中,定义了 getTopNRankings 方法,接收一个参数 n 表示要获取的排名前 N 的数量。通过 Jedis 客户端连接 Redis 后,使用 zrevrange 命令从名为 exam_scores_ranking 的有序集合中获取排名前 n 位的成员(也就是考生的学号),该命令默认按照分数从高到低排序(符合常见的排行榜从高到低展示的需求)。然后可以进一步根据获取到的学号,通过数据库查询等方式获取考生的详细信息(这里只是简单模拟了一下,实际应用中要替换为真实的从数据库获取学生姓名等完整信息的操作),最后将这些信息组成列表返回,以便后续展示给用户查看排行榜情况。

(二)查询某个考生的具体排名

有时候,用户可能想要知道某个特定考生在排行榜中的具体排名,使用 Jedis 也可以轻松实现这个功能。以下是示例代码,同样在 RankingQuery 类中添加方法:

import redis.clients.jedis.Jedis;
import java.util.List;public class RankingQuery {private static final String REDIS_KEY = "exam_scores_ranking";// 前面的getTopNRankings方法省略,保持代码结构清晰public static long getStudentRank(String studentId) {try (Jedis jedis = new Jedis()) {// 使用zrevrank命令获取指定成员(考生学号)的排名,分数从高到低排序,排名从0开始计数,返回的是Long类型的排名值,如果成员不存在返回nullLong rank = jedis.zrevrank(REDIS_KEY, studentId);if (rank!= null) {return rank + 1; // 因为通常排名习惯从1开始计数,所以这里加1}return -1; // 如果成员不存在,返回 -1表示未找到该考生在排行榜中的位置}}
}

在上述代码中,getStudentRank 方法接收一个考生学号作为参数,通过 Jedis 客户端连接 Redis 后,使用 zrevrank 命令获取该学号对应的考生在名为 exam_scores_ranking 的有序集合中的排名情况。由于 zrevrank 命令返回的排名是从 0 开始计数的,而通常我们习惯的排名是从 1 开始计数,所以在返回结果上加 1。如果该学号对应的成员不存在于有序集合中,就返回 -1,表示无法找到该考生在排行榜中的位置,这样就实现了查询特定考生排名的功能。

(三)通过 Java Web 展示排行榜(以 Servlet 为例简单演示)

假设我们要构建一个简单的 Web 页面来展示考试成绩排行榜,使用 Servlet 来处理 HTTP 请求并返回排行榜数据进行展示(这里只是一个基础示例,实际可以结合更现代的前端框架等进行美化和完善展示效果)。

  1. 创建一个 Servlet 类(RankingServlet)
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;public class RankingServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {response.setContentType("text/html;charset=UTF-8");PrintWriter out = response.getWriter();// 获取排名前10的考生信息(这里调用前面实现的方法,可根据需求调整获取数量等)List<String> top10Rankings = RankingQuery.getTopNRankings(10);out.println("<html><body>");out.println("<h1>考试成绩排行榜</h1>");out.println("<ul>");for (String ranking : top10Rankings) {out.println("<li>" + ranking + "</li>");}out.println("</ul>");out.println("</body></html>");}
}

在上述 RankingServlet 类中,重写了 doGet 方法来处理 HTTP 的 GET 请求(通常用于获取数据展示页面的情况)。在方法中,首先设置了响应的内容类型为 HTML 格式,并获取输出流对象,然后调用 RankingQuery.getTopNRankings 方法获取排名前 10 的考生信息(这里可根据实际需求调整获取的排名数量),最后通过 HTML 标签构建了一个简单的页面结构,将排行榜信息以列表的形式展示在页面上,当用户访问对应的 Servlet 路径时,就能看到成绩排行榜的相关内容了。

  1. 配置 Servlet 映射(在 web.xml 文件中)
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"version="4.0"><servlet><servlet-name>RankingServlet</servlet-name><servlet-class>RankingServlet</servlet-class></servlet><servlet-mapping><servlet-name>RankingServlet</servlet-name><url-pattern>/ranking</url-pattern></servlet-mapping></web-app>

通过上述配置,当用户在浏览器中访问项目的 /ranking 路径时,就会触发 RankingServlet 的 doGet 方法,进而展示出考试成绩排行榜页面,让用户直观地看到成绩排名情况。

八、高并发场景下的优化与注意事项

(一)Redis 连接池的使用

在高并发环境下,频繁地创建和销毁 Jedis 连接(像前面示例中每次操作 Redis 都创建和关闭 Jedis 对象)会带来较大的性能开销,而且可能导致连接资源耗尽等问题。这时可以使用 Redis 连接池来管理连接,提高性能和资源利用率。以下是使用 Jedis 连接池的示例代码,创建一个名为 JedisPoolUtil 的工具类:

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;public class JedisPoolUtil {private static JedisPool jedisPool;static {// 配置连接池参数JedisPoolConfig poolConfig = new JedisPoolConfig();poolConfig.setMaxTotal(100); // 最大连接数poolConfig.setMaxIdle(20); // 最大空闲连接数poolConfig.setMinIdle(5); // 最小空闲连接数poolConfig.setTestOnBorrow(true); // 在获取连接时进行有效性检测poolConfig.setTestOnReturn(true); // 在归还连接时进行有效性检测// 创建连接池,这里假设Redis服务器在本地,端口6379,无密码,可根据实际情况修改参数jedisPool = new JedisPool(poolConfig, "localhost", 6379);}public static Jedis getJedis() {return jedisPool.getResource();}public static void closeJedis(Jedis jedis) {if (jedis!= null) {jedis.close();}}
}

在上述代码中,通过 JedisPoolConfig 类配置了连接池的相关参数,如最大连接数、最大空闲连接数、最小空闲连接数以及连接有效性检测等规则,然后使用配置好的参数创建了 JedisPool 连接池对象。getJedis 方法用于从连接池中获取一个可用的 Jedis 连接,closeJedis 方法用于在使用完连接后将其归还到连接池中,这样在各个模块(如数据存储、排行榜查询等)操作 Redis 时,都可以通过这个连接池来获取和归还连接,避免了频繁创建和销毁连接带来的性能问题,提高了在高并发场景下系统的稳定性和性能。

例如,在 ScoreDataStorage 类的 storeScoreData 方法中使用连接池来获取 Jedis 连接的示例如下(其他使用 Jedis 的地方也可类似修改):

import redis.clients.jedis.Jedis;
import java.util.List;public class ScoreDataStorage {private static final String REDIS_KEY = "exam_scores_ranking";public static void storeScoreData(List<ScoreRecord> scoreRecords) {Jedis jedis = JedisPoolUtil.getJedis();try {for (ScoreRecord record : scoreRecords) {// 数据校验,检查成绩是否在合理范围(0 - 100分)if (record.getScore() < 0 || record.getScore() > 100) {System.err.println("成绩数据异常,学号为 " + record.getStudentId() + " 的成绩超出合理范围,成绩值为: " + record.getScore());continue;}// 这里可以添加更多校验逻辑,比如检查学号格式等// 将校验通过的数据存储到Redis有序集合中jedis.zadd(REDIS_KEY, record.getScore(), record.getStudentId());}} catch (Exception e) {e.printStackTrace();System.err.println("存储成绩数据到Redis时出现异常: " + e.getMessage());} finally {JedisPoolUtil.closeJedis(jedis);}}static class ScoreRecord {private String studentId;private String studentName;private int score;// Getter and Setter methodspublic String getStudentId() {return studentId;}public void setStudentId(String studentId) {this.studentId = studentId;}public String getStudentName() {return studentName;}public void setStudentName(String studentName) {this.studentName = studentName;}public int getScore() {return score;}public void setScore(int score) {this.score = score;}}
}

(二)缓存更新策略

当有新的成绩数据进入系统(比如考生重新参加考试更新了成绩),需要及时更新 Redis 中的排行榜数据,这时就涉及到缓存更新策略。一种常见的策略是先更新数据库中的成绩记录(毕竟数据库是数据的最终持久化存储地方),然后再根据更新后的成绩数据去更新 Redis 中的有序集合。例如,可以在成绩更新的业务逻辑中,先执行 SQL 语句更新数据库表中的成绩字段,然后调用 ScoreDataStorage 类的 storeScoreData 方法(传入更新后的成绩记录数据,这里可能需要根据实际业务逻辑调整获取和传递数据的方式)重新将成绩数据存储到 Redis 中,覆盖原来的旧数据,确保排行榜数据的实时性和准确性。

另外,还可以采用异步更新的方式,避免因为更新操作耗时较长影响系统的整体性能。比如使用消息队列(如 RabbitMQ、Kafka 等),当有成绩更新事件发生时,将更新任务封装成消息发送到消息队列中,然后由专门的消费者线程从消息队列中获取消息,执行更新 Redis 排行榜数据的操作,这样就可以将更新操作异步化,让主线程可以继续处理其他请求,提高系统的并发处理能力。以下是一个简单的使用 RabbitMQ 实现异步更新的示例思路(需要添加相应的 RabbitMQ 依赖以及配置等):

  1. 引入 RabbitMQ 依赖(以 Maven 项目为例)
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId><version>2.7.5</version>
</dependency>

  1. 定义成绩更新消息实体类(ScoreUpdateMessage.java)
import java.io.Serializable;public class ScoreUpdateMessage implements Serializable {private String studentId;private int newScore;public ScoreUpdateMessage(String studentId, int newScore) {this.studentId = studentId;this.newScore = newScore;}public String getStudentId() {return studentId;}public void setStudentId(String studentId) {this.studentId = studentId;}public int newScore() {return newScore;}public void setNewScore(int newScore) {this.newScore = newScore;}
}

  1. 发送成绩更新消息(在成绩更新业务逻辑处,示例代码片段)
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;@Component
public class ScoreUpdatePublisher {private static final String QUEUE_NAME = "score_update_queue";@Autowiredprivate RabbitTemplate rabbitTemplate;public void publishScoreUpdate(ScoreUpdateMessage message) {rabbitTemplate.convertAndSend(QUEUE_NAME, message);}
}

  1. 创建消息消费者(用于接收消息并更新 Redis 排行榜)
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import java.util.ArrayList;
import java.util.List;@Component
public class ScoreUpdateConsumer {private static final String REDIS_KEY = "exam_scores_ranking";@RabbitListener(queues = "score_update_queue")public void handleScoreUpdate(ScoreUpdateMessage message) {List<ScoreRecord> updatedScoreRecords = new ArrayList<>();// 根据消息中的学生学号等信息,从数据库查询完整的成绩记录(这里简化,实际需完善数据库查询逻辑)ScoreRecord record = getScoreRecordFromDB(message.getStudentId());if (record!= null) {record.setScore(message.newScore());updatedScoreRecords.add(record);// 使用Jedis连接池获取连接(假设已配置好连接池,前面有介绍示例)Jedis jedis = JedisPoolUtil.getJedis();try {// 先删除Redis中旧的该学生成绩数据jedis.zrem(REDIS_KEY, message.getStudentId());// 再添加更新后的成绩数据到有序集合jedis.zadd(REDIS_KEY, message.newScore(), message.getStudentId());} catch (Exception e) {e.printStackTrace();} finally {JedisPoolUtil.closeJedis(jedis);}}}private ScoreRecord getScoreRecordFromDB(String studentId) {// 模拟从数据库获取成绩记录,实际需完善数据库连接和查询语句等逻辑ScoreRecord record = new ScoreRecord();record.setStudentId(studentId);record.setScore(80); // 这里随便设置一个成绩值示例return record;}static class ScoreRecord {private String studentId;private String studentName;private int score;// Getter and Setter methodspublic String getStudentId() {return studentId;}public void setStudentId(String studentId) {this.studentId = studentId;}public String getStudentName() {return studentName;}public void setStudentName(String studentName) {this.studentName = studentName;}public int getScore() {return score;}public void setScore(int score) {this.score = score;}}
}

通过这样的异步消息队列机制,可以在高并发的成绩更新场景下,更高效地更新 Redis 中的排行榜数据,保障系统的性能和数据的实时性。

(三)数据一致性保障

在使用 Java 和 Redis 构建考试成绩排行榜时,要特别关注数据一致性问题,因为存在多个操作涉及数据库和 Redis,比如成绩数据的获取、存储以及更新等环节。

一方面,在初始将成绩数据从数据库加载到 Redis 构建排行榜时,要确保数据完整且准确地迁移。可以通过合理的事务处理机制(如果使用数据库的事务功能,比如在关系型数据库中通过 START TRANSACTIONCOMMIT 等语句来控制事务),保证在获取数据库成绩数据过程中,若出现异常情况(如网络故障、数据库连接断开等),不会出现部分数据加载到 Redis,部分未加载的不一致情况。例如,在 ScoreDataGetter 类获取数据以及 ScoreDataStorage 类存储数据的过程中,可以包裹在一个大的事务中(具体实现根据所选用的数据库框架和事务管理机制来定,如结合 Spring 的事务管理等),一旦某个环节出错,整个操作回滚,重新尝试或者进行相应的错误提示处理。

另一方面,在后续的成绩更新等操作中,如前面提到的缓存更新策略里,无论是先更新数据库再更新 Redis,还是采用异步更新方式,都要通过合适的手段验证数据在两个存储介质中的一致性。比如可以定期进行数据核对工作,通过编写校验程序,从数据库中查询所有成绩数据,然后与 Redis 中排行榜对应的成绩数据进行对比(对比学号和成绩是否一一对应且相等),如果发现不一致的情况,及时进行修复,可以根据具体的不一致情况选择是重新从数据库同步数据到 Redis,还是根据 Redis 数据去修正数据库中的记录(当然这需要谨慎操作,要根据业务场景和数据的权威性来判断),确保整个系统中成绩排行榜数据的一致性,给用户提供准确可靠的排名信息。

九、性能测试与优化

(一)性能测试工具与方法

为了了解我们构建的考试成绩排行榜系统在不同负载情况下的性能表现,需要进行性能测试。常用的性能测试工具有 JMeter、Apache Bench(ab)等。

  • JMeter
    它是一款功能强大的开源性能测试工具,支持多种协议(如 HTTP、JDBC 等)的测试。对于我们的排行榜系统,可以使用 JMeter 来模拟大量的用户并发请求,比如模拟多个用户同时查询排行榜、同时更新成绩等场景。通过在 JMeter 中配置线程组(用于设置并发用户数量、请求的循环次数等参数),添加 HTTP 请求采样器(如果是测试 Web 页面展示排行榜等 HTTP 接口相关性能)或者 JDBC 请求采样器(用于测试数据库获取成绩数据环节的性能)等组件,然后运行测试计划,收集性能指标数据,如响应时间、吞吐量、错误率等,来评估系统的性能状况。

  • Apache Bench(ab)
    这是一个简单但实用的命令行性能测试工具,特别适合对 HTTP 接口进行快速的性能测试。例如,要测试前面创建的 RankingServlet 展示排行榜的接口性能,可以在命令行中输入类似如下命令(假设服务器运行在本地,端口 8080,需根据实际情况调整参数):

ab -n 1000 -c 100 http://localhost:8080/ranking

上述命令表示发送 1000 个请求(通过 -n 参数指定),并发数为 100(通过 -c 参数指定),对 http://localhost:8080/ranking 这个 URL 对应的接口进行性能测试,测试完成后,会输出诸如每秒请求数、平均响应时间、请求的最长和最短响应时间等性能指标信息,方便我们直观地了解接口在一定并发压力下的性能表现。

(二)优化方向与实践

根据性能测试的结果,可以从多个方面对系统进行优化:

  1. 数据库优化
    如果在获取成绩数据环节性能不佳,比如查询速度慢,可以对数据库进行优化。首先检查数据库表的索引情况,确保在查询成绩数据的 WHERE 子句中涉及的字段(如 student_idexam_subject 等如果经常用于筛选条件)都添加了合适的索引,提高查询效率。同时,可以优化 SQL 查询语句,避免复杂的嵌套查询、子查询等导致性能下降的情况,尽量采用简单高效的连接查询、聚合查询等方式来获取所需的数据。另外,合理配置数据库的参数,如缓存大小、连接池大小等(不同数据库有不同的配置参数和优化方式,以 MySQL 为例,可以通过修改 my.cnf 配置文件中的相关参数来调整),也能提升数据库的整体性能,进而加快成绩数据获取速度,间接优化排行榜系统的性能。

  2. Redis 优化
    在 Redis 方面,除了前面提到的使用连接池优化连接管理外,还可以从数据结构选择和配置参数调整等角度优化。例如,根据实际业务场景,如果排行榜数据量非常大,可以考虑对 Redis 的内存使用进行优化,通过合理设置 maxmemory 参数(限制 Redis 最大内存使用量)以及选择合适的内存淘汰策略(如 volatile-lru 表示从设置了过期时间的数据集中,按照最近最少使用原则淘汰数据等不同策略,根据业务特点选择),确保 Redis 在处理大量排行榜数据时能够高效运行,不会因为内存不足等问题影响性能。同时,优化有序集合的操作,如果频繁进行排名查询等操作,可以考虑提前缓存一些常用的排名范围数据(比如每次查询排名前 100 的考生信息很频繁,就可以将这部分数据缓存到本地内存或者其他缓存介质中,下次查询时先从缓存中获取,减少对 Redis 的直接查询次数),提高查询响应速度。

  3. 代码逻辑优化
    从 Java 代码逻辑角度,避免在循环中进行复杂且耗时的操作,比如在将成绩数据存储到 Redis 的循环里,尽量减少不必要的数据库查询、复杂的计算等操作,确保每次循环执行的任务尽可能简单高效。另外,合理使用多线程或线程池技术(如果业务场景允许且有性能提升空间),比如在更新成绩数据涉及多个学生成绩更新时,可以通过线程池分配多个线程同时去处理不同学生的成绩更新及对应的 Redis 排行榜数据更新任务,提高并发处理能力,但要注意处理好线程安全问题,比如对共享资源(如 Redis 连接池等)的访问控制,避免出现数据不一致等错误情况。

相关文章:

用java和redis实现考试成绩排行榜

一、引言 在各类考试场景中&#xff0c;无论是学校里的学业测试&#xff0c;还是线上培训课程的考核&#xff0c;亦或是各类竞赛的选拔&#xff0c;成绩排行榜都是大家颇为关注的一个元素。它不仅能直观地展示考生之间的成绩差异&#xff0c;激发大家的竞争意识&#xff0c;还能…...

hhdb数据库介绍(9-24)

计算节点参数说明 failoverAutoresetslave 参数说明&#xff1a; PropertyValue参数值failoverAutoresetslave是否可见是参数说明故障切换时&#xff0c;是否自动重置主从复制关系默认值falseReload是否生效否 参数设置&#xff1a; <property name"failoverAutor…...

HDMI数据传输三种使用场景

视频和音频的传输 在HDMI传输音频中有3种方式进行传输&#xff0c;第一种将音频和视频信号被嵌入到同一数据流中&#xff0c;通过一个TMDS&#xff08;Transition Minimized Differential Signaling&#xff09;通道传输。第二种ARC。第三张种eARC。这三种音频的传输在HDMI线中…...

unigui 登陆界面

新建项目&#xff0c;因为我的Main页面做了其他的东西&#xff0c;所以我在这里新建一个form File -> New -> From(Unigui) -> 登录窗体 添加组件&#xff1a;FDConnection&#xff0c;FDQuery&#xff0c;DataSource&#xff0c;Unipanel和几个uniedit&#xff0c;…...

无人机 PX4飞控 | CUAV 7-Nano 飞行控制器介绍与使用

无人机 PX4飞控 | CUAV 7-Nano 飞行控制器介绍与使用 7-Nano简介硬件参数接口定义模块连接供电部分遥控器电机 固件安装 7-Nano简介 7-Nano是一款针对小型化无人系统设备研发的微型自动驾驶仪。它由雷迅创新自主研发和生产&#xff0c;其创新性的采用叠层设计&#xff0c;在极…...

安装spark

spark依赖java和scale。所以先安装java&#xff0c;再安装scale&#xff0c;再是spark。 总体教程跟着这个链接 我跟着这个教程走安装java链接&#xff0c;但是有一些不同&#xff0c;原教程有一些错误&#xff0c;在环境变量设置的地方。 java 首先下载jdk。 先看自己的环境…...

佛山三水戴尔R740服务器黄灯故障处理

1&#xff1a;佛山三水某某大型商场用户反馈一台DELL PowerEdge R740服务器近期出现了黄灯警告故障&#xff0c;需要冠峰工程师协助检查故障灯原因。 2&#xff1a;工程师协助该用户通过笔记本网线直连到服务器尾部的IDRAC管理端口&#xff0c;默认ip 192.168.0.120 密码一般在…...

大学课程项目中的记忆深刻 Bug —— 一次意外的数组越界

开头 在编程的世界里&#xff0c;每一行代码都像是一个小小的宇宙&#xff0c;承载着开发者的心血与智慧。然而&#xff0c;即便是最精心编写的代码&#xff0c;也难免会遇到那些突如其来的 bug&#xff0c;它们就像是潜伏在暗处的小怪兽&#xff0c;时不时跳出来捣乱。 在我…...

html数据类型

数据类型是字面含义&#xff0c;表示各种数据的类型。在任何语言中都存在数据类型&#xff0c;因为数据是各式各样。 1.数值类型 number let a 1; let num 1.1; // 整数小数都是数字值 ​ // 数字肯定有个范围 正无穷大和负无穷大 // Infinity 正无穷大 // -Infinity 负…...

Kotlin Multiplatform 未来将采用基于 JetBrains Fleet 定制的独立 IDE

近期 Jetbrains 可以说是动作不断&#xff0c;我们刚介绍了 IntelliJ IDEA 2024.3 K2 模式发布了稳定版支持 &#xff0c;而在官方最近刚调整过的 Kotlin Multiplatform Roadmap 优先关键事项里&#xff0c;可以看到其中就包含了「独立的 Kotlin Multiplatform IDE&#xff0c;…...

Redis中常见的数据类型及其应用场景

五种常见数据类型 Redis中的数据类型指的是 value存储的数据类型&#xff0c;key都是以String类型存储的&#xff0c;value根据场景需要&#xff0c;可以以String、List等类型进行存储。 各数据类型介绍&#xff1a; Redis数据类型对应的底层数据结构 String 类型的应用场景 常…...

代理IP在后端开发中的应用与后端工程师的角色

目录 引言 代理IP的基本概念和工作原理 代理IP在后端开发中的应用 网络爬虫与数据采集 负载均衡与性能优化 安全防护与隐私保护 后端工程师在使用代理IP时面临的挑战 结论 引言 在数字化时代&#xff0c;网络技术的飞速发展极大地推动了各行各业的发展。其中&#xff…...

工作流和流程引擎有什么区别?

在企业的数字化转型中&#xff0c;如何提升效率、优化业务流程是每个管理者都在思考的问题。而在这个过程中&#xff0c;工作流&#xff08;Workflow&#xff09;和流程引擎&#xff08;Process Engine&#xff09;这两个术语频频出现&#xff0c;成为企业流程自动化和智能化的…...

【SpringBoot】27 拦截器

Gitee仓库 https://gitee.com/Lin_DH/system 介绍 拦截器&#xff1a;拦截器是 Spring 框架提供的核心功能之一&#xff0c;主要用来拦截用户请求&#xff0c;在指定方法前后&#xff0c;根据业务需要执行预先设定的代码。 拦截器允许开发人员提前预定义一些逻辑&#xff0c…...

AI对开发者的影响,以及传统软件开发 与 AI参与的软件开发区别

AI 大模型&#xff0c;尤其是像 GPT-4、BERT 这样的语言模型&#xff0c;正以深远的影响改变着软件开发流程。传统的软件开发流程通常依赖开发人员进行代码编写、测试、调试等工作&#xff0c;但随着 AI 技术的进步&#xff0c;AI 可以承担越来越多的任务&#xff0c;自动化和优…...

HBase Java基础操作

Apache HBase 是一个开源的、分布式的、可扩展的大数据存储系统&#xff0c;它基于 Google 的 Bigtable 模型。使用 Java 操作 HBase 通常需要借助 HBase 提供的 Java API。以下是一个基本的示例&#xff0c;展示了如何在 Java 中连接到 HBase 并执行一些基本的操作&#xff0c…...

关于一次开源java spring快速开发平台项目RuoYi部署的记录

关于一次开源java spring快速开发平台项目RuoYi部署的记录 本次因为需要一些练习环境&#xff0c;想要快速搭建一个javaweb 项目作为练习环境&#xff0c;经过查询和实验找到一个文档详细&#xff0c;搭建简单&#xff0c;架构也相对比较新的开源项目RuoYi。 项目介绍&#xf…...

【AI编程实战】安装Cursor并3分钟实现Chrome插件(保姆级)

Cursor介绍 https://www.cursor.com/ 一句话介绍&#xff1a;AI代码编辑器&#xff0c;当前最火的AI编程器 软件下载与安装 下载 打开Cursor官网下载&#xff0c;会根据操作系统的差别进行选择 https://www.cursor.com/ 这里下载的内容很小&#xff0c;是个安装器&#x…...

【Chatgpt】如何通过分层Prompt生成更加细致的图文内容

如何通过分层Prompt生成更加细致的图文内容 利用ChatGPT和类似的生成式AI模型&#xff0c;通过分层Prompt设计可以生成更具层次感和细节的图文内容。分层Prompt的核心在于将需求分解成多层次的指令&#xff0c;从宏观到微观逐步细化&#xff0c;最终形成高质量的内容输出。 一…...

中间件--laravel进阶篇

laravel版本11.31,这中间件只有3种,分别是全局中间件,路由中间件,控制器中间件。相比thinkphp8,少了一个应用中间件。 一、创建中间件 laravel创建中间件可以使用命令的方式创建,非常方便。比如php artisan make:middleware EnsureTokenIsValid。EnsureTokenIsValid是中间…...

【vue】vue中.sync修饰符如何使用--详细代码对比

.sync修饰符作用 .sync修饰符是一个语法糖&#xff0c;可以简化父子组件通信操作&#xff0c;当子组件想改变父组件数值时&#xff0c;父组件只需要使用.sync修饰符&#xff0c;子组件使用props接收属性&#xff0c;再使用this.$emit(update:属性, 值);就可以实现子组件更新父…...

repmgr安装及常用运维指令

简介 repmgr 由 EDB 与其他个人和组织的贡献一起开发&#xff0c;安装部署相对较为简单 安装 repmgr官网上传对应的安装到服务器上 安装前/etc/hosts IP映射、始终同步、免密通信本文忽略 repmgr的安装相对较为简单,目前repmgr-5仅仅支持到postgresql-15 postgresql必要参数…...

RedHat系统配置静态IP

1、执行nmtui命令进入字符配置界面如下图所示 2、选择编辑连接进入 3、选择编辑进入后&#xff0c;将IPv4设置为手动模式后&#xff0c;选择显示后进行ip地址、网关、DNS的配置&#xff0c;配置完成后选择确定退出编辑 4、进入主界面后选择启用连接进入后&#xff0c;选择启用&…...

nvm和nrm的安装与使用

NVM相关请跳转&#xff1a; Node版本管理器nvm的安装与使用 nrm 的安装与使用 nrm&#xff08;NPM Registry Manager&#xff09;是一个用于管理和切换 NPM 源的工具。它允许你在多个 NPM 源之间快速切换&#xff0c;以提高包管理的速度和效率。以下是 nrm 的安装和使用方法&…...

10大核心应用场景,解锁AI检测系统的智能安全之道

随着工业化和自动化的快速推进&#xff0c;高风险作业场景的安全管理需求日益增加。思通数科AI检测系统以深度学习、计算机视觉和多模态数据融合技术为基础&#xff0c;通过智能化监控和实时反馈&#xff0c;为企业提供全面的作业安全和流程管理解决方案。本文将详细解读该系统…...

香豆烤馍:传统美食中的烟火记忆

食家巷香豆烤馍&#xff0c;承载着甘肃人的乡愁与记忆。它那朴实的外表下&#xff0c;蕴含着丰富的口感和深厚的文化底蕴。烤馍的制作过程充满了烟火气息。选用优质的面粉&#xff0c;经过发酵、揉制等多道工序&#xff0c;再放入传统的烤炉中慢慢烘烤。这个过程需要经验丰富的…...

金融量化交易模型的探索与发展

随着全球金融市场的不断变化与技术进步&#xff0c;量化交易逐渐成为机构和个人投资者的重要选择。作为数据驱动的交易方式&#xff0c;量化交易通过科学建模和技术手段&#xff0c;有效提升了交易效率与决策精准度。本文将探讨金融量化交易模型的创新探索与未来发展方向。 量化…...

灾难恢复计划 (DRP)

灾难恢复计划 (DRP)   目录 灾难恢复计划 (DRP) 1 1. 简介 2 2. 目的 2 3. 范围 3 4. 风险评估 3 5. 容灾方案 3 6. 关键系统恢复优先级 4 7. 恢复流程 4 8. 测试与维护 5 9. 联系信息 5 10. 批准与分发 5 11. 附录 5 1. 简介 灾难恢复计…...

Makefile 之 wordlist

wordlist $(wordlist <s>,<e>,<text> ) 名称&#xff1a;取单词串函数——wordlist。 功能&#xff1a;从字符串<text>中取从<s>开始到<e>的单词串。<s>和<e>是一个数字。 返回&#xff1a;返回字符串<text>中从…...

半导体工艺与制造篇1 绪论

我们为什么要研究半导体&#xff1f;半导体凭什么可以成为电子信息行业的基础呢&#xff1f; 这就要说到半导体的一个重要特点&#xff1a;可以通过控制掺杂率来控制它的导电性 集成电路IC的生产 集成电路IC的生产包括&#xff1a; #mermaid-svg-rWB59zU4pI2cGloo {font-fami…...

平台门户网站建设/最好的网站设计公司

表格是我们平常办公的时候非常常见的文档数据&#xff0c;表格工具也给我们提供了非常强大的功能&#xff0c;提高了我们办公的效率。不过在计算机中&#xff0c;表格的编辑也是非常多种多样的&#xff0c;除了Excel外&#xff0c;word等一些办公软件也能够编辑表格&#xff0c…...

商务网站开发工具/app开发平台

前言 小编在spring的beanPostProcessor详解以及模拟Aop中讲到springBean创建的最后一步调用BeanPostProcessor的postProcessAfterInitialization来进行Aop的处理&#xff0c;Aop的模拟是使用了JDK的动态代理&#xff0c;这次小编使用cglib来实现。 问题 spring在面试的时候总…...

记事本做网站表格/网页优化seo广州

由于重重原因&#xff0c;我取消maven的下载&#xff08;可能是jar包没有公服镜像&#xff09;。导致重新打开sts 时发生异常。 在网上寻找到资料&#xff0c;发现需要删除${workspace}\.metadata\.plugins\org.eclipse.e4.workbench\workbench.xmi文件即可。 参考&#xff1a;…...

连州网站建设/站长工具seo排名

2020/04/03每日十句英语口语 It sounds great!&#xff0e; 听起来很不错。 It‘s a fine day。 今天是个好天。 So far&#xff0c;So good&#xff0e; 目前还不错。 What time is it? 几点了? You can make it! 你能做到! Control yourself! 克制一下! Do me a favor? 帮…...

做公司网站的目的是什么/站长工具平台

2012.8&#xff0c;结束欧洲意法两国游之后&#xff0c;经过漫长的国际航线&#xff0c;终于飞临香港上空。由于喜欢拍照&#xff0c;总是尽量找人调换靠窗的位置&#xff0c;无奈这次没有换成功&#xff0c;所以没有赶上更好的视角&#xff0c;都是匆忙间拍下来的&#xff0c;…...

贵阳网站建设制作/营销软文范例大全100

C语言常用宏定义技巧用C语言编程&#xff0c;宏定义是个很重要的编程技巧。用好了宏定义&#xff0c;它可以增强程序的可读性、可移植性、方便性、灵活性等等。 1. 防止一个头文件被重复包含&#xff1a; #ifndef COMDEF_H #define COMDEF_H //头文件内容 #endif 当你所建的工程…...