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

高并发写利器-组提交,我的Spring组件实战

高并发写优化理论

对于高并发的读QPS优化手段较多,最经济简单的方式是上缓存。但是对于高并发写TPS该如何提升?业界常用的有分库分表、异步写入等技术手段。但是分库分表对于业务的改造十分巨大,涉及迁移数据的麻烦工作,不会作为常用的优化手段。异步写入到时经常在实际工作中使用,但是也不适合所有场景,特别对于带有事务的写入请求,带事务的写入请求通常是需要同步告知用户处理结果,所以不适用异步处理。

我们都知道批处理会比单条处理快很多,只需要发起一次网络请求,在网络层面节省了N次TCP连接获取和发送数据的步骤。实际我测试过,通过shark抓包,发现建立一条TCP连接可能需要耗费10ms~50ms左右。如果是跨洲际的TCP连接更久,可能耗费几百毫秒。单是节省的多次TCP连接就能节省不少时间,其次还有程序代码的循环执行时间。所以将多个写请求聚合成一个合适大小的批量写请求,一次性将数据发送给服务器进行批量写入是最高效的。

MySQL的组提交原理

在MySQL层面,为了保证事务的可靠性和数据同步给备节点、从节点的可靠性。通常会开启双一设置。在双一设置开启后,就会在事务提交前将redo log、binlog落盘,事务才返回成功,这就是WAL机制。

sync_binlog=1

innodb_flush_log_at_trx_commit=1

我们知道由于WAL机制,写入请求在修改了数据页后不会立即刷回磁盘,而是通过记录rodo log和binlog保证事务的持久性和同步给从节点。写rodo log和binlog就是顺序写入的,涉及磁盘的顺序写机制。磁盘顺序写会比随机写快很多。MySQL为了进一步提升多个事务在高并发下写入binlog的性能,采用了“组提交”的概念。顾名思义就是将多个事务在单位时间内聚集起来,一起写入磁盘,就变成了多事务的批量顺序写入,性能高很多。

这里简单介绍组提交。首先MySQL有2个参数控制组提交的等待时间和组大小。

binlog_group_commit_sync_delay=N:在等待N μs后,开始事务刷盘(图中Sync binlog)
binlog_group_commit_sync_no_delay_count=N:如果队列中的事务数达到N个,就忽视binlog_group_commit_sync_delay的设置,直接开始刷盘

解释下这张图。首先在第一步就已经将redo log刷到磁盘了,接下来就是将多个事务聚合在一个组调用write函数写入OS的缓冲。第一个到达的事务就会开启一个新组,等待N个事务到达或者等待N微秒之后主动提交。假设事务T1到达并开启新组1,等待T2来到加入组1,等待时间满后T1主动调用write函数将T1、T2事务都写入OS缓冲。此时T1、T2组成的组1进入第二个阶段,准备调用flush函数将缓冲区的数据刷入磁盘。组1在第二阶段继续等待新事务加入,此时有新组到达就会将组2和组1合并新组,再调用flush函数将组1、组2数据刷入磁盘。整个过程是批量+顺序写入磁盘,是很高效的。

我的组提交Spring组件

 我把这个组提交管理器的组件放到我GitHub上了,大家觉得不错的请Star,或觉得有优化空间的请提出mr,有错误的请斧正。

GitHub-组提交管理器

我们基于以上的理论分析,可以得出如果我们在高并发写入的时候能够模仿MySQL的组提交,实现一个主动等待和被动唤醒提交的组提交机制,将多个写入请求合成一个请求发送给MySQL就能提高写入性能。

总结MySQL的组提交机制原理:

  • 第一个到达的线程开启新组作为本组Leader领导本组的数据提交
  • Leader等待指定X毫秒时间,时间到后主动发起提交
  • 第K个线程到达,若发现本组负载满了唤醒Leader进行本组提交
  • 组与组之间互不阻塞,单位时间内可能有多个组并发提交

