查看原文
其他

RDS MySQL 自定义数据库如何做压测?

AWS 薛军 AWS 架构师之旅 2019-12-18

 很多用户对标准的 RDS MySQL 性能测试报告并不买账,原因是自家应用的数据库有不一样的 Schema,同时执行的 SQL 读写行为也不一样,那如何安心上线一个新应用呢?



01


定义问题



RDS MySQL 或者 AWS Aurora 有一份官方的标准 Sysbench 的 OLTP 数据集性能测试报告,客户提出的其实是一个定制化数据库测试要求;和标准测试不一样的地方:


  • 应用自己的数据库 vs 标准的 sbtest 模拟数据表

  • 执行自定义的 SQL 语句 vs 标准的针对 sbtest 模拟的 Select/Insert/Delete 等操作


02


实现思路


架构师的脑袋至少有两种以上方案,针对上面的问题,


方案一:创造轮子,优势,程序猿完全掌控整个逻辑


用 Python/Go 或其它脚本语言,写一段可以并发执行的数据库 SQL 执行逻辑(包含应用常见的执行语句)

利用 Linux Shell 脚本,执行一个 while 循环,参数是以上SQL脚本执行次数

从 RDS 控制台观察和分析数据库各项指标


方案二:直接利用 Sysbench 看是否支持自定义测试脚本


Sysbench 在1.0版本之后支持 Custom Benchmarks 通过外挂 lua 脚本,可以利用该特性实现自定义的SQL测试 (看上去是比较完美的方案,聚焦在测试逻辑本身而不是重复发明跑并发测试的已有成熟轮子)


你会选哪一种呢?


03


Sysbench 1.x 验证过程


首先先研究下如下一个标准的 Sysbench 执行命令所引用的 lua 脚本是怎么写的?

sysbench --db-driver=mysql --mysql-host=demo.rds.cn-north-1.amazonaws.com.cn --mysql-port=3306 \/usr/share/sysbench/oltp_read_write.lua \--threads=32 \--time=3600 \--events=999999999 \--table-size=10000000 \--tables=64 \--mysql-user=demouser --mysql-password=demopwd \run


以上命令,引用了 一个 oltp_read_write.lua 测试脚本,那我们看看这个脚本是怎么写的,主要逻辑就两个入口函数,

  • function prepare_statements():执行 prepare 逻辑入口

  • function event():执行 SQL 测试逻辑

#!/usr/bin/env sysbench
require("oltp_common")
function prepare_statements() if not sysbench.opt.skip_trx then prepare_begin() prepare_commit() end
prepare_point_selects()
if sysbench.opt.range_selects then prepare_simple_ranges() prepare_sum_ranges() prepare_order_ranges() prepare_distinct_ranges() end
prepare_index_updates() prepare_non_index_updates() prepare_delete_inserts()end
function event() if not sysbench.opt.skip_trx then begin() end
execute_point_selects()
if sysbench.opt.range_selects then execute_simple_ranges() execute_sum_ranges() execute_order_ranges() execute_distinct_ranges() end
execute_index_updates() execute_non_index_updates() execute_delete_inserts()
if not sysbench.opt.skip_trx then commit() endend


照葫芦画瓢,我们可以coding一段面向项目的业务逻辑,但注意这段逻辑要支持并发执行,因此,SQL 里面的参数构造可以借助于 sysbench 提供的 rand 函数:


#!/usr/bin/env sysbench
require("oltp_common")
function prepare_statements() -- We do not use prepared statements here, but oltp_common.sh expects this -- function to be definedend
function event() -- place your queries for testing here randUnit = sysbench.rand.string("###########-###########-###########")   randNum = sysbench.rand.string("###") con:query(string.format("INSERT INTO `table1` (`PSn`,`PU`,`ITime`,`DA`,`DY`,`TY`,`TR`,`AT`,`TTemp`,`FTemp`,`DCV`,`DCC`,`IPS`,`Uab`,`Ubc`,`Uca`,`Ia`,`Ib`,`Ic`,`PA`,`PB`,`PC`,`PS1`,`QS1`,`PF1`,`F1`,`IE`,`RS`,`Upv2`,`Upv3`,`Upv4`,`Upv5`,`Upv6`,`Ipv2`,`Ipv3`,`Ipv4`,`Ipv5`,`Ipv6`,`devnum`)" .. "VALUES (concat('15256384806','%s'),concat('140105', '%s'),now(),1,3.900,7868.900,'0',19.5,NULL,NULL,315.4,0.1,61.0,230.8,0.0,0.0,1.2,0.0,0.0,67.0,0.0,0.0,67.0,NULL,NULL,50.0,NULL,1,304.2,0.0,0.0,0.0,0.0,0.1,0.0,0.0,0.0,0.0,NULL)", randNum,randUnit))
con:query("SELECT a.PSn, a.ITime, a.dS, c.tS, a.pSSum FROM (SELECT b.PSn, MAX(b.ITime) AS ITime, SUM(b.DY) AS dS, SUM(b.PS1) AS pSSum FROM table1 b WHERE b.ITime >= DATE(NOW()) AND b.ITime <= DATE_ADD(NOW(), INTERVAL 1 DAY) GROUP BY b.PSn) AS a INNER JOIN (SELECT b.PSn, SUM(b.TY) AS tS FROM table1 b GROUP BY b.PSn) AS c ON a.PSn = c.PSn")
end


最后,利用 Shell 跑一段时间并输出结果:


for each in {1..50}; do \sysbench /home/ec2-user/db_custom.lua \--threads=400 \--db-driver=mysql \--mysql-host=demo.rds.cn-northwest-1.amazonaws.com.cn \--mysql-port=3306 \--mysql-db=demodb \--mysql-user=demouser \--mysql-password=demopwd \run >> dbcustomtest.log;done


04


小结


架构师并不总是一下子给出结论,有时候定义清楚问题比结论更重要,同时,在动手之前想想有没有其他更好的解决方案,最后再验证你的想法,影响你的客户!

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存