背景
大数据测试,有时候需要造大量数据(造数可以看我另外一篇文章),而且需要经常使用脚本快速插入数据,插入数据一般常用的两种方式,一种是接口,一种是数据库插入。按照正常的逻辑,使用接口造数是最好的方式,因为不需要去关注数据库字段关联的问题,但接口会有瓶颈,需要依赖接口的稳定型以及性能。如果接口的稳定性,性能不行,那就只能是使用数据库插入的方式进行了。
脚本逻辑
根据业务逻辑,一个流程当作一个事务,一个事务由插入更新等操作。如果事务里某个环节失败,则回滚。使用多进程多协程进行并发造数。下面代码总共插入1万条数据只需要30秒左右。一个进程3000条,3个进程9000条数据。进程的,协程多少对性能会有影响,自己可以试试观察。
代码
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
# @Author : lanny8588
# @File : mysql_insert.py
# @Software : PyCharm
# @Description :
"""
import pymysql
import random
from faker import Faker,Factory
import time
import gevent
from gevent import monkey; monkey.patch_all() #gevent得加上这个才能生效
import multiprocessing
class mysqlmanager(object):
def __init__(self,user,password,database,host,port):
self.user=user
self.password=password
self.database=database
self.host=host
self.port=port
self.conn=None
self.cur=None
def connectDatabase(self):
try:
self.conn = pymysql.connect(self.host, self.user,self.password, self.database ,self.port,charset='utf8')
print ("connectDatabase sucess")
except Exception as err:
print ("connectDatabase failed",err)
return False
self.cur = self.conn.cursor()
return True
def dbclose(self):
self.cur.close()
self.conn.close()
def dbcommit(self):
self.conn.commit()
def execute(self, sql,params=None):
''' 执行sql,每一个表操作都是一个事务,比如insert update 同一个表操作,如果某个环节失败,则回滚,返回fales,正确则返回true。'''
print("----------插入数据----------" )
try:
self.cur.execute(sql,params)
# self.conn.commit()
except Exception as err:
print ('\033[1;31;0m\t4---插入更新失败,msg:\033[0m',err,sql.replace("\n", ""),params)
self.conn.rollback()
return False
return True
def run(min,max):
dbhadle = mysqlmanager(user="xx", password="xxx", database="student", host="xxxxx",port=3306)
dbhadle.connectDatabase()
fake = Faker("zh_CN") #这个申明会有点影响性能。
for i in range (min,max):
class_id=random.randint(1,6)
name=fake.name()
father=fake.name()
phone=fake.phone_number()
sn=fake.ean8()
data1=(name,class_id,sn)
sql1= '''
INSERT into student2 (name,class_id,sn)
VALUES(%s,%s,%s)
'''
data2=(phone,sn)
sql2= '''
UPDATE student2 s2 set s2.phone=%s
where sn=%s
'''
data3=(father,phone)
sql3='''
insert into student_family (father,fa_phone)
VALUES (%s,%s);
'''
# print ("----------插入第%d数据----------"%i)
insertdb=dbhadle.execute(sql1, data1)
update=dbhadle.execute(sql2, data2)
if update : # 只有上一条执行成功了才能做下一步操作
insertdb1=dbhadle.execute(sql3, data3)
else:
pass
if insertdb and update and insertdb1 : #判断所有的数据成功才打印出成功插入数据的信息
print("1---插入数据成功,%s,%s" % (sql1.replace("\n", ""), data1))
print("2---更新数据成功,%s,%s" % (sql2.replace("\n", ""), data2))
print("3---插入数据成功,%s,%s" % (sql3.replace("\n", ""), data3))
dbhadle.dbcommit()
dbhadle.dbclose()
def yibuzhixing():
max_line = 500 # # 定义每次插入多少条。
g_l = [gevent.spawn(run, i, i + max_line) for i in range(1, 30000, max_line)]#这里的数据代表是每个进程插入多少,这里的数据除以max_line ,代表启动多少线程。#这里的数据代表是每个进程插入多少,这里的数据除以max_line ,代表启动多少线程。
gevent.joinall(g_l) # 等待所以操作都执行完毕
if __name__ == '__main__':
start_time = time.time()
p = multiprocessing.Process(target=yibuzhixing)
p2 = multiprocessing.Process(target=yibuzhixing)
p3 = multiprocessing.Process(target=yibuzhixing)
p.start()
p2.start()
p3.start()
p.join()
p2.join()
p3.join()
print("总共耗时%s"%(time.time()-start_time))
模拟
在自己本地数据库里创建一个数据库student库。
创建几个表:
create table `class`( `id` int not null AUTO_INCREMENT,
`name` char(16),
PRIMARY KEY (`id`)
)ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8 #id是主键
CREATE TABLE `student2` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` char(16) NOT NULL,
`class_id` int(11) NOT NULL,
`phone` char(11) DEFAULT NULL,
`sn` char(11) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_phone` (`phone`) USING BTREE,
UNIQUE KEY `IDX_sn` (`sn`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1001 DEFAULT CHARSET=utf8;
CREATE TABLE `student_family` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`father` char(16) NOT NULL,
`fa_phone` char(11) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_faphone` (`fa_phone`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=914 DEFAULT CHARSET=utf8;