基于以上原理,我设计了两个类:GroupManager组管理器、GroupCommit组提交对象。GroupManager负责接收外部线程提交的数据,然后放到当前组里。并且实现整个组提交的流程。GroupCommit是一个组的具象化对象,提供一个组的入队,提交数据,挂起等待,唤醒Leader等基础方法,给GroupManager调用以实现组提交机制。

为了避免高并发时多线程竞相进入组内,导致组错乱,使用了两把锁解决。大部分线程都会被挡在第一关,每次只会放一个线程进到临界区尝试入组。入组之前要先获得当前组的锁,为什么要第二把锁?因为Leader会主动醒来提交本组的数据队列,所以提交时要确保所有资源都是排他的,需要组内锁来保证。入组的线程抢到组内锁之后就代表可以安全入组,此时有三种情况:

  • 如果此时入组前发现组已经满了就开一个新组自己当Leader并唤醒当前组的Leader让它赶快提交
  • 如果入组后发现组满了,唤醒当前组Leader让它赶快提交,自己则挂起等待提交后唤醒
  • 入组后发现还未满,挂起自己等待唤醒

线程在获取到组内锁后都会立即释放GroupManager的锁,目的是让后续线程如果发现当前组满了,就立即开新组提交,提高效率。

系统架构

 因为我们工作中大多数使用的是Tomcat容器,目前Tomcat的IO处理模型是Reactor+线程池的模式。

在整个系统架构层面,组提交影响性能的有两个参数:组大小和等待时间。组大小就是在组内挂起等待的线程数,等待时间是Leader主动等待的毫秒数。组大小直接影响到剩余可工作的线程数,Tomcat线程数量默认200,通常我们根据业务场景和硬件资源调整,线程数量也就几百左右。如果组大小太大同时等待时间太久!!直接把Tomcat所有线程都挂起了这时服务器就假死了,所以对组大小的设置建议通过压测来确定,按照下面的压测经验一般建议设置为Tomcat线程数量的1/4~1/2。这样最大1/2能确保还有一半线程可以服务其它请求。

等待时间,因为这个参数会导致接口RT上升,建议设置在5ms~20ms之间。我们生产MySQL的组提交等待时间设置500微秒,是很短的。我经过反复压测和调参发现,纯MySQL插入操作,等待时间5ms左右就合适了。

总结起来,组大小和等待时间需要根据业务类型和Tomcat线程数量和CPU数量,经过测试来决定一个合适的参数,没有通用的方法论能决定。

在整个系统架构层面,负载均衡器和服务器Pod,Tomcat线程池和多个组提交的关系。

压测报告

环境介绍

  • Mac OS M2 10核16G,SSD
  • MySQL 8.0
  • JDK8u221
  • SpringBoot,Tomcat线程池400
  • Druid数据库连接池 40连接数
  • Jmeter 5.3,700线程并发,循环1000,共70万请求
  • JVM参数设置

-XX:-ClassUnloadingWithConcurrentMark -Xms4g -Xmx4g -Xmn3g -XX:G1HeapRegionSize=4m -XX:InitiatingHeapOccupancyPercent=30 -XX:MaxGCPauseMillis=200 -XX:MaxMetaspaceSize=268435456 -XX:MetaspaceSize=268435456 -XX:ParallelGCThreads=10 -XX:+ParallelRefProcEnabled -XX:-ReduceInitialCardMarks -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseG1GC

