使用GO对PostgreSQL进行有意思的多线程压测

图片

前言

针对PostgreSQL进行压缩,有很多相关的工具。有同学又要问了,为何还要再搞一个?比如,pgbench, sysbench之类的,已经很强大了。是的,它们都很强大。但有时候,在一些特殊的场景,可能自己构造一个更能接近真实的生产环境。

这里,我半写,半借助于ChatGPT,搞出一个代码片段来模拟启动一段多线程并发SQL请求,作用于PostgreSQL数据库。然后,你可以对请求执行完以后的结果进行观测,尤其是表膨胀,受影响记录条数之类的。

基于此,我们还可以进行持续改造,快速用于工作之中。

实作

需求:

实现一段代码,读取一个sql文件,然后分段分批执行,并且是以多线程(比如10个线程,go里边可能就是协程,非常高效)去执行这个SQL中的所有SQL语句。再加一个时间限制,比如持续执行120秒。

实现:

package mainimport ("bufio""context""database/sql""fmt""io""os""strings""sync""time"_ "github.com/lib/pq"
)const (host     = "localhost"port     =  5555user     = "postgres"password = "password"dbname   = "mydb"
)func execute_sqls(ctx context.Context, sqls []string, wg *sync.WaitGroup, thread int) {defer wg.Done()psqlInfo := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable", host, port, user, password, dbname)db, err := sql.Open("postgres", psqlInfo)if err != nil {panic(err)}defer db.Close()start := time.Now()for {for _, sql := range sqls {select {case <-ctx.Done():elapsed := time.Since(start)fmt.Printf("Thread %d stopped. It executed SQLs for %s \n", thread, elapsed)returndefault:_, err := db.Exec(sql)if err != nil {fmt.Println(err)}}}}
}func read_sqls(file string) []string {f, err := os.Open(file)if err != nil {panic(err)}defer f.Close()sqls := make([]string, 0)r := bufio.NewReader(f)for {line, err := r.ReadString(';')if err == io.EOF {break} else if err != nil {panic(err)}sql := strings.TrimSpace(line)if sql != "" {sqls = append(sqls, sql)}}return sqls
}func main() {filepath := "file.sql" // Replace with your file pathnumThreads := 10  // Number of threadssqls := read_sqls(filepath)var wg sync.WaitGroupctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) // 60 secondsfor i := 0; i < numThreads; i++ {wg.Add(1)go execute_sqls(ctx, sqls, &wg, i)}wg.Wait()cancel()fmt.Println("All goroutines stopped")
}

上边的代码,关于输入文件:file.sql,  线程数:10, 运行时间:60秒,都是硬编码进去的。你可以根据实际情况,进行参数化。

体验:

在你的go环境已经安装了"github.com/lib/pq"等必备包之后(go get github.com/lib/pq),就可以直接执行了。我们准备一个pg的基本环境。database: mydb,  端口:5555, 就用postgres用户及相应密码(仅用于测试目的),不缀述。

目标表的准备:

\c mydb
create table t(id int, col2 varchar(32));

file.sql文件内容如下:

insert into t values ((10000*random())::int, md5(random()::varchar));
with updates as (select (10000*random())::int as id) update t set col2 = 'update' || updates.id from updates where t.id=updates.id returning updates.id;

这个测试的代码片段,就是插入一条随机记录,并且再随机更新一条记录,使用CTE语法,把对应的id值返回来,有可能找不到对应的记录,就返回的是空值。在并发大的情况下,update语句慢慢就起作用了。这样就可以反复执行。

来看看效果:

go run ./stress.gohread 7 stopped. It executed SQLs for 59.999324s 
Thread 1 stopped. It executed SQLs for 1m0.000756208s 
Thread 2 stopped. It executed SQLs for 1m0.000604792s 
Thread 4 stopped. It executed SQLs for 1m0.001703583s 
Thread 0 stopped. It executed SQLs for 1m0.008518875s 
Thread 9 stopped. It executed SQLs for 1m0.008456083s 
Thread 5 stopped. It executed SQLs for 1m0.007964375s 
Thread 6 stopped. It executed SQLs for 1m0.007968292s 
Thread 3 stopped. It executed SQLs for 1m0.008145042s 
Thread 8 stopped. It executed SQLs for 1m0.008202209s 
All goroutines stopped

