Python流式游標(biāo)與緩存式(默認(rèn))游標(biāo)的那些坑及解決
一. 起因
本問題起源于自己在服務(wù)器大量解析圖片數(shù)據(jù)。
運行過程中出現(xiàn)錯誤:
(2013, 'Lost connection to MySQL server during query')
因為這個,我很認(rèn)真的仔細(xì)的查了Mysql有關(guān)的timeout的問題:見“Mysql的timeout 以及 python重連”。
最后排查自己的問題不出在connect_time(兩個sql語句之間的等待時間),而在于我在不斷獲取下一排數(shù)據(jù)項(即調(diào)用fetchone()方法)中間進行大量耗時的工作,導(dǎo)致超過net_write_time而連接自動斷開。
原代碼:
#這是一個類方法里面的內(nèi)容,簡化版
sql = "select * from storys"
try:
# 獲取一個鏈接,一個cursor,執(zhí)行SQL語句,MysqlController為封裝的數(shù)據(jù)庫連接池處理工具
conn = MysqlController.getConn()
cursor = conn.cursor()
cursor.execute(sql)
#信號鎖
semaphore = threading.BoundedSemaphore(self.maxRunThread)
while(True):
result = cursor.fetchone()
if not result:
break
#這里不是重點
#———————————————————————————————————————————————————————————————————————————————————————————
#這是一個for循環(huán),里面開啟了10個線程,每次開啟線程之前要求獲得到信號鎖。
#每個線程run方法結(jié)束之后會調(diào)用semaphore.release(),以此保證同時運行的線程不超過self.maxRunThread個
for i in range(10):
semaphore.acquire()
t = threading.Thread(target=self.run, args=(semaphore,i))
t.start()
#———————————————————————————————————————————————————————————————————————————————————————————
print('結(jié)束')
cursor.close()
conn.close()
except Exception as e:
print(e)但是非常奇怪的是,盡管報錯Lost connection,但是我之后的數(shù)據(jù)依舊取到了,并完成分析了。
于是我做了一個小測試:
我預(yù)計取10個數(shù)據(jù),在取第3個數(shù)據(jù)的時候,手動把連接斷開了。
按照我之前對fetchone()的理解,是每一次cursor在mysql中下移一個位置,返回給客戶端。
如果連接一旦斷開,cursor就不能獲取到數(shù)據(jù)了。
import pymysql
import time
def mytest():
connection = pymysql.connect(
host='localhost',
port=3306,
user='root',
password='',
db='*******',
charset='utf8')
cursor = connection.cursor()
cursor.execute("select * from storys limit 10")
data = cursor.fetchone()
i = 0
while data != None:
if(i == 3):
connection.close()
print('connection is close')
print(data[0])
i+=1
data = cursor.fetchone()
cursor.close()
connection.close()
if __name__ == '__main__':
mytest()但是結(jié)果:
python3 run.py
23
24
25
connection is close
26
...
可以看見,手動關(guān)閉連接后,cursor依舊能取到數(shù)據(jù)。
最后結(jié)論:dbq,是我菜了,這個是假的fetchone。
二. 正事兒
經(jīng)過查詢,python中的cursor主要分為兩大類:
非緩存式游標(biāo)和緩存式游標(biāo)。
cursor = connection.cursor()
這種方法默認(rèn)的是緩存式游標(biāo),緩存式游標(biāo)顧名思義,不管是fetchone還是fetchall都是在執(zhí)行語句的時候一次性返回所有數(shù)據(jù)到客戶端。
這種返回在數(shù)據(jù)量特別大的時候無疑是不利的,會占用大量內(nèi)存,導(dǎo)致我的主機卡成ppt。
最正確的用法是使用非緩存式游標(biāo),即流式游標(biāo)(SSCursor也可以):
cursor = conn.cursor(pymysql.cursors.SSDictCursor)#返回字典式數(shù)據(jù)
三. 小石子Warning
在查明白cursor的區(qū)別后,我非常開心的把游標(biāo)換了。
但由于之前曾經(jīng)處理過timeout的問題,我曾經(jīng)非常多此一舉的在代碼里面添加過ping():
#這是一個類方法里面的內(nèi)容,簡化版
sql = "select * from storys"
try:
# 獲取一個鏈接,一個cursor,執(zhí)行SQL語句,MysqlController為封裝的數(shù)據(jù)庫連接池處理工具
conn = MysqlController.getConn()
cursor = conn.cursor(pymysql.cursors.SSDictCursor)#這里改游標(biāo)類型了?。。。。。?!
cursor.execute(sql)
while(True):
conn.ping()# 就是這句話?。。。。。。。?!畫蛇添足?。。。。。?!正確代碼去掉這句話
result = cursor.fetchone()
if not result:
break
#做了一些事兒
print('結(jié)束')
cursor.close()
conn.close()
except Exception as e:
print(e)要知道,在一個循環(huán)的不斷取數(shù)據(jù)的過程中,如果使用了ping就會使之前的查詢斷掉。
于是報錯:
UserWarning: Previous unbuffered result was left incomplete warnings.warn("Previous unbuffered result was left incomplete")
這個是由于查詢未完成而造成的,去掉ping就好了。
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Python過濾txt文件內(nèi)重復(fù)內(nèi)容的方法
今天小編就為大家分享一篇Python過濾txt文件內(nèi)重復(fù)內(nèi)容的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-10-10
YOLOv5車牌識別實戰(zhàn)教程(七)實時監(jiān)控與分析
這篇文章主要介紹了YOLOv5車牌識別實戰(zhàn)教程(七)實時監(jiān)控與分析,在這個教程中,我們將一步步教你如何使用YOLOv5進行車牌識別,幫助你快速掌握YOLOv5車牌識別技能,需要的朋友可以參考下2023-04-04
Python編程實現(xiàn)雙擊更新所有已安裝python模塊的方法
這篇文章主要介紹了Python編程實現(xiàn)雙擊更新所有已安裝python模塊的方法,涉及Python針對模塊操作命令的相關(guān)封裝與調(diào)用技巧,需要的朋友可以參考下2017-06-06
Python 統(tǒng)計列表中重復(fù)元素的個數(shù)并返回其索引值的實現(xiàn)方法
這篇文章主要介紹了Python 統(tǒng)計列表中重復(fù)元素的個數(shù)并返回其索引值,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-05-05