MySQL没有经过调优都是默认的参数。MySQL和应用服务还有Jmeter都是在Mac上运行的。对比两种测试用例:1.使用组提交组件 2.单条数据写入。

    @PostMapping("/submit")public Boolean submit() {long tid = Thread.currentThread().getId();log.info("threadId={}", tid);OrderInfo orderInfo = new OrderInfo();orderInfo.setOrderNo(UUID.randomUUID().toString());orderInfo.setAddressId(123321123321123L);orderInfo.setMerchantId(123321123321123L);orderInfo.setUserId(123321123321123L);orderInfo.setOrderAmount(BigDecimal.valueOf(123123L));return groupManager.queueGroup(orderInfo);}@PostMapping("/submit2")public Boolean submit2() {OrderInfo orderInfo = new OrderInfo();orderInfo.setOrderNo(UUID.randomUUID().toString());orderInfo.setAddressId(123321123321123L);orderInfo.setMerchantId(123321123321123L);orderInfo.setUserId(123321123321123L);orderInfo.setOrderAmount(BigDecimal.valueOf(123123L));return orderInfoService.save(orderInfo);}

经过反复实验以及调整组提交的组大小、等待时间参数,得出组大小200,等待时间5ms,得出的TPS是比较好的。TPS达到近8800。接口错误率几乎没有

单提交(每次请求提交一次)所有配置和环境一致的情况下。并发700,循环1000次,70万请求。TPS在5200。错误率0

可以看出组提交比单提交TPS高出68%左右,优化比较明显。如果能针对组大小和等待时间继续调整优化,可能TPS会更高。RT上平均时间比但提交快了1倍,但是P99、P95、P90都比单提交要慢1倍。

 

相关文章:

高并发写利器-组提交,我的Spring组件实战

高并发写优化理论 对于高并发的读QPS优化手段较多,最经济简单的方式是上缓存。但是对于高并发写TPS该如何提升?业界常用的有分库分表、异步写入等技术手段。但是分库分表对于业务的改造十分巨大,涉及迁移数据的麻烦工作,不会作为…...

音视频入门基础:MPEG2-PS专题(4)——FFmpeg源码中,判断某文件是否为PS文件的实现

一、引言 通过FFmpeg命令: ./ffmpeg -i XXX.ps 可以判断出某个文件是否为PS文件: 所以FFmpeg是怎样判断出某个文件是否为PS文件呢?它内部其实是通过mpegps_probe函数来判断的。从《FFmpeg源码:av_probe_input_format3函数和AVI…...

如何使用OpenCV进行抓图-多线程

前言 需求: 1、如何使用OpenCV捕抓Windows电脑上USB摄像头的流、 2、采用多线程 3、获知当前摄像头的帧率。 这个需求,之前就有做了,但是由于出现了一个问题,人家摄像头的帧率目前都可以达到60帧/s 了,而我的程序…...

电子应用设计方案86:智能 AI背景墙系统设计

智能 AI 背景墙系统设计 一、引言 智能 AI 背景墙系统旨在为用户创造一个动态、个性化且具有交互性的空间装饰体验,通过融合先进的技术和创意设计,提升室内环境的美观度和功能性。 二、系统概述 1. 系统目标 - 提供多种主题和风格的背景墙显示效果&…...

【《python爬虫入门教程11--重剑无峰168》】

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 【《python爬虫入门教程11--selenium的安装与使用》】 前言selenium就是一个可以实现python自动化的模块 一、Chrome的版本查找?-- 如果用edge也是类似的1.chrome…...

.net core 线程锁,互斥锁,自旋锁,混合锁

线程锁、互斥锁、自旋锁和混合锁是多线程编程中的重要概念,它们用于控制对共享资源的访问,避免数据竞争和不一致性。每种锁有其特定的适用场景和特点。我们来逐一解释它们,并进行比较。 1. 线程锁(Thread Lock) 线程…...

【DevOps】Jenkins项目发布

Jenkins项目发布 文章目录 Jenkins项目发布前言资源列表基础环境一、Jenkins发布静态网站1.1、项目介绍1.2、部署Web1.3、准备gitlab1.4、配置gitlab1.5、创建项目1.6、推送代码 二、Jenkins中创建gitlab凭据2.1、创建凭据2.2、在Jenkins中添加远程主机2.3、获取gitlab项目的UR…...

C# OpenCV机器视觉:霍夫变换