1分钟跑完之后,我们看到这样的部分记录结果:

mydb=# select * from t limit 10;id  |    col2
------+------------4792 | update47923416 | update34169290 | update9290887 | update8878778 | update87787472 | update74724602 | update46023454 | update34542604 | update26041990 | update1990
(10 rows)

总记录条数:

mydb=# select count(*) from t;count
--------126056
(1 row)

引申:可以认为单个C+U操作,10个线程并发,1分钟入库12.6万。

表大小:

mydb=# select pg_total_relation_size('t');pg_total_relation_size
------------------------8372224
(1 row)

使用下边的SQL看看相关指标:

WITH cteTableInfo AS 
(SELECT COUNT(1) AS ct,SUM(length(t::text)) AS TextLength  ,'public.t'::regclass AS TableName  FROM public.t AS t  
)
,cteRowSize AS 
(SELECT ARRAY [pg_relation_size(TableName), pg_relation_size(TableName, 'vm'), pg_relation_size(TableName, 'fsm'), pg_table_size(TableName), pg_indexes_size(TableName), pg_total_relation_size(TableName), TextLength] AS val, ARRAY ['Relation Size', 'Visibility Map', 'Free Space Map', 'Table Included Toast Size', 'Indexes Size', 'Total Relation Size', 'Live Row Byte Size'] AS NameFROM cteTableInfo
)
SELECT unnest(name) AS Description,unnest(val) AS Bytes,pg_size_pretty(unnest(val)) AS BytesPretty,unnest(val) / ct AS bytes_per_row
FROM cteTableInfo, cteRowSizeUNION ALL SELECT '------------------------------', NULL, NULL, NULL
UNION ALL SELECT 'TotalRows', ct, NULL, NULL FROM cteTableInfo
UNION ALL SELECT 'LiveTuples', pg_stat_get_live_tuples(TableName), NULL, NULL FROM cteTableInfo
UNION ALL SELECT 'DeadTuples', pg_stat_get_dead_tuples(TableName), NULL, NULL FROM cteTableInfo;

结果:

          description           |  bytes  | bytespretty | bytes_per_row
--------------------------------+---------+-------------+---------------Relation Size                  | 8339456 | 8144 kB     |            66Visibility Map                 |    8192 | 8192 bytes  |             0Free Space Map                 |   24576 | 24 kB       |             0Table Included Toast Size      | 8372224 | 8176 kB     |            66Indexes Size                   |       0 | 0 bytes     |             0Total Relation Size            | 8372224 | 8176 kB     |            66Live Row Byte Size             | 2338451 | 2284 kB     |            18------------------------------ |         |             |TotalRows                      |  126056 |             |LiveTuples                     |  126056 |             |DeadTuples                     |   12274 |             |
(11 rows)

里边有涉及到的死元组为12274行。

mydb=# create extension pgstattuple;
CREATE EXTENSIONmydb=# select * from pgstattuple('public.t') \gx
-[ RECORD 1 ]------+--------
table_len          | 8339456
tuple_count        | 126056
tuple_len          | 5125646
tuple_percent      | 61.46
dead_tuple_count   | 12110
dead_tuple_len     | 483172
dead_tuple_percent | 5.79
free_space         | 1451672
free_percent       | 17.41

这两种统计结果也都比较接近。

当你针对相同的表,进行随机多次测试,发现上边的值也会不断变化(update的命中率会越来越高)。

总结:

本文的目的,只是作一个抛砖引玉,可以随时使用go, python甚至rust去构建一个小的压缩环境,对各种复杂的压力环境进行模拟,并得出相关结论。当然,作为一个团队,可以开发出使用Java之类的接近业务逻辑的工具也是可以的。也有的测试团队,原意使用JMeter + jdbc来构建测试套集,都不失为一种方式。这类工具,是介于pgbench 和 真实业务场景压测之间的一种使用方式。哪个更方便,就可以用哪个。

上边的代码片段,稍加改造,就可以用到实际的实验当中。

关于表膨胀,可以看看我前边的文章:

