项目的一些难点

news/发布时间2024/5/18 14:20:29

1.不用redis?分布式锁,如何防止用户重复点击?

1.乐观锁

乐观锁是一种在数据库层面上避免并发冲突的机制。它通常通过在数据库记录中添加一个版本号(或时间戳)来实现。每次更新记录时,都会检查版本号是否与数据库中的版本号匹配,如果匹配,则更新数据并将版本号加一。这确保了在更新期间没有其他操作更改了记录。

  • 应用场景:适用于更新操作并不频繁,且冲突概率较低的场景

代码实现:

//实体类:注意@Version注解
@Entity
@Table(name = "form_submissions")
public class FormSubmission {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;@Column(name = "user_id")private Long userId;@Column(name = "form_data")private String formData;@Column(name = "version")@Versionprivate int version;// 构造函数、Getter和Setter略去
}//业务层
@Service
public class FormSubmissionService {@Autowiredprivate FormSubmissionRepository repository;@Transactionalpublic String submitForm(Long userId, String formData) {Optional<FormSubmission> existingSubmission = repository.findById(userId);FormSubmission submission;if (existingSubmission.isPresent()) {// 更新现有记录submission = existingSubmission.get();submission.setFormData(formData);} else {// 创建新的记录submission = new FormSubmission();submission.setUserId(userId);submission.setFormData(formData);submission.setVersion(0); // 或根据需要设置初始版本号}try {repository.save(submission);return "表单提交成功。";} catch (org.springframework.orm.ObjectOptimisticLockingFailureException e) {// 捕获乐观锁异常,处理冲突return "提交失败,请不要重复提交。";}}
}//没有注解的时候sql层
UPDATE form_submissions
SET form_data = '新的表单数据', version = version + 1
WHERE id = ? AND version = ?;

2.数据库悲观锁

悲观锁通常通过数据库提供的锁机制实现,如 SQL 的 SELECT FOR UPDATE 语句,这会锁定被选中的数据库行,直到事务完成。这种方法适用于高冲突环境,因为它会阻止其他任何尝试修改这些行的操作。

  • 应用场景:适用于更新操作频繁,且冲突概率高的场景。

代码实现:

@Service
public class FormSubmissionService {@Autowiredprivate FormSubmissionRepository repository;@Transactionalpublic boolean submitForm(Long userId, String formData) {Optional<FormSubmission> existingSubmission = repository.findByUserIdForUpdate(userId);if (existingSubmission.isPresent()) {// 存在记录,处理重复提交逻辑return false;} else {// 不存在记录,保存新的表单提交FormSubmission submission = new FormSubmission();submission.setUserId(userId);submission.setFormData(formData);repository.save(submission);return true;}}
}//sql层代码
@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("SELECT fs FROM FormSubmission fs WHERE fs.userId = :userId")
Optional<FormSubmission> findByUserIdForUpdate(@Param("userId") Long userId);

3.基于内存的锁

如果你的应用程序运行在单个实例或能够使用共享内存系统(如 Hazelcast、Apache Ignite),可以使用内存中的数据结构来实现锁逻辑。例如,使用一个全局哈希表存储正在进行的操作的标识符,来防止重复。

  • 应用场景:适用于单实例应用或者有共享内存系统的分布式应用。
@Service
public class FormSubmissionService {private final Map<Long, Lock> userLocks = new HashMap<>();public String submitForm(Long userId, String formData) {Lock lock = userLocks.computeIfAbsent(userId, k -> new ReentrantLock());if (lock.tryLock()) {try {// 模拟表单处理逻辑Thread.sleep(1000); // 假设处理需要一段时间System.out.println("表单数据处理: " + formData);return "表单提交成功。";} catch (InterruptedException e) {Thread.currentThread().interrupt();return "表单处理中断。";} finally {lock.unlock();}} else {return "正在处理中,请不要重复提交。";}}
}

4.应用程序级的去重逻辑

在应用程序级别实现去重逻辑,例如,通过在前端禁用提交按钮,直到请求完成,或者在后端设置一个短暂的时间窗口,在这个窗口内忽略来自同一用户的重复请求。

  • 应用场景:适用于需要快速实现且冲突概率不高的场景。

5.唯一标识符

要求客户端在请求时生成一个唯一的标识符(如 UUID),并在服务器端检查这个标识符是否已经被处理。这个标识符可以存储在内存或数据库中,以确保每个请求只被处理一次。

