Python3 多线程(连接池)操作MySQL插入数据


Posted in Python onJune 09, 2021

多线程(连接池)操作MySQL插入数据

针对于此篇博客的收获心得:

  • 首先是可以构建连接数据库的连接池,这样可以多开启连接,同一时间连接不同的数据表进行查询,插入,为多线程进行操作数据库打基础
  • 多线程根据多连接的方式,需求中要完成多语言的入库操作,我们可以启用多线程对不同语言数据进行并行操作
  • 在插入过程中,一条一插入,比较浪费时间,我们可以把数据进行积累,积累到一定的条数的时候,执行一条sql命令,一次性将多条数据插入到数据库中,节省时间cur.executemany

1.主要模块

DBUtils : 允许在多线程应用和数据库之间连接的模块套件
Threading : 提供多线程功能

2.创建连接池

PooledDB 基本参数:

  • mincached : 最少的空闲连接数,如果空闲连接数小于这个数,Pool自动创建新连接;
  • maxcached : 最大的空闲连接数,如果空闲连接数大于这个数,Pool则关闭空闲连接;
  • maxconnections : 最大的连接数;
  • blocking : 当连接数达到最大的连接数时,在请求连接的时候,如果这个值是True,请求连接的程序会一直等待,直到当前连接数小于最大连接数,如果这个值是False,会报错;
def mysql_connection():
    maxconnections = 15  # 最大连接数
    pool = PooledDB(
        pymysql,
        maxconnections,
        host='localhost',
        user='root',
        port=3306,
        passwd='123456',
        db='test_DB',
        use_unicode=True)
    return pool

# 使用方式
pool = mysql_connection()
con = pool.connection()

3.数据预处理

文件格式:txt

共准备了四份虚拟数据以便测试,分别有10万, 50万, 100万, 500万行数据

MySQL表结构如下图:

Python3 多线程(连接池)操作MySQL插入数据

数据处理思路 :

  • 每一行一条记录,每个字段间用制表符 “\t” 间隔开,字段带有双引号;
  • 读取出来的数据类型是 Bytes ;
  • 最终得到嵌套列表的格式,用于多线程循环每个任务每次处理10万行数据;

格式 : [ [(A,B,C,D), (A,B,C,D),(A,B,C,D),…], [(A,B,C,D), (A,B,C,D),(A,B,C,D),…], [], … ]

import re
import time

st = time.time()
with open("10w.txt", "rb") as f:
    data = []
    for line in f:
        line = re.sub("\s", "", str(line, encoding="utf-8"))
        line = tuple(line[1:-1].split("\"\""))
        data.append(line)
    n = 100000  # 按每10万行数据为最小单位拆分成嵌套列表
    result = [data[i:i + n] for i in range(0, len(data), n)]
print("10万行数据,耗时:{}".format(round(time.time() - st, 3)))

# 10万行数据,耗时:0.374
# 50万行数据,耗时:1.848
# 100万行数据,耗时:3.725
# 500万行数据,耗时:18.493

4.线程任务

每调用一次插入函数就从连接池中取出一个链接操作,完成后关闭链接;
executemany 批量操作,减少 commit 次数,提升效率;

def mysql_insert(*args):
    con = pool.connection()
    cur = con.cursor()
    sql = "INSERT INTO test(sku,fnsku,asin,shopid) VALUES(%s, %s, %s, %s)"
    try:
        cur.executemany(sql, *args)
        con.commit()
    except Exception as e:
        con.rollback()  # 事务回滚
        print('SQL执行有误,原因:', e)
    finally:
        cur.close()
        con.close()

5.启动多线程

代码思路 :