也聊聊PostgreSQL中的空间膨胀与AutoVacuum

PG中的一例简单的update看表膨胀

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

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

相关文章

Django开发复盘

一、URL 对于一个不会写正则表达式的蒟蒻来说&#xff0c;在urls.py中就只能傻傻的写死名字&#xff0c;但是即便这样&#xff0c;还会有很多相对路径和绝对路径的问题&#xff08;相对ip端口的路径&#xff09;&#xff0c;因为我们网页中涉及到页面跳转&#xff0c;涉及到发送…

Tuxera for Mac2024免费读写硬盘U盘工具

作为软件产品专家&#xff0c;我对各类软件都有较为深入的了解&#xff0c;下面介绍Tuxera for Mac这款读写硬盘/U盘工具的相关信息&#xff1a; Tuxera for Mac是一款高效稳定的NTFS读写工具&#xff0c;专为解决Mac系统无法直接读写NTFS格式驱动器的问题而设计。它提供了完整…

Android 自定义EditText

文章目录 Android 自定义EditText概述源码可清空内容的EditText可显示密码的EditText 使用源码下载 Android 自定义EditText 概述 定义一款可清空内容的 ClearEditText 和可显示密码的 PasswordEditText&#xff0c;支持修改提示图标和大小、背景图片等。 源码 基类&#xf…

实现商铺和缓存与数据库双写一致

2.4 实现商铺和缓存与数据库双写一致 核心思路如下&#xff1a; 修改ShopController中的业务逻辑&#xff0c;满足下面的需求&#xff1a; 根据id查询店铺时&#xff0c;如果缓存未命中&#xff0c;则查询数据库&#xff0c;将数据库结果写入缓存&#xff0c;并设置超时时间…

ssm小区车库停车系统开发mysql数据库web结构java编程计算机网页源码eclipse项目

一、源码特点 ssm小区车库停车系统是一套完善的信息系统&#xff0c;结合springMVC框架完成本系统&#xff0c;对理解JSP java编程开发语言有帮助系统采用SSM框架&#xff08;MVC模式开发&#xff09;&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模…

如何在家中使用手机平板电脑 公司iStoreOS软路由实现远程桌面

文章目录 简介一、配置远程桌面公网地址二、家中使用永久固定地址 访问公司电脑**具体操作方法是&#xff1a;** 简介 软路由是PC的硬件加上路由系统来实现路由器的功能&#xff0c;也可以说是使用软件达成路由功能的路由器。 使用软路由控制局域网内计算机的好处&#xff1a…

HCIA-Datacom实验_03_实验一:华为VRP系统基本操作

1.运行eNSP&#xff0c;设置-界面设置-自定义界面-设备标签&#xff0c;“总显示接口标签” 打钩。 2.按照实验拓扑添加设备 注&#xff1a;如果是真实环境&#xff0c;需要接两条线&#xff1a; &#xff08;1&#xff09;串口线&#xff1a;电脑USB口到网络设备Console口&am…

Windows下载使用nc(netcat)命令

‘nc’ 不是内部或外部命令&#xff0c;也不是可运行的程序&#xff1f; 点击链接地址&#xff0c;下载压缩包。 完成后解压 使用方式&#xff08;三种&#xff09;&#xff1a; 1、直接双击exe使用 2、把这个exe放到cmd启动的默认路径下 放到默认路径下&#xff0c;使用nc&a…

MySQL高级语句(二)

一、前期准备工作 1.1 登陆mysql&#xff0c;查看数据表 1.2 显示所有表 1.3 创建guigui表并插入数据 1.4 创建ky35表格并插入数据 二、子连接 子查询也被称作内查询或者嵌套查询&#xff0c;是指在一个查询语句里面还嵌套着另一个查询语句。子查询语句是先于主查询语句被执行的…

自动生成测试位置吸附脚本设计思路

前言 计算一个异质结需要测试对比不同吸附位置之间的能量差异&#xff0c;可以直接手动建模&#xff0c;但是人太懒了&#xff0c;能交给机器的自己就别动手 问题描述 如图上所示是我计算吸附用的衬底&#xff0c;当原子在上面吸附时我考虑了25个&#xff08;可以随便取&…

