您当前的位置:首页 > 电脑百科 > 数据库 > MYSQL

如何快速在mysql中生成大量Mock数据做性能测试?

时间:2019-10-15 10:04:25  来源:  作者:

 

最近做一个数据可视化项目时,需要 Mock 大量的数据(千万级)来进行接口开发。本文将记录探索实践的全部过程。

1. 如何快速 Mock 大量数据

1.1 逐条插入数据

因为博主主业是搞前端开发的,对 MySQL 其实不是特别在行了。要 Mock 数据,第一想法当然是写个程序或脚本来自动插入数据了。于是说干就干,很快一个基于 NodeJs 的 demo 就完成了。

建表 sql(为了演示方便,这里仅取4个字段,原测试 demo 有 21 个字段):

use test;
CREATE TABLE test.data (
 `id` INT NOT NULL,
 `nt` VARCHAR(100) NOT NULL,
 `imei` VARCHAR(100) NOT NULL,
 `model` VARCHAR(50) NOT NULL,
 PRIMARY KEY (id)
)

逐条插入数据代码:

let mysql = require('mysql');
const uuidv1 = require('uuid/v1');
let connection = mysql.createConnection({
 host: 'localhost',
 port: '6666',
 user: 'root',
 password: '123456',
 database: 'test'
});
const close = () => connection.end();
function network() {
 return ['"wifi"', '"g4"', '"g3"', '"g2"'][Math.floor(Math.random() * 4)]
}
function phone_model() {
 return ['"NEX"','"x23"','"x21"','"x20"'][Math.floor(Math.random() * 4)]
}
function imei() {
 return `"${uuidv1()}"`;
}
connection.connect();
let s = Date.now();
let i = 0;
function insert() {
 connection.query(`insert into data(id, nt, imei, model) values (${i}, ${network()}, ${imei()}, ${phone_model()})`,
 function (error, results, fields) {
 if (error) throw error;
 if (i + 1 < 100000) {
 i++;
 insert();
 } else {
 console.log('done:' + (Date.now() - s));
 }
 });
}
insert();

用 10 条数据测试了下,perfect!完美工作。清空表,数量加大到1万条数据测试,勉强 perfect ,这次插入1万数据耗时48192ms,也就是48s左右,还能勉强接受。

继续扩量到10万条数据,这下就尴尬的很明显了,生成10万条数据,一共耗时618983ms,618s,也就是10分钟左右。大概心里估计了下,就算以线性递增来算,那么100万条数据大概就是100分钟,1000万数据大概是1000分钟,也就是近17个小时。

很明显,这效率太慢了,必须寻找效率更高的方式。

1.2 使用储存过程批量插入数据

在网上搜索了一会儿,果然找到了一种新方式:使用 mysql 储存过程来批量插入数据。所谓“储存过程”,个人认为就是批处理。

建表 sql,因为数据量大,这里加上了分区:

use test;
CREATE TABLE test.data (
 `id` INT NOT NULL,
 `nt` VARCHAR(100) NOT NULL,
 `imei` VARCHAR(100) NOT NULL,
 `model` VARCHAR(50) NOT NULL,
 PRIMARY KEY (id)
) ENGINE = MyISAM ROW_FORMAT = DEFAULT
 partition BY RANGE (id) (
 partition p0 VALUES LESS THAN (10000000),
 partition p1 VALUES LESS THAN (20000000),
 partition p2 VALUES LESS THAN (30000000),
 partition p3 VALUES LESS THAN (40000000),
 partition p4 VALUES LESS THAN (50000000),
 partition p5 VALUES LESS THAN (60000000),
 partition p6 VALUES LESS THAN (70000000),
 partition p7 VALUES LESS THAN (80000000),
 partition p8 VALUES LESS THAN (90000000),
 Partition p9 VALUES LESS THAN MAXVALUE
 );

接着是创建 mysql 储存过程,不过在编写储存过程代码时,遇到了一个问题,如何实现下面函数的功能,即随机从 wigi, 4g, 3g, 2g 中返回一个网络类型。

function network() {
 return ['"wifi"', '"g4"', '"g3"', '"g2"'][Math.floor(Math.random() * 4)]
}

