python多线程并发threading模块

最近开发一个获取所有集群从读写实例的接口,运行接口发现返回数据时间长,达到了9秒多,实在是太慢了!那么慢在哪里呢?

review代码,发现是因为需要读取二十多个不同的数据库查询结果,这些数据库分布在二十多台不同的物理机,代码采用for循环依次读取这些数据库,从而产生耗时严重的情况.

模拟for循环依次读取数据库脚本如下:

#!/usr/bin/python
# encoding=utf-8

import os
import mysql.connector

ossdb_dict = {'上海A':'xxx.xxx.xxx.xxx:3306','上海B':'xxx.xxx.xxx.xxx:3306','上海C':'xxx.xxx.xxx.xxx:3306','北京A':'xxx.xxx.xxx.xxx:3306','北京B':'xxx.xxx.xxx.xxx:3306','北美A':'xxx.xxx.xxx.xxx:3306','天津A':'xxx.xxx.xxx.xxx:3306','天津B':'xxx.xxx.xxx.xxx:3306','广州A':'xxx.xxx.xxx.xxx:3306','广州B':'xxx.xxx.xxx.xxx:3306','广州C':'xxx.xxx.xxx.xxx:3306','广州D':'xxx.xxx.xxx.xxx:3306','德国A':'xxx.xxx.xxx.xxx:3306','成都A':'xxx.xxx.xxx.xxx:3306','新加坡A':'xxx.xxx.xxx.xxx:3306','深圳A':'xxx.xxx.xxx.xxx:3306','深圳B':'xxx.xxx.xxx.xxx:3306','美国A':'xxx.xxx.xxx.xxx:3306','重庆A':'xxx.xxx.xxx.xxx:3306','香港A':'xxx.xxx.xxx.xxx:3306','韩国A':'xxx.xxx.xxx.xxx:3306','广州E':'xxx.xxx.xxx.xxx:3306'}

cluster_dict = {'上海A':49,'上海B':61,'上海C':63,'北京A':204,'北京B':206,'北美A':59,'天津A':64,'天津B':70,'广州A':73,'广州B':22,'广州C':60,'广州永顺':D,'德国A':202,'成都A':201,'新加坡A':71,'深圳A':74,'深圳B':81,'美国A':79,'重庆A':205,'香港A':52,'韩国A':207,'广州E':76}

slave_rw_sql = """select tb_mysql_pair.instance_name,tb_mysql_pair.app_name,tb_mysql_pair.master_port,tb_mysql_pair.slave_port,tb_device_pair.master_ip,tb_device_pair.slave_ip from tb_mysql_pair,tb_device_pair where tb_mysql_pair.status=19 and tb_mysql_pair.device_pair_id=tb_device_pair.pair_id;"""

db_user  = 'xxx'
db_pass = 'xxx'
access_db = 'xxx'   

def execDB(ossdb_dict,cluster_dict,db_user,db_pass,access_db,slave_rw_sql):
         
    cluster_ids  = []
    for key in cluster_dict:
        id   = cluster_dict[key] 
        cluster_ids.append(id)

    ossdb_list = [] 
    for cluster_id in cluster_ids:
        cluster_name = cluster_dict.keys()[cluster_dict.values().index(cluster_id)]
        ossdb  = ossdb_dict[cluster_name]

        ossdb_list.append(ossdb)

    #for循环一个一个读取数据库,是单线程的,所以会比较慢
    for ossdb in ossdb_list:
        cluster_name = ossdb_dict.keys()[ossdb_dict.values().index(ossdb)]
        db_host  = ossdb.split(':',1)[0]
        db_port   = ossdb.split(':',1)[1]

        conn = mysql.connector.connect(
            user  = db_user,
            password = db_pass,
            host  = db_host,
            port   = db_port,
            database = access_db)

        cur = conn.cursor()
        cur.execute(slave_rw_sql)
        slave_rw_results = cur.fetchall()


if __name__=='__main__':
    execDB(ossdb_dict,cluster_dict,db_user,db_pass,access_db,slave_rw_sql)

实际执行脚本,耗时情况:
time python 3.py

real 0m9.444s
user 0m0.060s
sys 0m0.016s

跑完脚本需要0m9.444s,很显然耗时是在for循环依次查询数据库产生的耗时.

是否有办法提高读取数据库的效率,消除耗时呢?答案是肯定的!

python的标准库提供了并发执行的多线程模块thread和threading,thread是低级模块,而threading是高级模块,对thread进行了封装。在绝大多数情况下,我们只需要使用threading这个高级模块.

在python中实现多线程有两种方式,一种就是函数形式,通过将需要执行的方法传入,然后创建多线程实例;另一种就是创建一个类,并且继承threading.Thread类来实现. 这里我只讲第一种方式,比较容易理解一些.

注:
什么是多线程呢?顾名思义,多线程就是在同一个进程的情况下拉起来多个线程,进程和线程之间的关系,就好比是工厂和工人之间的关系。工厂是一个,但是工人有多个,多人干活自然就可以提高生产效率。

来看看多线程并发执行的脚本:

#!/usr/bin/python
# encoding=utf-8


import os
import threading  #引入threading多线程模块
import mysql.connector