  • 应用场景:适合于任何需要确保请求唯一性的场景,特别是在分布式系统中。

代码实现:

@Service
public class RequestService {@Autowiredprivate RequestIdRepository requestIdRepository;public boolean processRequest(String requestId) {// 检查请求ID是否已存在Optional<RequestId> existingRequestId = requestIdRepository.findById(requestId);if (existingRequestId.isPresent()) {// 请求ID已存在,拒绝重复处理return false;} else {// 请求ID不存在,处理请求RequestId newRequestId = new RequestId();newRequestId.setId(requestId);requestIdRepository.save(newRequestId); // 保存请求ID标记为已处理// 在这里执行其他请求处理逻辑...return true;}}
}

在这个实现中,客户端需要生成一个 UUID 并在每次请求时发送这个 UUID 作为 `requestId` 参数。服务器通过检查这个 `requestId` 是否已经存在于 `request_ids` 表中来防止重复处理相同的请求。这种方法适用于分布式系统中确保请求的唯一性,有效地防止了用户因为多次点击导致的重复请求问题。

2.Cookie、Session、Token、JWT之间的区别

傻傻分不清之 Cookie、Session、Token、JWT - 掘金

3.40亿个QQ号,限制1G内存,如何去重?

1.位图(Bitmap)

图是一种非常高效的数据结构,通过使用1个位来标记某个元素是否存在。假设我们使用40亿位(或者说500MB)的内存空间,就可以表示40亿个不同的QQ号。

  1. 初始化位图:创建一个大约500MB大小的位图,每个位对应一个可能的QQ号。由于QQ号可能不会完全连续,我们需要根据实际QQ号的范围来调整位图的大小。

  2. 标记QQ号:遍历所有QQ号,对每个QQ号,计算它在位图中的位置,并将相应的位设置为1。

  3. 去重:再次遍历QQ号,通过检查位图中对应位的值,可以判断一个QQ号是否已经出现过。

这种方法的缺点是,如果QQ号的范围非常大,位图的大小可能会超出1GB的内存限制。

代码实现:

public class Bitmap {private byte[] bits;public Bitmap(int size) {bits = new byte[(size + 7) / 8];}public void set(int k) {int byteIndex = k / 8;int bitIndex = k % 8;bits[byteIndex] |= (1 << bitIndex);}public boolean get(int k) {int byteIndex = k / 8;int bitIndex = k % 8;return (bits[byteIndex] & (1 << bitIndex)) != 0;}
}// 假设QQ号范围在一定区间内,这里简化处理
Bitmap bitmap = new Bitmap(4000000000); // 大约需要500MB内存
// 设置QQ号
bitmap.set(qqNumber);
// 检查QQ号是否已存在
boolean exists = bitmap.get(qqNumber);

2.布隆过滤器(Bloom Filter)

布隆过滤器是一种空间效率极高的概率型数据结构,用于判断一个元素是否在集合中:

  1. 初始化布隆过滤器:根据数据量和可接受的误判率初始化布隆过滤器。

  2. 添加元素:遍历QQ号,将每个QQ号添加到布隆过滤器中。