Tensorflow2.0笔记 - 使用compile,fit,evaluate,predict简化流程

本笔记主要用compile, fit, evalutate和predict来简化整体代码&#xff0c;使用这些高层API可以减少很多重复代码。具体内容请自行百度&#xff0c;本笔记基于FashionMnist的训练笔记&#xff0c;原始笔记如下&#xff1a; Tensorflow2.0笔记 - FashionMnist数据集训练-CSDN博…

OSPF-区域间路由计算

一、概述 前面学习了我们学习了Router-LSA和Network-LSA&#xff0c;它们都只能在区域内进行泛洪&#xff0c;而且我们之前一直主要是单区域学习。OSPF的核心是骨干区域Area 0&#xff0c;其它都为非骨干区域。但是在大型网络中&#xff0c;单区域OSPF会存在一定的问题&#xf…

[BT]BUUCTF刷题第9天(3.27)

第9天&#xff08;共2题&#xff09; [护网杯 2018]easy_tornado 打开网站就是三个txt文件 /flag.txt flag in /fllllllllllllag/welcome.txt render/hints.txt md5(cookie_secretmd5(filename))当点进flag.txt时&#xff0c;url变为 http://b9e52e06-e591-46ad-953e-7e8c5f…

gopher伪协议

基础知识 基本格式 基本格式&#xff1a;URL:gopher://<host>:<port>/<gopher-path>web也需要加端口号80gophert协议默认端口为70gopheri请求不转发第一个字符 get请求 问号&#xff08;&#xff1f;)需要转码为URL编码&#xff0c;也就是%3f回车换行要变…

【Linux】信号的处理{信号处理的时机/了解寄存器/内核态与用户态/信号操作函数}

文章目录 0.对于信号捕捉的理解1.信号处理的时机1.1 何时处理信号&#xff1f;1.2 内核态和用户态1.3 内核态和用户态的切换 2.了解寄存器3.信号捕捉的原理4.信号操作函数4.1sighandler_t signal(int signum, sighandler_t handler);4.2int sigaction(int signum, const struct…

IP如何异地共享文件?

【天联】 组网由于操作简单、跨平台应用、无网络要求、独创的安全加速方案等原因&#xff0c;被几十万用户广泛应用&#xff0c;解决了各行业客户的远程连接需求。采用穿透技术&#xff0c;简单易用&#xff0c;不需要在硬件设备中端口映射即可实现远程访问。 异地共享文件 在…

基于SpringBoot和Vue的校园管理系统的设计与实现

今天要和大家聊的是一款基于SpringBoot和Vue的校园管理系统的设计与实现 &#xff01;&#xff01;&#xff01; 有需要的小伙伴可以通过文章末尾名片咨询我哦&#xff01;&#xff01;&#xff01; &#x1f495;&#x1f495;作者&#xff1a;李同学 &#x1f495;&#x1f…

vscode上编辑vba

安装xvba插件更换vscode的工作目录启动扩展服务器在config.json中添加目标工作簿的名称加载excel文件&#xff08;必须带宏的xlsm&#xff09;这个扩展就会自动提取出Excel文件中的代码Export VBA&#xff08;编辑完成的VBA代码保存到 Excel文件 &#xff09;再打开excel文件可…

【LVGL-键盘部件,实体按键控制】

LVGL-二维码库 ■ LVGL-键盘部件■ 示例一&#xff1a;键盘弹窗提示■ 示例二&#xff1a;设置键盘模式■ 综合示例&#xff1a; ■ LVGL-实体按键控制■ 简介 ■ LVGL-键盘部件 ■ 示例一&#xff1a;键盘弹窗提示 lv_keyboard_set_popovers(kb,true);■ 示例二&#xff1a;设…

基于Givens旋转完成QR分解进而求解实矩阵的逆矩阵

基于Givens旋转完成QR分解进而求解实矩阵的逆矩阵 目录 前言 一、Givens旋转简介 二、Givens旋转解释 三、Givens旋转进行QR分解 四、Givens旋转进行QR分解数值计算例子 五、求逆矩阵 六、MATLAB仿真 七、参考资料 总结 前言 在进行QR分解时&#xff0c;HouseHolder变换…