项目的一些难点
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号。
-
初始化位图:创建一个大约500MB大小的位图,每个位对应一个可能的QQ号。由于QQ号可能不会完全连续,我们需要根据实际QQ号的范围来调整位图的大小。
-
标记QQ号:遍历所有QQ号,对每个QQ号,计算它在位图中的位置,并将相应的位设置为1。
-
去重:再次遍历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)
布隆过滤器是一种空间效率极高的概率型数据结构,用于判断一个元素是否在集合中:
-
初始化布隆过滤器:根据数据量和可接受的误判率初始化布隆过滤器。
-
添加元素:遍历QQ号,将每个QQ号添加到布隆过滤器中。
-
预过滤:再次遍历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);
相关文章:
项目的一些难点
1.不用redis?分布式锁,如何防止用户重复点击? 1.乐观锁 乐观锁是一种在数据库层面上避免并发冲突的机制。它通常通过在数据库记录中添加一个版本号(或时间戳)来实现。每次更新记录时,都会检查版本号是否与数据库中的…...
Kubernetes 卷存储 NFS | nfs搭建配置 原理介绍 nfs作为存储卷使用
1、NFS介绍 NFS(Network File System)是一种分布式文件系统协议,允许客户端远程访问服务器上的文件,实现数据共享。它整合多个存储设备为统一文件系统,方便数据存储和管理,支持负载均衡和故障转移…...
开启智能互动新纪元——ChatGPT提示词工程的引领力
目录 提示词工程的引领力 高效利用ChatGPT提示词方法 提示词工程的引领力 近年来,随着人工智能技术的迅猛发展,ChatGPT提示词工程正逐渐崭露头角,为智能互动注入了新的活力。这一技术的引入,使得人机交流更加流畅、贴近用户需求&…...
ElasticSearch语法
Elasticsearch 概念 入门学习: Index索引>MySQL 里的表(table)建表、增删改查(查询需要花费的学习时间最多)用客户端去调用 ElasticSearch(3 种)语法:SQL、代码的方法(4 种语法) ES 相比于 MySQL,能够自动帮我们做分词,能够非常高效、灵活地查询内…...
SMT贴片加工厂需要哪些加工资料
SMT贴片加工中在评估报价的时候需要给到SMT贴片加工厂以下资料,以便工程师和采购进行工艺和报价评估。 在SMT加工中如果需要供应商提供一站式的加工服务,那么在前期就需要更频繁的沟通和配合,包工包料服务是需要PCB制板资料和制板说明、BOM清…...
jmeter下载base64加密版pdf文件
一、何为base64加密版pdf文件 如下图所示,接口jmeter执行后,返回一串包含大小写英文字母、数字、、/、的长字符串,直接另存为pdf文件后,文件有大小,但是打不开;另存为doc文件后,打开可以看到和…...
【regex】正则表达式
集合 [0-9.] [0-9.\-] 例子 正则表达式,按照规则写,写的时候应该不算困难,但是可读性差 不同语言中regex会有微小的差异 vim 需要转义, perl/python中不需要转义 锚位 \b am\b i am 命名 / 命名捕获组 ( 捕获组(…...
78.Spring和SpringBoot的关系和区别?
一、Spring和SpringBoot的关系和区别 SpringBoot是Spring生态的产品。 Spring Framework是一个容器框架 SpringBoot 它不是一个框架、它是一个可以快速构建基于Spring的脚手架(里面包含了Spring和各种框架),为开发Spring生态其他框架铺平道路࿰…...
【PyTorch][chapter 17][李宏毅深度学习]【无监督学习][ Auto-encoder]
前言: 本篇重点介绍AE(Auto-Encoder) 自编码器。这是深度学习的一个核心模型. 自编码网络是一种基于无监督学习方法的生成类模型,自编码最大特征输出等于输入 Yann LeCun&Bengio, Hinton 对无监督学习的看法. 目录: AE 模型原…...
Modern C++ std::variant的实现原理
前言 std::variant是C17标准库引入的一种类型,用于安全地存储和访问多种类型中的一种。它类似于C语言中的联合体(union),但功能更为强大。与联合体相比,std::variant具有类型安全性,可以判断当前存储的实际…...
⭐北邮复试刷题LCR 018. 验证回文串__双指针 (力扣119经典题变种挑战)
LCR 018. 验证回文串 给定一个字符串 s ,验证 s 是否是 回文串 ,只考虑字母和数字字符,可以忽略字母的大小写。 本题中,将空字符串定义为有效的 回文串 。 示例 1: 输入: s “A man, a plan, a canal: Panama” 输出: true 解释…...
C++面试:数据库的权限管理数据库的集群和高可用
目录 一、数据库的权限管理 1. 用户和角色管理 用户管理 实例举例(以MySQL为例): 角色管理 实例举例(以MySQL为例): 总结 2. 权限和授权 用户和角色管理 用户管理 角色管理 权限和授权 权限 授…...
个人搭建部署gpt站点
2024搭建部署gpt 参照博客 https://cloud.tencent.com/developer/article/2266669?areaSource102001.19&traceIdRmFvGjZ9BeaIaFEezqQBj博客核心点 准备好你的 OpenAI API Key; 点击右侧按钮开始部署: Deploy with Vercel,直接使用 Github 账号登…...
samber/lo 库的使用方法: condition
samber/lo 库的使用方法: condition samber/lo 是一个 Go 语言库,使用泛型实现了一些常用的操作函数,如 Filter、Map 和 FilterMap。汇总目录页面 这个库函数太多,因此我决定按照功能分别介绍,本文介绍的是 samber/l…...
Chrome插件精选 — 缓存清理
Chrome实现同一功能的插件往往有多款产品,逐一去安装试用耗时又费力,在此为某一类型插件挑选出比较好用的一款或几款,尽量满足界面精致、功能齐全、设置选项丰富的使用要求,便于节省一个个去尝试的时间和精力。 1. Chrome清理大师…...
Redis之缓存穿透问题解决方案实践SpringBoot3+Docker
文章目录 一、介绍二、方案介绍三、Redis Docker部署四、SpringBoot3 Base代码1. 依赖配置2. 基本代码 五、缓存优化代码1. 校验机制2. 布隆过滤器3. 逻辑优化 一、介绍 当一种请求,总是能越过缓存,调用数据库,就是缓存穿透。 比如当请求一…...
每日shell脚本之超级整合程序3.0
每日shell脚本之超级整合程序3.0 本期带来之前的升级版2.0整合脚本程序,学习工作小利器,同时模块化构建方便二次开发。 上图 上源码 #!/usr/bin/bash # *******************************************# # * CDDN : M乔木 # # * qq邮箱 …...
Docker介绍与使用
Docker介绍与使用 目录: 一、Docker介绍 1、Docker概述与安装 2、Docker三要素 二、Docker常用命令的使用 1、镜像相关命令 2、容器相关命令 三、Docker实战之下载mysql、redis、zimg 一、Docker介绍 Docker是一个开源的应用容器引擎,让开发者可以打包…...
Gin框架: 使用go-ini配置参数与不同环境下的配置部署
关于 INI 配置文件与go-ini 1 )概述 在INI配置文件中可以处理各种数据的配置INI文件是一种简单的文本格式,常用于配置软件的各种参数go-ini 是地表 最强大、最方便 和 最流行 的 Go 语言 INI 文件操作库 Github 地址:https://github.com/go-…...
探究网络工具nc(netcat)的使用方法及安装步骤
目录 🐶1. 什么是nc(netcat)? 🐶2. nc(netcat)的基本使用方法 2.1 🥙使用 nc 进行端口监听 2.2 🥙使用 nc 进行端口扫描 2.3 🥙使用 Netcat 进行文件传输…...
Linux 文件类型,目录与路径,文件与目录管理
文件类型 后面的字符表示文件类型标志 普通文件:-(纯文本文件,二进制文件,数据格式文件) 如文本文件、图片、程序文件等。 目录文件:d(directory) 用来存放其他文件或子目录。 设备…...
FFmpeg 低延迟同屏方案
引言 在实时互动需求激增的当下,无论是在线教育中的师生同屏演示、远程办公的屏幕共享协作,还是游戏直播的画面实时传输,低延迟同屏已成为保障用户体验的核心指标。FFmpeg 作为一款功能强大的多媒体框架,凭借其灵活的编解码、数据…...
Docker 运行 Kafka 带 SASL 认证教程
Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明:server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…...
linux arm系统烧录
1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 (忘了有没有这步了 估计有) 刷机程序 和 镜像 就不提供了。要刷的时…...
[Java恶补day16] 238.除自身以外数组的乘积
给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O(n) 时间复杂度…...
在WSL2的Ubuntu镜像中安装Docker
Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包: for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...
【HarmonyOS 5 开发速记】如何获取用户信息(头像/昵称/手机号)
1.获取 authorizationCode: 2.利用 authorizationCode 获取 accessToken:文档中心 3.获取手机:文档中心 4.获取昵称头像:文档中心 首先创建 request 若要获取手机号,scope必填 phone,permissions 必填 …...
处理vxe-table 表尾数据是单独一个接口,表格tableData数据更新后,需要点击两下,表尾才是正确的
修改bug思路: 分别把 tabledata 和 表尾相关数据 console.log() 发现 更新数据先后顺序不对 settimeout延迟查询表格接口 ——测试可行 升级↑:async await 等接口返回后再开始下一个接口查询 ________________________________________________________…...
Linux 中如何提取压缩文件 ?
Linux 是一种流行的开源操作系统,它提供了许多工具来管理、压缩和解压缩文件。压缩文件有助于节省存储空间,使数据传输更快。本指南将向您展示如何在 Linux 中提取不同类型的压缩文件。 1. Unpacking ZIP Files ZIP 文件是非常常见的,要在 …...
scikit-learn机器学习
# 同时添加如下代码, 这样每次环境(kernel)启动的时候只要运行下方代码即可: # Also add the following code, # so that every time the environment (kernel) starts, # just run the following code: import sys sys.path.append(/home/aistudio/external-libraries)机…...
