Django 中 Python 多线程连接数据问题请教

2021-12-30 17:06:10 +08:00
 182247236

需求: 需要请求数据库中 1 个月的带宽数据。
遇到问题: 我的思路是按天请求数据,一个月 30 天,for 语句循环 30 次连接数据库,每次时间大约是 0.6 秒左右,30 次大概是 20 秒,可以完成查询。但毕竟是展示数据,感觉 20 秒时间太长了,想要缩短到 10 秒内。
我的解决方式: 想利用 python 的多线程( threading ),如果能同时请求 5 天的数据那么速度就能降到 5 秒内。
我遇到的问题: 使用的多线程,但并没有使查询时间缩短。
我是用的框架:Django
我的视图函数代码(代码有点多,但啥好办法了,逻辑是没问题的,跑出结果 20 秒,和 for 没区别):

def cdn_detail(request):

"""
获取域名带宽
"""
company_id = request.GET['company_id']
domain_id = request.GET['domain_id'].split(",")
domain = request.GET['domain_id']
daterange = request.GET['daterange'].split(' ~ ')
"""
filter(xx__in = list)
"""

import datetime
t1=time.time() 
def sql_execut(company_id,domain,date,backValue):
    with connection.cursor() as cursor:
        sql_select = f"SELECT timestrap, bps FROM cdn_bandwidth WHERE (company_id = {company_id} AND domain_id IN ({domain}) AND time >= '{date} 00:00:00' AND time <= '{date} 23:59:59')"
        cursor.execute(sql_select)
        row = cursor.fetchall()
        fr_row = pd.DataFrame(list(row), columns=['timestrap', 'bps'])
        backValue.put(fr_row)

day_range = pd.date_range(start=daterange[0], end=daterange[1]).strftime("%Y-%m-%d").to_list()#创建日期范围 list
import threading
from queue import Queue
threads =[]
n = range(len(day_range))
backValue = Queue()
frame = pd.DataFrame(columns=['timestrap', 'bps'])#创建一个空 DataFrame
for i in n:
    t=threading.Thread(target=sql_execut,args=(company_id,domain,day_range[i],backValue))
    t.start()
    threads.append(t)
for i in threads:
    i.join()
for _ in n:
    frame = frame.append(backValue.get()).groupby('timestrap')['bps'].sum().reset_index()
frame_sum = frame.values.tolist()
t2=time.time()
print("相差",(datetime.datetime.fromtimestamp(t2)-datetime.datetime.fromtimestamp(t1)).seconds,"秒")
point = int(len(frame_sum)/100*95)#95 值的点
value_95 = sorted(frame_sum, key = lambda k:k[1])[point][1]#95 值
context = {
    'datas': frame_sum,
    'value_95' : value_95
}
datas = json.dumps(context, ensure_ascii=False)
return HttpResponse(datas, content_type="application/json")
2319 次点击
所在节点    Python
26 条回复
182247236
2022-01-02 23:06:44 +08:00
@lybcyd 一个月的数据大概有 80 万条左右,用 mysql 做 group by sum 真的估计更慢了,这步我用 pandas 处理其实挺好的了,这个业务是内部运维用的,SQL 注入这些因为我不是专业的,所以暂时先不理会吧
182247236
2022-01-02 23:07:45 +08:00
@meiyoumingzi6 我的处理结果是 SQL 慢了,pandas 还是很给力的。。。哭
182247236
2022-01-02 23:10:12 +08:00
@neoblackcap 我的做法是写多线程,每个线程都请求数据库,但你说的这个很有可能是我遇到的问题,我节后好好看看
182247236
2022-01-02 23:12:53 +08:00
@Anivial 这个方式最近有想过,但是没有专业学过数据库,又着急,所以往后可能会考虑分表,SQL 注入这个超纲了。。。
182247236
2022-01-02 23:17:35 +08:00
@julyclyde 我这边有 100 多个域名,一个月的数据就是 80 多万条了,查起来确实太慢了。所以想用 python 的多线程解决。。。SQL 注入漏洞的问题后面再解决吧。。
patrickpu
2022-03-18 17:43:06 +08:00
性能慢主要慢在两个方面,一个是 python 的 for 循环,一个是获取 sql 查询数据。
pymysql 是纯 python 的,而 python 的 for 循环性能是很低的,低的惨不忍听,数据量不大还好,当你一次要处理+10w 数据的时候性能就很感人了,最好的方法是用 cython 把.py 转成动态链接库.so 的形式,会有明显的加速效果。
当你查询的数据量大了后,pymysql 等 python 客户端执行的结果集获取是通过游标分块获取的,也就是说查询 1w 的数据,数据库往返请求可能会有 100 次,这些请求都是串行的是透明的,优化的方向可以考虑通过多线程并发,按 id 分块读,同时指定 limit
见 pymysql 的 cursor_iter:
def cursor_iter(cursor, sentinel, col_count, itersize):
"""
Yield blocks of rows from a cursor and ensure the cursor is closed when
done.
"""
try:
for rows in iter((lambda: cursor.fetchmany(itersize)), sentinel):
yield rows if col_count is None else [r[:col_count] for r in rows]
finally:
cursor.close()

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/825345

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX