Python流式游標(biāo)與緩存式(默認(rèn))游標(biāo)的那些坑及解決
一. 起因
本問(wèn)題起源于自己在服務(wù)器大量解析圖片數(shù)據(jù)。
運(yùn)行過(guò)程中出現(xiàn)錯(cuò)誤:
(2013, 'Lost connection to MySQL server during query')
因?yàn)檫@個(gè),我很認(rèn)真的仔細(xì)的查了Mysql有關(guān)的timeout的問(wèn)題:見(jiàn)“Mysql的timeout 以及 python重連”。
最后排查自己的問(wèn)題不出在connect_time(兩個(gè)sql語(yǔ)句之間的等待時(shí)間),而在于我在不斷獲取下一排數(shù)據(jù)項(xiàng)(即調(diào)用fetchone()方法)中間進(jìn)行大量耗時(shí)的工作,導(dǎo)致超過(guò)net_write_time而連接自動(dòng)斷開(kāi)。
原代碼:
#這是一個(gè)類方法里面的內(nèi)容,簡(jiǎn)化版 sql = "select * from storys" try: # 獲取一個(gè)鏈接,一個(gè)cursor,執(zhí)行SQL語(yǔ)句,MysqlController為封裝的數(shù)據(jù)庫(kù)連接池處理工具 conn = MysqlController.getConn() cursor = conn.cursor() cursor.execute(sql) #信號(hào)鎖 semaphore = threading.BoundedSemaphore(self.maxRunThread) while(True): result = cursor.fetchone() if not result: break #這里不是重點(diǎn) #——————————————————————————————————————————————————————————————————————————————————————————— #這是一個(gè)for循環(huán),里面開(kāi)啟了10個(gè)線程,每次開(kāi)啟線程之前要求獲得到信號(hào)鎖。 #每個(gè)線程run方法結(jié)束之后會(huì)調(diào)用semaphore.release(),以此保證同時(shí)運(yùn)行的線程不超過(guò)self.maxRunThread個(gè) 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)
但是非常奇怪的是,盡管報(bào)錯(cuò)Lost connection,但是我之后的數(shù)據(jù)依舊取到了,并完成分析了。
于是我做了一個(gè)小測(cè)試:
我預(yù)計(jì)取10個(gè)數(shù)據(jù),在取第3個(gè)數(shù)據(jù)的時(shí)候,手動(dòng)把連接斷開(kāi)了。
按照我之前對(duì)fetchone()的理解,是每一次cursor在mysql中下移一個(gè)位置,返回給客戶端。
如果連接一旦斷開(kāi),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
...
可以看見(jiàn),手動(dòng)關(guān)閉連接后,cursor依舊能取到數(shù)據(jù)。
最后結(jié)論:dbq,是我菜了,這個(gè)是假的fetchone。
二. 正事兒
經(jīng)過(guò)查詢,python中的cursor主要分為兩大類:
非緩存式游標(biāo)和緩存式游標(biāo)。
cursor = connection.cursor()
這種方法默認(rèn)的是緩存式游標(biāo),緩存式游標(biāo)顧名思義,不管是fetchone還是fetchall都是在執(zhí)行語(yǔ)句的時(shí)候一次性返回所有數(shù)據(jù)到客戶端。
這種返回在數(shù)據(jù)量特別大的時(shí)候無(wú)疑是不利的,會(huì)占用大量?jī)?nèi)存,導(dǎo)致我的主機(jī)卡成ppt。
最正確的用法是使用非緩存式游標(biāo),即流式游標(biāo)(SSCursor也可以):
cursor = conn.cursor(pymysql.cursors.SSDictCursor)#返回字典式數(shù)據(jù)
三. 小石子Warning
在查明白cursor的區(qū)別后,我非常開(kāi)心的把游標(biāo)換了。
但由于之前曾經(jīng)處理過(guò)timeout的問(wèn)題,我曾經(jīng)非常多此一舉的在代碼里面添加過(guò)ping():
#這是一個(gè)類方法里面的內(nèi)容,簡(jiǎn)化版 sql = "select * from storys" try: # 獲取一個(gè)鏈接,一個(gè)cursor,執(zhí)行SQL語(yǔ)句,MysqlController為封裝的數(shù)據(jù)庫(kù)連接池處理工具 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)
要知道,在一個(gè)循環(huán)的不斷取數(shù)據(jù)的過(guò)程中,如果使用了ping就會(huì)使之前的查詢斷掉。
于是報(bào)錯(cuò):
UserWarning: Previous unbuffered result was left incomplete warnings.warn("Previous unbuffered result was left incomplete")
這個(gè)是由于查詢未完成而造成的,去掉ping就好了。
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
如何解決Pycharm運(yùn)行報(bào)錯(cuò)No Python interpreter selected
這篇文章主要介紹了如何解決Pycharm運(yùn)行時(shí)No Python interpreter selected問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-05-05Python Django網(wǎng)頁(yè)界面協(xié)同過(guò)濾推薦算法實(shí)現(xiàn)商品管理與推薦
商品管理與推薦系統(tǒng),本系統(tǒng)使用Python作為主要開(kāi)發(fā)語(yǔ)言,前端采用HTML、CSS、BootStrap等技術(shù)搭建顯示界面,后端采用Django框架處理用戶的請(qǐng)求響應(yīng)2023-11-11python判斷端口是否打開(kāi)的實(shí)現(xiàn)代碼
python判斷端口是否打開(kāi)的代碼,有需要的朋友可以參考下2013-02-02MacOS(M1芯片 arm架構(gòu))下安裝PyTorch的詳細(xì)過(guò)程
這篇文章主要介紹了MacOS(M1芯片 arm架構(gòu))下安裝PyTorch的詳細(xì)過(guò)程,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-02-02Python基于輾轉(zhuǎn)相除法求解最大公約數(shù)的方法示例
這篇文章主要介紹了Python基于輾轉(zhuǎn)相除法求解最大公約數(shù)的方法,結(jié)合實(shí)例形式分析了Python使用輾轉(zhuǎn)相除法求解最大公約數(shù)的實(shí)現(xiàn)方法與優(yōu)化操作技巧,需要的朋友可以參考下2018-04-04使用Python實(shí)現(xiàn)從麥克風(fēng)獲取音頻并識(shí)別
這篇文章主要為大家詳細(xì)介紹了如何使用Python實(shí)現(xiàn)從麥克風(fēng)獲取音頻并識(shí)別功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2025-02-02PyChar學(xué)習(xí)教程之自定義文件與代碼模板詳解
pycharm默認(rèn)的【新建】文件,格式很不友好,那么就需要改一下文件模板。下面這篇文章主要給大家介紹了關(guān)于PyChar學(xué)習(xí)教程之自定義文件與代碼模板的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友們下面跟著小編來(lái)一起看看吧。2017-07-07pycharm中下載的包但是import還是無(wú)法使用/報(bào)紅的解決方法
用pycharm開(kāi)發(fā)時(shí),在導(dǎo)入自己寫的python文件時(shí)出現(xiàn)模塊名爆紅的情況,下面這篇文章主要給大家介紹了關(guān)于pycharm中下載包但是import還是無(wú)法使用/報(bào)紅的解決方法,需要的朋友可以參考下2023-02-02Python與數(shù)據(jù)庫(kù)交互:入門指南
這篇文章主要介紹了Python與數(shù)據(jù)庫(kù)交互:入門指南的相關(guān)資料,需要的朋友可以參考下2023-11-11