RDS MySQL 自定义数据库如何做压测?
“ 很多用户对标准的 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()
end
end
照葫芦画瓢,我们可以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 defined
end
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
—
小结
架构师并不总是一下子给出结论,有时候定义清楚问题比结论更重要,同时,在动手之前想想有没有其他更好的解决方案,最后再验证你的想法,影响你的客户!