  3. 预过滤:再次遍历QQ号,首先使用布隆过滤器检查是否可能已经存在。由于布隆过滤器存在一定的误判率,对于判断存在的元素,需要进一步确认。

布隆过滤器适用于快速预过滤,减少需要进一步处理的数据量,但需要额外的机制来处理误判。

public class BloomFilter {private BitSet hashes;private int size;public BloomFilter(int size) {this.size = size;this.hashes = new BitSet(size);}private int hash(Object obj, int k) {return Math.abs(obj.hashCode() * k) % size;}public void add(Object obj) {for (int k = 1; k <= 3; k++) { // 使用3个不同的哈希函数int hash = hash(obj, k);hashes.set(hash);}}public boolean mightContain(Object obj) {for (int k = 1; k <= 3; k++) {int hash = hash(obj, k);if (!hashes.get(hash)) {return false; // 如果有一个位不是1,那么对象肯定没有添加}}return true; // 可能包含}
}// 使用
BloomFilter filter = new BloomFilter(100000000); // 需要调整大小以适应内存限制
filter.add(qqNumber);
boolean mightExist = filter.mightContain(qqNumber);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.bcls.cn/jaHj/2037.shtml

如若内容造成侵权/违法违规/事实不符,请联系编程老四网进行投诉反馈email:xxxxxxxx@qq.com,一经查实,立即删除!

相关文章

Commonjs 和 Es Module详解

一 前言 今天我们来深度分析一下 Commonjs 和 Es Module&#xff0c;希望通过本文的学习&#xff0c;能够让大家彻底明白 Commonjs 和 Es Module 原理&#xff0c;能够一次性搞定面试中遇到的大部分有关 Commonjs 和 Es Module 的问题。 带上疑问开始今天的分析&#xff1a; …

PyTorch深度学习实战(37)——CycleGAN详解与实现

PyTorch深度学习实战&#xff08;37&#xff09;——CycleGAN详解与实现 0. 前言1. CycleGAN 基本原理2. CycleGAN 模型分析3. 实现 CycleGAN小结系列链接 0. 前言 CycleGAN 是一种用于图像转换的生成对抗网络(Generative Adversarial Network, GAN)&#xff0c;可以在不需要配…

《深入浅出 Spring Boot 3.x》预计3月份发版

各位&#xff0c;目前本来新书《深入浅出 Spring Boot 3.x》已经到了最后编辑排版阶段&#xff0c;即将在3月份发布。 目录&#xff1a; 现在把目录截取给大家&#xff1a; 主要内容&#xff1a; 本书内容安排如下。 ● 第 1 章和第 2 章讲解 Spring Boot 和传统 Spri…

stm32——hal库学习笔记(定时器)

这里写目录标题 一、定时器概述&#xff08;了解&#xff09;1.1&#xff0c;软件定时原理1.2&#xff0c;定时器定时原理1.3&#xff0c;STM32定时器分类1.4&#xff0c;STM32定时器特性表1.5&#xff0c;STM32基本、通用、高级定时器的功能整体区别 二、基本定时器&#xff0…

找不到android.support.v4.app.Fragment的类文件

问题 android.support.v4.app.Fragment的类文件 详细问题 笔者Android项目开发集成QQ登录 控制台报错 D:\AndroidProjects\assistingAgriculture\app\src\main\java\com\example\assistingagriculture\activity\normal_mode\QQLoginActivity.java:43: 错误: 无法访问Fragme…

k-邻近算法(kNN)

目录 k-近邻算法概述 k-近邻算法的一般流程 kNN算法伪代码 k-近邻算法概述 优点&#xff1a;精度高、对异常值不敏感、无数据输入假定 缺点&#xff1a;计算复杂度高、空间复杂度高 适用数据范围&#xff1a;数值型和标称型 k-近邻算法的一般流程 &#xff08;1&#x…

【小呆的力学笔记】弹塑性力学的初步认知五:初始屈服条件(1)

文章目录 3. 初始屈服条件3.1 两个假设以及屈服条件基本形式3.2 π \pi π平面、Lode参数3.3 屈服曲线的一般特征 3. 初始屈服条件 3.1 两个假设以及屈服条件基本形式 在简单拉伸时&#xff0c;材料的屈服很明确&#xff0c;即 σ > σ s (1) \sigma\gt\sigma_s\tag{1} …

[Docker实战] 旭日X3派上Docker Openwrt +Samba 实现局域网NAS 开启AP模式

​ &#x1f308; 博客个人主页&#xff1a;Chris在Coding &#x1f3a5; 本文所属专栏&#xff1a;[旭日X3派] [Docker实战] ❤️ 前置学习专栏&#xff1a;[Linux学习] ⏰ 我们仍在旅途 …

Sora:新一代实时音视频通信框架

一、Sora简介 Sora是一个开源的实时音视频通信框架&#xff0c;旨在提供高效、稳定、可扩展的音视频通信解决方案。它基于WebRTC技术&#xff0c;支持跨平台、跨浏览器的实时音视频通信&#xff0c;并且具备低延迟、高并发、易集成等特点。 --点击进入Sora(一定要科学哦&#x…

【PX4学习笔记】08.PX4中显示在QGC的参数讲解

目录 文章目录 目录PX4参数的获取方式PX4参数的分类PX4常用参数的意义和调整注意事项PX4的姿态控制和位置控制参数常规的调整参数流程 [!NOTE] ​ 姿态控制的P-PID&#xff1a;roll、pitch、yaw三个角速度的内环PID&#xff0c;外环P的控制 ​ 位置控制的P-PID&#xff1a;xy水…

小迪安全27WEB 攻防-通用漏洞SQL 注入Tamper 脚本Base64Jsonmd5 等

#知识点&#xff1a; 1、数据表现格式类型注入 2、字符转义绕过-宽字节注入 3、数字&字符&搜索&编码&加密等 #参考资料&#xff1a; https://www.cnblogs.com/bmjoker/p/9326258.html 扫描&#xff0c;利用工具等都不会自动判断数据类型&#xff0c…

Chrome插件精选 — 缓存清理

Chrome实现同一功能的插件往往有多款产品&#xff0c;逐一去安装试用耗时又费力&#xff0c;在此为某一类型插件挑选出比较好用的一款或几款&#xff0c;尽量满足界面精致、功能齐全、设置选项丰富的使用要求&#xff0c;便于节省一个个去尝试的时间和精力。 1. Chrome清理大师…

深度学习基础——SSD目标检测

SSD网络介绍 使用多个特征图作为特征预测层。 SSD (Single Shot MultiBox Detector)于2016年提出。当网络输入为300300大小时&#xff0c;在VOC2007测试集上达到74.3%的mAP;当输入是512512大小时&#xff0c;达到了76.9%的mAP SSD_Backbone部分介绍 不变的部分 特征提取网…

QT day2

#include "widget.h"Widget::Widget(QWidget *parent): QWidget(parent) {//设置窗口标题setWindowTitle("有课云登录");//设置窗口图标setWindowIcon(QIcon("C:\\Users\\Administrator\\Desktop\\pictrue\\login.png"));//设置固定窗口大小resi…

5 原型模式 Prototype

1.模式定义: 指原型实例指定创建对象的种类&#xff0c;并且通过拷贝这些原型创建新的对象 2.应用场景&#xff1a; 当代码不应该依赖于需要复制的对象的具体类时&#xff0c;请使用Prototype模式。 Spring源码中的应用 org.springframework.beans.factory.support.AbstractB…

动态规划--线性DP最长上升子序列及其二分优化

1、B站视频链接&#xff1a;E03 线性DP 最长上升子序列_哔哩哔哩_bilibili #include <bits/stdc.h> using namespace std; int n9; int a[101]{0,5,7,1,9,4,6,2,8,3}; int f[101]; //f[i]表示以a[i]为结尾的 //最长上升子序列的长度 int main(){int i,j,ans1;for(int i1…

【招聘】资深后端开发工程师-飞书IM(杭州、北京)

职位描述 团队介绍&#xff1a;飞书是字节跳动旗下先进企业协作与管理平台&#xff0c;围绕目标、信息与人三个维度全方位助力组织升级。一站式整合即时沟通、日历、音视频会议、文档、云盘、邮箱等办公协作套件&#xff0c;让组织和个人工作更高效更愉悦。飞书目前已服务包括互…

GitHub使用记录

1.创建仓库 2.删除仓库 翻到最下面 3.将本地文件同步到云端库上 Github——git本地仓库建立与远程连接&#xff08;最详细清晰版本&#xff01;附简化步骤与常见错误&#xff09;_将本地仓库与远程仓库关联-CSDN博客 第三步参考&#xff1a;Github——git本地仓库建立与远程连…

华为23年9月笔试原题,巨详细题解,附有LeetCode测试链接

文章目录 前言思路主要思路关于f函数的剖析Code就到这&#xff0c;铁子们下期见&#xff01;&#xff01;&#xff01;&#xff01; 前言 铁子们好啊&#xff01;今天阿辉又给大家来更新新一道好题&#xff0c;下面链接是23年9月27的华为笔试原题&#xff0c;LeetCode上面的ha…

C#知识点-13(进程、多线程、使用Socket实现服务器与客户端通信)

进程 定义&#xff1a;每一个正在运行的应用程序&#xff0c;都是一个进程 进程不等于正在运行的应用程序。而是为应用程序的运行构建一个运行环境 Process[] pros Process.GetProcesses();//获取电脑中所有正在运行的进程//通过进程&#xff0c;直接打开文件//告诉进程&…
推荐文章