在一个阳光灿烂得近乎放肆的午后,阿强的实验室就像被施了魔法的科学城堡,到处闪耀着神秘的科技光芒。阿强呢,像个即将踏上惊险征程的探险家,一屁股坐在那堆满奇奇怪怪设备的桌前,眼神中透露出按捺不住的兴奋劲儿&#…...

Kraft模式安装Kafka(含常规、容器两种安装方式)

一、#创作灵感# 公司使用Kafka的软件项目较多,故写技术笔记巩固知识要点 二、软件环境 - Kafka 3.9.0 官方下载地址:Kafka 3.9.0 - Docker Desktop 4.37 容器图形化工具 官方下载地址:Docker Desktop 4.37 特别说明 - Docker Desktop…...

Linux驱动开发(16):输入子系统–电容触摸驱动实验

有关电容触摸的基础知识内容可以参考野火STM32相关教程,这里只介绍电容触摸驱动的相关内容。 本章配套源码、设备树以及更新固件位于“~/embed_linux_driver_tutorial_imx6_code/linux_driver/touch_scream_GTxxx”目录下。 触摸面板通过双面胶粘在显示屏上&#…...

《深入浅出HTTPS​​​​​​​​​​​​​​​​​》读书笔记(24):椭圆曲线密码学

《深入浅出HTTPS​​​​​​​​​​》读书笔记(24):椭圆曲线密码学 为了保证DH的密钥对不被破解,提升安全性的主要手段就是增加密钥对的长度,但是长度越长,性能越低。 为了解决性能问题,需要…...

现代光学基础5

总结自老师的讲义 yt5 开卷考试复习资料:光探测器与光伏技术 目录 光探测器(Photodetector) 工作原理二极管电路连接方式响应度(Responsivity)微弱光检测超导纳米线单光子探测光电二极管噪声 太阳能电池&#xff0…...

力扣hot100——贪心

121. 买卖股票的最佳时机 class Solution { public:int maxProfit(vector<int>& a) {if (a.size() 1) return 0;int ans 0;int mi a[0];for (int i 1; i < a.size(); i) {ans max(ans, a[i] - mi);mi min(mi, a[i]);}return ans;} };55. 跳跃游戏 class S…...

vue3如何实现防抖?

第一 防抖就是我们设置一个调用时间&#xff0c;点击后设置时间开始倒计时&#xff0c;如果再次点击会重新倒计时 npm或yarn安装&#xff1a; npm install lodash <template><div click"debouncedInputHandler"><button>打印</button>…...

西安电子科技大学初/复试笔试、面试、机试成绩占比

西安电子科技大学初/复试笔试、面试、机试成绩占比 01通信工程学院 02电子工程学院 03计算机科学与技术学院 04机电工程学院 06经济与管理学院 07数学与统计学院 08人文学院 09外国语学院 12生命科学与技术学院 13空间科学与技术学院 14先进材料与纳米科技学院 15网络与信息安…...

spring mvc源码学习笔记之六

pom.xml 内容如下 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org/P…...

树莓派4b如何连接ov7670摄像头

在树莓派4B上连接和使用OV7670摄像头是一项具有一定技术挑战的任务。这是因为OV7670摄像头是一个原始的CMOS摄像头模块,它通过并行接口与主机通信,而树莓派的GPIO接口通常用于串行接口(如I2C、SPI、UART)通信,不直接支持并行摄像头接口。因此,需要一些额外的硬件和软件工…...

[微服务]分布式搜索Java客户端

快速入门 使用RestClient客户端进行数据搜索可以分为两步 构建并发起请求 代码解读&#xff1a; 第一步&#xff0c;创建SearchRequest对象&#xff0c;指定索引库名第二步&#xff0c;利用request.source()构建DSL&#xff0c;DSL中可以包含查询、分页、排序、高亮等 query…...

如何使用 `uiautomator2` 控制 Android 设备并模拟应用操作_VIVO手机