2. 如何在储存过程中基于数组来生成随机值

查了大量资料,发现 mysql 储存过程不支持数组操作。一时间,似乎走到了死胡同。最后发现了这篇文章:MySQL函数和存储过程生成电话号码。作者生成电话号码的思路给了我启发,于是我参照他的思路,实现了随机生成网络类型的功能。

----------------------------
-- 生成网络类型的函数
----------------------------
DELIMITER $$
create function network() returns char(4)
begin 
 declare networks varchar(100) default "wifi4g 3g 2g "; -- 1,5,9,13
 declare idx int;
 declare ret char(4);
 set idx = 1+floor(rand()*4)*4;
 set ret = trim(substring(networks,idx,4));
 return ret;
end $$
DELIMITER ;

大概思路就是:

  • 找出数组中最长的字符串项,比如 wifi,4g,3g,2g 中最长的项是 'wifi',长度为4
  • 将数组所有项用空格填充,让其与最长项长度一样,即wifi4g 3g 2g
  • 随机生成固定的字符截取起始点。这里的随机,固定可能会不太好理解。看这个表达式1+floor(rand()*4)*4就清楚了,此表达式总是返回 1,5,9,13中某个值
  • 截取字符串,同时去掉填充的空格,trim(substring(networks,idx,4)),就得到随机值了

解决随机生成值的问题后,储存过程的代码也就出来了:

----------------------------
-- 生成网络类型的函数
----------------------------
DELIMITER $$
create function network() returns char(4)
begin 
 declare networks varchar(100) default "wifi4g 3g 2g "; -- 1,5,9,13
 declare idx int;
 declare ret char(4);
 set idx = 1+floor(rand()*4)*4;
 set ret = trim(substring(networks,idx,4));
 return ret;
end $$
DELIMITER ;
----------------------------
-- 生成机型的函数
----------------------------
DELIMITER $$
create function phone_model() returns char(10)
begin 
 declare phone_types varchar(100) default "NEX x23 x21 x20 x9 x7 x6 x5 Z1 Z2 Z3 Y97 Y91 Y85 Y83 Y81 Y79 ";
 declare idx int;
 declare ret char(10);
 set idx = 1+floor(rand()*17)*4;
 set ret = trim(substring(phone_types,idx,4));
 return ret;
end $$
DELIMITER ;
----------------------------
-- 生成IMEI的函数
----------------------------
DELIMITER $$
create function randchar() returns char(5)
begin
 declare ret char(5); 
 set ret = substring("ABCDEFGHIJKLMNOPQRSTUVWXYZ",floor(1+26*rand()),1);
 return ret;
end $$
DELIMITER ;
DELIMITER $$
create function imei() returns char(50)
begin
 declare ret char(50) default ""; 
 declare imeiLen int default 11;
 DECLARE idx INT default 0;
 WHILE idx < imeiLen DO
 SET idx = idx + 1;
 SET ret = CONCAT(ret, randchar());
 END WHILE;
 return ret;
end $$
DELIMITER ;
----------------------------
-- 创建储存过程
----------------------------
use test;
DROP PROCEDURE IF EXISTS test.BatchInsertCustomer;
delimiter //
CREATE PROCEDURE BatchInsertCustomer(IN start INT,IN loop_time INT)
 BEGIN
 DECLARE Var INT;
 DECLARE ID INT;
 SET Var = 0;
 SET ID= start;
 WHILE Var < loop_time DO
 insert into data(`id`, `nt`, `imei`, `model`) 
 values (ID, network(), imei(), phone_model());
 SET Var = Var + 1;
 SET ID = ID + 1;
 END WHILE;
 END;
 //
delimiter ;

调用储存过程:

-- 调用
ALTER TABLE test DISABLE KEYS;
CALL BatchInsertCustomer(1, 10);
ALTER TABLE test ENABLE KEYS;

在测试时,使用储存过程生成1000万数据大概是140分钟,不到2个半小时。相比逐条插入的17个小时,快了8,9倍,效率提升不少。

3. 小结

使用 mysql 储存过程可以快速地生成 Mock 数据。同时本文还提供了一种“如何在储存过程中基于数组来生成随机值“的思路,希望对大家有些帮助。