设定最大队列数,该值必须要小于连接池的最大连接数,否则创建线程任务所需要的连接无法满足,会报错 : pymysql.err.OperationalError: (1040, ‘Too many connections')循环预处理好的列表数据,添加队列任务如果达到队列最大值 或者 当前任务是最后一个,就开始多线程队执行队列里的任务,直到队列为空;

def task():
    q = Queue(maxsize=10)  # 设定最大队列数和线程数
    # data : 预处理好的数据(嵌套列表)
    while data:
        content = data.pop()
        t = threading.Thread(target=mysql_insert, args=(content,))
        q.put(t)
        if (q.full() == True) or (len(data)) == 0:
            thread_list = []
            while q.empty() == False:
                t = q.get()
                thread_list.append(t)
                t.start()
            for t in thread_list:
                t.join()

6.完整示例

import pymysql
import threading
import re
import time
from queue import Queue
from DBUtils.PooledDB import PooledDB

class ThreadInsert(object):
    "多线程并发MySQL插入数据"
    def __init__(self):
        start_time = time.time()
        self.pool = self.mysql_connection()
        self.data = self.getData()
        self.mysql_delete()
        self.task()
        print("========= 数据插入,共耗时:{}'s =========".format(round(time.time() - start_time, 3)))
        
    def mysql_connection(self):
        maxconnections = 15  # 最大连接数
        pool = PooledDB(
            pymysql,
            maxconnections,
            host='localhost',
            user='root',
            port=3306,
            passwd='123456',
            db='test_DB',
            use_unicode=True)
        return pool

    def getData(self):
        st = time.time()
        with open("10w.txt", "rb") as f:
            data = []
            for line in f:
                line = re.sub("\s", "", str(line, encoding="utf-8"))
                line = tuple(line[1:-1].split("\"\""))
                data.append(line)
        n = 100000    # 按每10万行数据为最小单位拆分成嵌套列表
        result = [data[i:i + n] for i in range(0, len(data), n)]
        print("共获取{}组数据,每组{}个元素.==>> 耗时:{}'s".format(len(result), n, round(time.time() - st, 3)))
        return result

    def mysql_delete(self):
        st = time.time()
        con = self.pool.connection()
        cur = con.cursor()
        sql = "TRUNCATE TABLE test"
        cur.execute(sql)
        con.commit()
        cur.close()
        con.close()
        print("清空原数据.==>> 耗时:{}'s".format(round(time.time() - st, 3)))

    def mysql_insert(self, *args):
        con = self.pool.connection()
        cur = con.cursor()
        sql = "INSERT INTO test(sku, fnsku, asin, shopid) VALUES(%s, %s, %s, %s)"
        try:
            cur.executemany(sql, *args)
            con.commit()
        except Exception as e:
            con.rollback()  # 事务回滚
            print('SQL执行有误,原因:', e)
        finally:
            cur.close()
            con.close()

    def task(self):
        q = Queue(maxsize=10)  # 设定最大队列数和线程数
        st = time.time()
        while self.data:
            content = self.data.pop()
            t = threading.Thread(target=self.mysql_insert, args=(content,))
            q.put(t)
            if (q.full() == True) or (len(self.data)) == 0:
                thread_list = []
                while q.empty() == False:
                    t = q.get()
                    thread_list.append(t)
                    t.start()
                for t in thread_list:
                    t.join()
        print("数据插入完成.==>> 耗时:{}'s".format(round(time.time() - st, 3)))

if __name__ == '__main__':
    ThreadInsert()

插入数据对比

共获取1组数据,每组100000个元素.== >> 耗时:0.374's
清空原数据.== >> 耗时:0.031's
数据插入完成.== >> 耗时:2.499's
=============== 10w数据插入,共耗时:3.092's ===============
共获取5组数据,每组100000个元素.== >> 耗时:1.745's
清空原数据.== >> 耗时:0.0's
数据插入完成.== >> 耗时:16.129's
=============== 50w数据插入,共耗时:17.969's ===============
共获取10组数据,每组100000个元素.== >> 耗时:3.858's
清空原数据.== >> 耗时:0.028's
数据插入完成.== >> 耗时:41.269's
=============== 100w数据插入,共耗时:45.257's ===============
共获取50组数据,每组100000个元素.== >> 耗时:19.478's
清空原数据.== >> 耗时:0.016's
数据插入完成.== >> 耗时:317.346's
=============== 500w数据插入,共耗时:337.053's ===============

7.思考/总结

思考 :多线程+队列的方式基本能满足日常的工作需要,但是细想还是有不足;
例子中每次执行10个线程任务,在这10个任务执行完后才能重新添加队列任务,这样会造成队列空闲.如剩余1个任务未完成,当中空闲数 9,当中的资源时间都浪费了;
是否能一直保持队列饱满的状态,每完成一个任务就重新填充一个.

 到此这篇关于Python3 多线程(连接池)操作MySQL插入数据的文章就介绍到这了,更多相关Python3 多线程插入MySQL数据内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Python 相关文章推荐
下载给定网页上图片的方法
Feb 18 Python
Python工程师面试题 与Python Web相关
Jan 14 Python
Python简单实现子网掩码转换的方法
Apr 13 Python
使用Python脚本实现批量网站存活检测遇到问题及解决方法
Oct 11 Python
用Django实现一个可运行的区块链应用
Mar 08 Python
numpy向空的二维数组中添加元素的方法
Nov 01 Python
删除DataFrame中值全为NaN或者包含有NaN的列或行方法
Nov 06 Python
Python 计算任意两向量之间的夹角方法
Jul 05 Python
手把手教你Python yLab的绘制折线图的画法
Oct 23 Python
Python基于百度AI实现OCR文字识别
Apr 02 Python
如何编写python的daemon程序
Jan 07 Python
python函数指定默认值的实例讲解
Mar 29 Python
jupyter notebook保存文件默认路径更改方法汇总(亲测可以)
Django rest framework如何自定义用户表
Jun 09 #Python
如何使用Python提取Chrome浏览器保存的密码
Jun 09 #Python
python缺失值的解决方法总结
Jun 09 #Python
Python提取PDF指定内容并生成新文件
Python激活Anaconda环境变量的详细步骤
Jun 08 #Python
Python序列化与反序列化相关知识总结
Jun 08 #Python
You might like
PHP中10个不常见却非常有用的函数
2010/03/21 PHP
php调用自己java程序的方法详解
2016/05/13 PHP
PHP实现从PostgreSQL数据库检索数据分页显示及根据条件查找数据示例
2018/06/09 PHP
jquery.validate使用攻略 第五步 正则验证
2010/07/01 Javascript
JQuery 应用 JQuery.groupTable.js
2010/12/15 Javascript
JavaScript验证18位身份证号码最后一位正确性的实现代码
2014/08/07 Javascript
深入理解javascript作用域和闭包
2014/09/23 Javascript
javascript的函数作用域
2014/11/12 Javascript
javascript实现带节日和农历的日历特效
2015/02/01 Javascript
jquery实现焦点图片随机切换效果的方法
2015/03/12 Javascript
JavaScript自定义等待wait函数实例分析
2015/03/23 Javascript
jQuery Mobile中的button按钮组件基础使用教程
2016/05/23 Javascript
Jquery和JS获取ul中li标签的实现方法
2016/06/02 Javascript
js判断空对象的实例(超简单)
2016/07/26 Javascript
Jquery Easyui验证组件ValidateBox使用详解(20)
2016/12/18 Javascript
详解vue-validator(vue验证器)
2017/01/16 Javascript
Element UI 自定义正则表达式验证方法
2018/09/04 Javascript
JQuery Ajax执行跨域请求数据的解决方案
2018/12/10 jQuery
javascript 设计模式之享元模式原理与应用详解
2020/04/08 Javascript
[06:33]DOTA2亚洲邀请赛小组赛第二日 TOP10精彩集锦
2015/01/31 DOTA
python装饰器decorator介绍
2014/11/21 Python
python获取各操作系统硬件信息的方法
2015/06/03 Python
通过Python爬虫代理IP快速增加博客阅读量
2016/12/14 Python
Django 浅谈根据配置生成SQL语句的问题
2018/05/29 Python
Python3实现的判断环形链表算法示例
2019/03/07 Python
Python如何基于smtplib发不同格式的邮件
2019/12/30 Python
PageFactory设计模式基于python实现
2020/04/14 Python
为什么说python更适合树莓派编程
2020/07/20 Python
如何基于Python爬虫爬取美团酒店信息
2020/11/03 Python
护理专业毕业生推荐信
2013/10/31 职场文书
旅行社各个岗位职责
2014/03/15 职场文书
青年志愿者活动总结
2014/04/26 职场文书
就业协议书
2014/09/12 职场文书
护士辞职信怎么写
2015/02/27 职场文书
教师节主题班会方案
2015/08/17 职场文书
市直属机关2016年主题党日活动总结
2016/04/05 职场文书