在 Android 自动化测试中,uiautomator2 是一个非常强大的工具,能够帮助我们通过 Python 控制 Android 设备执行各种操作。今天,我将通过一个简单的示例,介绍如何使用 uiautomator2 控制 Android 设备,执行特定的应用启动、广告跳过以及其他 UI 操作。此示例的目标是自动化…...

在Ubuntu 18.04.6 LTS安装OpenFace流程

一、修改配置:将gcc8&#xff0c;g8作为默认选项 sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-8 100 sudo update-alternatives --config gcc 选择版本&#xff0c;再查看gcc --version sudo update-alternatives --install /usr/bin/g g /usr/bin/g-…...

变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析

一、变量声明设计&#xff1a;let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性&#xff0c;这种设计体现了语言的核心哲学。以下是深度解析&#xff1a; 1.1 设计理念剖析 安全优先原则&#xff1a;默认不可变强制开发者明确声明意图 let x 5; …...

论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(二)

HoST框架核心实现方法详解 - 论文深度解读(第二部分) 《Learning Humanoid Standing-up Control across Diverse Postures》 系列文章: 论文深度解读 + 算法与代码分析(二) 作者机构: 上海AI Lab, 上海交通大学, 香港大学, 浙江大学, 香港中文大学 论文主题: 人形机器人…...

《通信之道——从微积分到 5G》读书总结

第1章 绪 论 1.1 这是一本什么样的书 通信技术&#xff0c;说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号&#xff08;调制&#xff09; 把信息从信号中抽取出来&am…...

sqlserver 根据指定字符 解析拼接字符串

DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...

Spring Boot面试题精选汇总

&#x1f91f;致敬读者 &#x1f7e9;感谢阅读&#x1f7e6;笑口常开&#x1f7ea;生日快乐⬛早点睡觉 &#x1f4d8;博主相关 &#x1f7e7;博主信息&#x1f7e8;博客首页&#x1f7eb;专栏推荐&#x1f7e5;活动信息 文章目录 Spring Boot面试题精选汇总⚙️ **一、核心概…...

QT: `long long` 类型转换为 `QString` 2025.6.5

在 Qt 中&#xff0c;将 long long 类型转换为 QString 可以通过以下两种常用方法实现&#xff1a; 方法 1&#xff1a;使用 QString::number() 直接调用 QString 的静态方法 number()&#xff0c;将数值转换为字符串&#xff1a; long long value 1234567890123456789LL; …...

根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:

根据万维钢精英日课6的内容&#xff0c;使用AI&#xff08;2025&#xff09;可以参考以下方法&#xff1a; 四个洞见 模型已经比人聪明&#xff1a;以ChatGPT o3为代表的AI非常强大&#xff0c;能运用高级理论解释道理、引用最新学术论文&#xff0c;生成对顶尖科学家都有用的…...

select、poll、epoll 与 Reactor 模式

在高并发网络编程领域&#xff0c;高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表&#xff0c;以及基于它们实现的 Reactor 模式&#xff0c;为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。​ 一、I…...

智能分布式爬虫的数据处理流水线优化:基于深度强化学习的数据质量控制

在数字化浪潮席卷全球的今天&#xff0c;数据已成为企业和研究机构的核心资产。智能分布式爬虫作为高效的数据采集工具&#xff0c;在大规模数据获取中发挥着关键作用。然而&#xff0c;传统的数据处理流水线在面对复杂多变的网络环境和海量异构数据时&#xff0c;常出现数据质…...

Xen Server服务器释放磁盘空间

disk.sh #!/bin/bashcd /run/sr-mount/e54f0646-ae11-0457-b64f-eba4673b824c # 全部虚拟机物理磁盘文件存储 a$(ls -l | awk {print $NF} | cut -d. -f1) # 使用中的虚拟机物理磁盘文件 b$(xe vm-disk-list --multiple | grep uuid | awk {print $NF})printf "%s\n"…...