3.1 参考

  • MySQL批量插入大量数据方法
  • MySQL函数和存储过程生成电话号码


Tags:mysql 性能测试   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
最近做一个数据可视化项目时,需要 Mock 大量的数据(千万级)来进行接口开发。本文将记录探索实践的全部过程。1. 如何快速 Mock 大量数据1.1 逐条插入数据因为博主主业是搞前端...【详细内容】
2019-10-15  Tags: mysql 性能测试  点击:(412)  评论:(0)  加入收藏
▌简易百科推荐
作者:雷文霆 爱可生华东交付服务部 DBA 成员,主要负责Mysql故障处理及相关技术支持。爱好看书,电影。座右铭,每一个不曾起舞的日子,都是对生命的辜负。 本文来源:原创投稿 *爱可生...【详细内容】
2021-12-24  爱可生    Tags:MySQL   点击:(7)  评论:(0)  加入收藏
生成间隙(gap)锁、临键(next-key)锁的前提条件 是在 RR 隔离级别下。有关Mysql记录锁、间隙(gap)锁、临键锁(next-key)锁的一些理论知识之前有写过,详细内容可以看这篇文章...【详细内容】
2021-12-14  python数据分析    Tags:MySQL记录锁   点击:(18)  评论:(0)  加入收藏
binlog 基本认识 MySQL的二进制日志可以说是MySQL最重要的日志了,它记录了所有的DDL和DML(除了数据查询语句)语句,以事件形式记录,还包含语句所执行的消耗的时间,MySQL的二...【详细内容】
2021-12-14  linux上的码农    Tags:mysql   点击:(13)  评论:(0)  加入收藏
为查询优化你的查询 大多数的MySQL服务器都开启了查询缓存。这是提高性最有效的方法之一,而且这是被MySQL的数据库引擎处理的。当有很多相同的查询被执行了多次的时候,这些查...【详细内容】
2021-12-09  元宇宙iwemeta    Tags:mysql   点击:(15)  评论:(0)  加入收藏
测试的目的和原因,公司有很多程序员,每个程序员对数据库和表结构都有自己的理解。而且每个程序员的理解往往是以效率考虑。既然都是为了效率考虑,那么我就来测试一下究竟哪种使...【详细内容】
2021-12-08  吴彬的分享    Tags:Mysql数据库   点击:(14)  评论:(0)  加入收藏
当你们考虑项目并发的时候,我在部署环境,当你们在纠结使用ArrayList还是LinkedArrayList的时候,我还是在部署环境。所以啊,技术不止境,我在部环境。今天这篇文章缕一下在同一台服...【详细内容】
2021-12-08  秃头码哥    Tags:MySQL数据库   点击:(17)  评论:(0)  加入收藏
对于数据分析来说,MySQL使用最多的是查询,比如对数据进行排序、分组、去重、汇总及字符串匹配等,如果查询的数据涉及多个表,还需要要对表进行连接,本文就来说说MySQL中常用的查询...【详细内容】
2021-12-06  笨鸟学数据分析    Tags:MySQL   点击:(21)  评论:(0)  加入收藏
在学习SQL语句之前,首先需要区分几个概念,我们常说的数据库是指数据库软件,例如MySQL、Oracle、SQL Server等,而本文提到的数据库是指数据库软件中的一个个用于存储数据的容器。...【详细内容】
2021-11-24  笨鸟学数据分析    Tags:SQL语句   点击:(23)  评论:(0)  加入收藏
概述以前参加过一个库存系统,由于其业务复杂性,搞了很多个应用来支撑。这样的话一份库存数据就有可能同时有多个应用来修改库存数据。比如说,有定时任务域xx.cron,和SystemA域...【详细内容】
2021-11-05  Java云海    Tags:分布式锁   点击:(32)  评论:(0)  加入收藏
MySQL的进阶查询 一、 按关键字排序 使用ORDERBY语句来实现排序排序可针对一个或多个字段ASC:升序,默认排序方式 【升序是从小到大】DESC:降序 【降序是从大到小】ORDER BY的...【详细内容】
2021-11-05  Java热点    Tags:SQL语句   点击:(28)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条