ossdb_dict = {'上海A':'xxx.xxx.xxx.xxx:3306','上海B':'xxx.xxx.xxx.xxx:3306','上海C':'xxx.xxx.xxx.xxx:3306','北京A':'xxx.xxx.xxx.xxx:3306','北京B':'xxx.xxx.xxx.xxx:3306','北美A':'xxx.xxx.xxx.xxx:3306','天津A':'xxx.xxx.xxx.xxx:3306','天津B':'xxx.xxx.xxx.xxx:3306','广州A':'xxx.xxx.xxx.xxx:3306','广州B':'xxx.xxx.xxx.xxx:3306','广州C':'xxx.xxx.xxx.xxx:3306','广州D':'xxx.xxx.xxx.xxx:3306','德国A':'xxx.xxx.xxx.xxx:3306','成都A':'xxx.xxx.xxx.xxx:3306','新加坡A':'xxx.xxx.xxx.xxx:3306','深圳A':'xxx.xxx.xxx.xxx:3306','深圳B':'xxx.xxx.xxx.xxx:3306','美国A':'xxx.xxx.xxx.xxx:3306','重庆A':'xxx.xxx.xxx.xxx:3306','香港A':'xxx.xxx.xxx.xxx:3306','韩国A':'xxx.xxx.xxx.xxx:3306','广州E':'xxx.xxx.xxx.xxx:3306'}

cluster_dict = {'上海A':49,'上海B':61,'上海C':63,'北京A':204,'北京B':206,'北美A':59,'天津A':64,'天津B':70,'广州A':73,'广州B':22,'广州C':60,'广州永顺':D,'德国A':202,'成都A':201,'新加坡A':71,'深圳A':74,'深圳B':81,'美国A':79,'重庆A':205,'香港A':52,'韩国A':207,'广州E':76}

slave_rw_sql = """select tb_mysql_pair.instance_name,tb_mysql_pair.app_name,tb_mysql_pair.master_port,tb_mysql_pair.slave_port,tb_device_pair.master_ip,tb_device_pair.slave_ip from tb_mysql_pair,tb_device_pair where tb_mysql_pair.status=19 and tb_mysql_pair.device_pair_id=tb_device_pair.pair_id;"""

db_user  = 'xxx'
db_pass  = 'xxx'
access_db  = 'xxx'   

def execDB(ossdb,ossdb_dict,db_user,db_pass,access_db,slave_rw_sql):
    cluster_name = ossdb_dict.keys()[ossdb_dict.values().index(ossdb)]
    db_host   = ossdb.split(':',1)[0]
    db_port    = ossdb.split(':',1)[1]
    
    conn = mysql.connector.connect(
        user  = db_user,
        password = db_pass,
        host   = db_host,
        port    = db_port,
        database = access_db)
        
    cur = conn.cursor()
    cur.execute(slave_rw_sql)
    slave_rw_results = cur.fetchall()


#定义多线程执行的函数multithread  
def multithread(ossdb_dict,cluster_dict,db_user,db_pass,access_db,slave_rw_sql):
    cluster_ids  = []
    for key in cluster_dict:
        id  = cluster_dict[key]
        cluster_ids.append(id)
        
    ossdb_list = []
    for cluster_id in cluster_ids:
        cluster_name = cluster_dict.keys()[cluster_dict.values().index(cluster_id)]
        ossdb   = ossdb_dict[cluster_name]
        
        ossdb_list.append(ossdb)
    
    #定义一个列表threads,存储要启动多线程的实例
    threads = []
    
    #循环读取多个ossdb,也就是启动了多个线程去查询DB啦~
    for ossdb in ossdb_list:
           #target表示实际要执行读取数据库的函数,multithread函数调用execDB函数,往execDB函数传参.
        t   = threading.Thread(target=execDB,args=(ossdb,ossdb_dict,db_user,db_pass,access_db,slave_rw_sql,))  
        threads.append(t)   #把要启动多线程的实例,追加到列表threads
        

    #把threads列表中的实例遍历出来后,调用start()方法启动多线程,就会有多个线程并发去读取数据库
    for thr in threads:
        thr.start()
    
    for thr in threads:
        #isAlive()可以返回true或者false,用来判断此时是否还有没有执行完的线程,如果还有未执行完的线程就让主线程等待线程执行结束之后,主线程再来结束.
        if thr.isAlive():
            thr.join()
    
if __name__=='__main__':
    multithread(ossdb_dict,cluster_dict,db_user,db_pass,access_db,slave_rw_sql)

上述脚本遍历了两次threads列表,最后一次遍历的目的是为了查看还有没有没有执行完成的子线程,只要还有子线程是活的,没有退出,就通过join()方法强制程序不可以让主线程退出,只有等所有子线程执行完成退出后,才能让主线程退出.

来看看采用多线程并发之后,实际执行脚本耗时:
time python 1.py
real 0m1.927s
user 0m0.064s
sys 0m0.012s

可以看到耗时0m1.927s,效率已经提升了好几倍,这个耗时在可接受范围.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容

  • Python 面向对象Python从设计之初就已经是一门面向对象的语言,正因为如此,在Python中创建一个类和对...
    顺毛阅读 4,207评论 4 16
  • 一文读懂Python多线程 1、线程和进程 计算机的核心是CPU,它承担了所有的计算任务。它就像一座工厂,时刻在运...
    星丶雲阅读 1,442评论 0 4
  • Python的面向对象 类 Class 类变量 Class variable 数据成员 Data member 函...
    JasonJe阅读 1,111评论 0 3
  • 晚风吹得特别的动人,落日的余晖被它拖着迟迟不肯熄灭,三杯两盏的街灯迫不及待地盛开在路旁,在灯影阑珊里,人影幢幢。我...
    采臣Allen阅读 419评论 0 0
  • 嗨、你知道吗? 我喜欢你、不知道从什么时候开始,你的模样深深的烙在了我的脑海里。 所以我总会想把最好的给你,可以坚...
    南辰先生阅读 346评论 6 2