python常用庫(kù)之?dāng)?shù)據(jù)庫(kù)orm框架之SQLAlchemy
一、什么是SQLAlchemy
官網(wǎng):https://www.sqlalchemy.org/
SQLAlchemy是一個(gè)基于Python實(shí)現(xiàn)的SQL工具包和ORM框架,提供了高層抽象來(lái)管理數(shù)據(jù)庫(kù)交互。
SQLAlchemy功能強(qiáng)大,可以省去很多手動(dòng)管理數(shù)據(jù)庫(kù)連接、資源、事務(wù)等重復(fù)工作,讓開(kāi)發(fā)者更加高效地使用數(shù)據(jù)庫(kù)。許多大型Python項(xiàng)目都選擇使用SQLAlchemy作為ORM框架。
SQLAlchemy 使用場(chǎng)景
SQLAlchemy是一個(gè)強(qiáng)大的Python ORM框架,主要應(yīng)用于以下場(chǎng)景:
- 數(shù)據(jù)庫(kù)訪(fǎng)問(wèn)和操作:SQLAlchemy提供了高層抽象來(lái)操作數(shù)據(jù)庫(kù),可以避免寫(xiě)原生SQL語(yǔ)句。支持多種數(shù)據(jù)庫(kù)后端。
- ORM映射:建立Python類(lèi)與數(shù)據(jù)庫(kù)表的映射關(guān)系,簡(jiǎn)化數(shù)據(jù)模型的操作,支持聲明式操作。
- 復(fù)雜查詢(xún): SQLAlchemy提供豐富的查詢(xún)方式,如過(guò)濾、分組、聯(lián)結(jié)等,可以構(gòu)建復(fù)雜查詢(xún)。
- 異步查詢(xún):基于Greenlet等實(shí)現(xiàn)異步查詢(xún),提高查詢(xún)效率。
- 事務(wù)控制: 通過(guò)Session管理數(shù)據(jù)庫(kù)會(huì)話(huà)和事務(wù)。
- 工具集成:如數(shù)據(jù)遷移工具Alembic,可以實(shí)現(xiàn)Schema版本控制和遷移。
- 大數(shù)據(jù)集查詢(xún):基于Pagination實(shí)現(xiàn)數(shù)據(jù)分頁(yè),避免大量數(shù)據(jù)查詢(xún)內(nèi)存溢出。
- 多數(shù)據(jù)庫(kù)支持:支持Postgres、MySQL、Oracle等主流數(shù)據(jù)庫(kù)。
- Web框架集成:框架如Flask可以集成SQLAlchemy,便于Web應(yīng)用開(kāi)發(fā)。
二、SQLAlchemy使用
SQLAlchemy根據(jù)模型查詢(xún)
- 引入和初始化
導(dǎo)入sqlalchemy模塊,然后使用create_engine
創(chuàng)建引擎,sessionmaker
創(chuàng)建會(huì)話(huà)類(lèi)并生成會(huì)話(huà)對(duì)象。
from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker
engine = create_engine(SQLALCHEMY_DATABASE_URI)
db_session = scoped_session(sessionmaker(autoflush=False,bind=engine))
- 使用sql 或者 數(shù)據(jù)模型
這里先舉例模型方式:
在SQLAlchemy中,declarative_base()用來(lái)創(chuàng)建一個(gè)基類(lèi),這個(gè)基類(lèi)會(huì)為所有繼承它的子類(lèi)提供declarative的ORM功能。
TblObject = declarative_base() 的作用是:
3. 創(chuàng)建了一個(gè)名為T(mén)blObject的基類(lèi)
4. 這個(gè)TblObject具有declarative的功能
5. 后續(xù)定義的模型類(lèi)可以繼承這個(gè)TblObject基類(lèi)來(lái)使用declarative
from sqlalchemy.orm import declarative_base
from sqlalchemy import Column, Text, Integer,String, VARCHAR, TIMESTAMP, BOOLEAN, Float, text
TblObject = declarative_base()
class User(TblObject):
__tablename__ = 'table_user'
__table_args__ = ({"schema": "public"})
id = Column(Integer, primary_key=True)
name = Column(String)
query = db_session.query(User)
user = query.first()
print(user.name)
SQLAlchemy SQL 格式化的方式
- 將sql語(yǔ)句本身不包含參數(shù)格式化,直接作為文本構(gòu)建完整的sql:
要想實(shí)現(xiàn)變量替換,需要使用f-string的格式
myschema = 'xxxx'
sql = text(f'SELECT id FROM {myschema }.table...')
注意看字符串前面有個(gè)f。
- 使用參數(shù)綁定的方式,而不是%格式化:
sql = text('SELECT id FROM :schema.table...')
params = {'schema': 'my_schema'}
result = session.execute(sql, params)
總結(jié):
- sql文本內(nèi)不要再使用% formatting
- 使用f-string或參數(shù)綁定格式化
- 創(chuàng)建text對(duì)象時(shí)直接構(gòu)建完整sql文本
參數(shù)綁定也可以提高安全性,避免SQL注入\
db_session.query和 db_session.execute區(qū)別
在SQLAlchemy中,db_session.query()和db_session.execute()主要有以下幾點(diǎn)區(qū)別:
- 返回值不同:
- db_session.query() 返回一個(gè)Query對(duì)象,可以用于構(gòu)建查詢(xún)并最終獲取結(jié)果。
- db_session.execute() 直接執(zhí)行語(yǔ)句并返回結(jié)果。
- 查詢(xún)方式不同:
- db_session.query() 通過(guò)ORM構(gòu)建查詢(xún)。
- db_session.execute() 通過(guò)原生SQL語(yǔ)句查詢(xún)。
- 查詢(xún)靈活性不同:
- db_session.query() 可以構(gòu)建非常靈活的查詢(xún),包含各種過(guò)濾、JOIN等。
- db_session.execute() 只能執(zhí)行簡(jiǎn)單的SQL語(yǔ)句查詢(xún)。
- 返回結(jié)果不同:
- db_session.query() 返回的是ORM對(duì)象或者自定義類(lèi)的實(shí)例。
- db_session.execute() 直接返回行數(shù)據(jù)組成的列表。
- 性能不同:
- 對(duì)簡(jiǎn)單查詢(xún),db_session.execute()往往更快。
- 對(duì)復(fù)雜查詢(xún),db_session.query()可以通過(guò)ORM特性進(jìn)行優(yōu)化。
所以,簡(jiǎn)單來(lái)說(shuō):
- db_session.query() 是面向?qū)ο蟮牟樵?xún)方式,更靈活,但復(fù)雜查詢(xún)可能有性能問(wèn)題。
- db_session.execute() 是執(zhí)行原生SQL的直接查詢(xún),性能好但不夠靈活。
需要特別注意的點(diǎn):使用 db_session.query() 后獲取的 ORM 對(duì)象實(shí)例,在訪(fǎng)問(wèn)其屬性之前,需要確保與該查詢(xún)關(guān)聯(lián)的 db_session 沒(méi)有關(guān)閉或失效。
否則報(bào)錯(cuò):
raise orm_exc.DetachedInstanceError(
sqlalchemy.orm.exc.DetachedInstanceError: Instance <User at 0x7f2973d6fbe0> is not bound to a Session; attribute refresh operation cannot proceed (Background on this error at: https://sqlalche.me/e/20/bhk3)
通常的使用模式是:
db_session = Session()
# 創(chuàng)建查詢(xún)
query = db_session.query(User)
# 獲取用戶(hù)對(duì)象
user = query.first()
# 訪(fǎng)問(wèn)屬性
print(user.name)
# 關(guān)閉會(huì)話(huà)
db_session.close()
在關(guān)閉 db_session 之前,需要先完成對(duì)其查詢(xún)結(jié)果的處理和訪(fǎng)問(wèn)。
如果在獲取到 user 對(duì)象后立即關(guān)閉會(huì)話(huà),然后訪(fǎng)問(wèn) user.name,會(huì)發(fā)生 detached實(shí)例錯(cuò)誤。
因?yàn)樵跁?huì)話(huà)關(guān)閉后,user 對(duì)象脫離了會(huì)話(huà),屬性無(wú)法加載。
使用 db_session.execute() 執(zhí)行原生SQL查詢(xún)general不會(huì)存在會(huì)話(huà)過(guò)早關(guān)閉導(dǎo)致屬性訪(fǎng)問(wèn)錯(cuò)誤的問(wèn)題。
db_session.execute() 返回的是數(shù)據(jù)庫(kù)行記錄組成的結(jié)果集,不涉及 ORM 對(duì)象映射。
實(shí)測(cè)demo 總結(jié):讓我們留意一下SQLAlchemy 的 lazy loading 特性
測(cè)試結(jié)果:經(jīng)過(guò)測(cè)試,query = db_session.query(User).all()
這樣db_session.close()
后,使用query[0].name
會(huì)報(bào)錯(cuò),但是query = db_session.query(User)
,close之后 query的數(shù)據(jù)還能用。
db_session.query(User) 只是構(gòu)造了一個(gè)查詢(xún),并沒(méi)有立即獲取結(jié)果。
而 db_session.query(User).all() 執(zhí)行了查詢(xún)并獲取了結(jié)果。
在會(huì)話(huà)關(guān)閉后,這兩種情況的行為不同:
- 對(duì)于僅構(gòu)造查詢(xún)的 db_session.query(User),由于沒(méi)有結(jié)果產(chǎn)生,所以不會(huì)有 detached 實(shí)例的問(wèn)題。我們可以在會(huì)話(huà)關(guān)閉后,使用這個(gè)查詢(xún)加上過(guò)濾條件等再次執(zhí)行,產(chǎn)生新結(jié)果。
- 但對(duì)于立即執(zhí)行了的 db_session.query(User).all(),其結(jié)果中的實(shí)例與關(guān)閉后的會(huì)話(huà)失去關(guān)聯(lián),成為 detached 實(shí)例,會(huì)導(dǎo)致訪(fǎng)問(wèn)屬性失敗。
這與 SQLAlchemy 的 lazy loading 特性有關(guān) - 查詢(xún)只有在需要時(shí)才執(zhí)行和加載實(shí)例。
- 構(gòu)造查詢(xún)只生成了一個(gè)執(zhí)行計(jì)劃,不涉及具體實(shí)例數(shù)據(jù),所以會(huì)話(huà)關(guān)閉后計(jì)劃還可以再執(zhí)行。
- 但執(zhí)行查詢(xún)并獲取實(shí)例,實(shí)際上已經(jīng)加載了具體的數(shù)據(jù),所以會(huì)依賴(lài)會(huì)話(huà)提供的數(shù)據(jù)狀態(tài)。
scoped session
使用 db_session.query() 時(shí),為了避免訪(fǎng)問(wèn) detached 實(shí)例,我們希望延遲關(guān)閉會(huì)話(huà) db_session。但這樣就無(wú)法及時(shí)提交事務(wù),可能會(huì)導(dǎo)致鎖表問(wèn)題。
解決方法:
- 在查詢(xún)之后、訪(fǎng)問(wèn)實(shí)例屬性之前,先顯式提交事務(wù):
db_session.query(...)
db_session.commit() # 提交事務(wù)
obj.some_attr # 訪(fǎng)問(wèn)屬性
這可以先釋放鎖,同時(shí)實(shí)例也綁定到會(huì)話(huà)中。
- 使用scoped session,可以避免手動(dòng) close db_session。
from sqlalchemy.orm import sessionmaker, scoped_session
session_factory = sessionmaker(bind=engine)
Session = scoped_session(session_factory)
def get_user():
session = Session()
return session.query(User).first()
這里的Session是一個(gè)scoped session類(lèi)。其特點(diǎn)是:
- 每個(gè)線(xiàn)程或請(qǐng)求都會(huì)自動(dòng)創(chuàng)建一個(gè)新的session實(shí)例,避免同一個(gè)session跨線(xiàn)程/請(qǐng)求使用。
- 使用完session后不需要關(guān)閉它,scoped session會(huì)在當(dāng)前上下文退出后自動(dòng)關(guān)閉、invalidate該session。
- 所以我們只需要使用Session類(lèi)創(chuàng)建session,不需要close。
- 不同的框架可以集成scoped session到自己的上下文。比如Flask集成后,每個(gè)請(qǐng)求都會(huì)自動(dòng)開(kāi)啟一個(gè)session,請(qǐng)求結(jié)束后自動(dòng)關(guān)閉。
- 這樣就可以不需要我們手動(dòng)管理session的生命周期。
所以綜上,scoped session通過(guò)封裝context管理,讓session的生命周期與當(dāng)前上下文(線(xiàn)程、請(qǐng)求等)綁定,自動(dòng)開(kāi)啟和關(guān)閉,省去了手動(dòng)管理的問(wèn)題。
總結(jié):
方法1: 我們只調(diào)commmit,這里不調(diào)colse(),需要其他地方,只有在確定不用的情況下才調(diào)close。
方法2:我們使用scoped_session,scoped session 的生命周期由應(yīng)用管理,不需要我們手動(dòng)關(guān)閉。
使用scoped_session,這個(gè) session 實(shí)例綁定到線(xiàn)程/請(qǐng)求,在其線(xiàn)程結(jié)束時(shí)自動(dòng)關(guān)閉.
關(guān)于scoped_session自動(dòng)提交事務(wù)
scoped_session 的工作原理是:
- 為每個(gè)線(xiàn)程或請(qǐng)求創(chuàng)建一個(gè)新的 session 實(shí)例
- 這個(gè) session 實(shí)例綁定到線(xiàn)程/請(qǐng)求,在其結(jié)束時(shí)自動(dòng)關(guān)閉
- 開(kāi)發(fā)者只需要使用 Session 類(lèi)即可,不需要手動(dòng)關(guān)閉
也就是說(shuō),每個(gè)線(xiàn)程/請(qǐng)求都會(huì)有一個(gè)獨(dú)立的 session 實(shí)例,這個(gè)實(shí)例不會(huì)自動(dòng)提交。
如果我們需要自動(dòng)提交,需要在使用 session 的時(shí)候設(shè)置:
在會(huì)話(huà)層面設(shè)置自動(dòng)提交:
from sqlalchemy.orm import sessionmaker
Session = sessionmaker(bind=engine)
session = Session()
session.autocommit = True
通過(guò)設(shè)置session.autocommit啟用自動(dòng)提交。這會(huì)針對(duì)當(dāng)前線(xiàn)程的 session 實(shí)例開(kāi)啟自動(dòng)提交。
另外,scoped_session 也不建議使用自動(dòng)提交模式,因?yàn)闀?huì)有并發(fā)問(wèn)題。推薦的方式還是手動(dòng)提交控制事務(wù)。
總結(jié),對(duì)于 scoped_session:
- 在創(chuàng)建時(shí)設(shè)置autocommit 不會(huì)生效
- 應(yīng)在使用時(shí)針對(duì) session 實(shí)例設(shè)置autocommit
- 推薦的方式仍是手動(dòng)提交控制事務(wù)
參數(shù)autoflush=False 的工作機(jī)制
db_session = scoped_session(sessionmaker(autoflush=False, bind=engine))
db_session = scoped_session(sessionmaker(bind=engine))
如上,有什么區(qū)別?
關(guān)閉自動(dòng) flush(autoflush=False)的主要目的是可以更精細(xì)地控制 flush 的時(shí)機(jī),原因如下:
- 自動(dòng) flush 可能導(dǎo)致不必要的數(shù)據(jù)庫(kù)寫(xiě)入操作,比如在一個(gè)事務(wù)中有多次 CRUD 操作時(shí),希望只在事務(wù)提交時(shí)一次性寫(xiě)入,而不是每次操作都觸發(fā) flush。
- 在一些場(chǎng)景下需要確保 flush 只在事務(wù)提交時(shí)發(fā)生,如兩個(gè)關(guān)聯(lián)對(duì)象的變更,希望它們的變更作為一個(gè)事務(wù)執(zhí)行。
- 需要根據(jù)業(yè)務(wù)邏輯精確控制 flush 時(shí)機(jī),而不是通過(guò)隱式的自動(dòng) flush。
SQLAlchemy中的session.commit()內(nèi)部會(huì)自動(dòng)調(diào)用session.flush()來(lái)保證所有pending的變更都被持久化。
即使設(shè)置autoflush=False,commit操作也會(huì)強(qiáng)制執(zhí)行flush。只是在commit之外的其他情況下,需要手動(dòng)調(diào)用flush。
在autoflush=False
的情況下:
- 正常的增刪改操作不會(huì)自動(dòng)flush
- 調(diào)用commit會(huì)觸發(fā)強(qiáng)制flush以持久化變更
這是SQLAlchemy的一種保護(hù)機(jī)制,來(lái)確保在事務(wù)提交時(shí)不會(huì)丟失還未flush的變更。
autoflush=False
需要手動(dòng)flush,但不影響commit的自動(dòng)flush行為。
三、關(guān)于SQLAlchemy自動(dòng)提交事務(wù)
背景:
工作發(fā)現(xiàn)使用,出現(xiàn)很多 sqlalchemy出現(xiàn)很多未提交事務(wù) 情況。
demo:
from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy import text
username="aaa"
password="xxx"
host="127.0.0.1"
port="5432"
db="xx"
sqlalchemy_uri = f"postgresql://{username}:{password}@{host}:{port}/{db}"
engine = create_engine(sqlalchemy_uri)
#engine = create_engine(sqlalchemy_uri, echo=False, client_encoding='utf8', pool_size=100, pool_recycle=3600)
db_session = scoped_session(sessionmaker(bind=engine))
sql = text('SELECT * FROM auth_user WHERE id = :userid;')
userid=1
# execute() 方法僅僅是執(zhí)行查詢(xún),不會(huì)自動(dòng)提交事務(wù)。
objs = db_session.execute(sql, {"userid": userid}).fetchall()
print(objs)
**每個(gè)session默認(rèn)都是在一個(gè)事務(wù)中,不會(huì)自動(dòng)提交。**因此當(dāng)你調(diào)用db_session.execute(sql)
時(shí),會(huì)默認(rèn)復(fù)用當(dāng)前session的事務(wù)來(lái)執(zhí)行,而這個(gè)session已經(jīng)在一個(gè)未提交的事務(wù)中了。
這與直接調(diào)用begin()開(kāi)始一個(gè)新事務(wù)其實(shí)效果是一樣的。
PostgreSQL 和 MySQL 自動(dòng)提交區(qū)別
PostgreSQL 的默認(rèn)設(shè)置是自動(dòng)提交關(guān)閉的,每次事務(wù)需要顯式地通過(guò) COMMIT 提交。
PostgreSQL的事務(wù)autocommit是transaction級(jí)的。每個(gè)transaction需要顯式提交或回滾。
在PostgreSQL中,autocommit是作用于每個(gè)事務(wù)(transaction)的,主要體現(xiàn)在以下兩個(gè)方面:
- 新建立的連接默認(rèn)都是非自動(dòng)提交狀態(tài)(autocommit=off)。這個(gè)連接下的所有事務(wù)操作默認(rèn)都是非自動(dòng)提交的。
- 開(kāi)始一個(gè)新的事務(wù)時(shí),這個(gè)事務(wù)會(huì)繼承連接當(dāng)前的autocommit狀態(tài)。如果連接是非自動(dòng)提交的,那么這個(gè)事務(wù)也是非自動(dòng)提交的。
舉個(gè)例子:
– 新建立連接,默認(rèn)autocommit=off
postgres=# begin; -- 開(kāi)始一個(gè)新事務(wù),繼承連接的autocommit=off
postgres=# insert into table_a values (1); -- 非自動(dòng)提交
postgres=# commit; -- 需要顯式提交事務(wù)
新連接默認(rèn)的是非自動(dòng)提交狀態(tài)。當(dāng)開(kāi)始一個(gè)新的事務(wù)時(shí),這個(gè)事務(wù)繼承了連接的非自動(dòng)提交屬性。所以我們必須通過(guò)提交事務(wù)來(lái)持久化改變。
- MySQL 的默認(rèn)設(shè)置是自動(dòng)提交開(kāi)啟的,每條語(yǔ)句會(huì)自動(dòng)提交事務(wù)。
MySQL的事務(wù)autocommit標(biāo)志是session級(jí)的。也就是說(shuō),一個(gè)會(huì)話(huà)內(nèi)全部操作默認(rèn)都是自動(dòng)提交的。
您的問(wèn)題提到了MySQL中的自動(dòng)提交模式,這和PostgreSQL中的事務(wù)處理模式有些不同。
MySQL中的自動(dòng)提交(autocommit)是作用于整個(gè)數(shù)據(jù)庫(kù)連接會(huì)話(huà)的,主要體現(xiàn)在:
- 新建立的連接默認(rèn)是自動(dòng)提交模式(autocommit=on)。
- 連接的自動(dòng)提交模式作用于該連接下執(zhí)行的所有事務(wù)。不管執(zhí)行多少個(gè)事務(wù),都使用該連接的自動(dòng)提交模式。
- 設(shè)置自動(dòng)提交模式會(huì)改變連接的默認(rèn)提交行為。
舉個(gè)例子:
– 新連接默認(rèn)autocommit=ON
mysql> start transaction; -- 開(kāi)始事務(wù),但繼承連接的自動(dòng)提交模式
mysql> insert into table_a values (1); -- 自動(dòng)提交
mysql> commit; -- 提交無(wú)實(shí)際效果,因?yàn)橐炎詣?dòng)提交
在這個(gè)例子中,由于連接的默認(rèn)autocommit=on,所以不管執(zhí)行多少個(gè)事務(wù),每條SQL語(yǔ)句都會(huì)隱式提交。
SQLAlchemy 的設(shè)計(jì)是為了最大程度兼容不同數(shù)據(jù)庫(kù)的行為,所以從 1.4 版本開(kāi)始采用關(guān)閉自動(dòng)提交作為默認(rèn)值,這更符合 PostgreSQL 和一些其他數(shù)據(jù)庫(kù)的行為。
總結(jié):PostgreSQL的autocommit屬性是針對(duì)每個(gè)事務(wù)的,而不是整個(gè)會(huì)話(huà)。這就要求我們必須顯式地提交或回滾事務(wù)來(lái)結(jié)束一個(gè)事務(wù)。
查看PostgreSQL當(dāng)前連接是否開(kāi)啟自動(dòng)提交
查詢(xún) pg_settings
系統(tǒng)表:
SELECT setting FROM pg_settings WHERE name = 'default_transaction_isolation';
返回 ‘read committed’ 表示未開(kāi)啟。
是否可以直接查詢(xún)PostgreSQL的autocommit狀態(tài),而不通過(guò) default_transaction_isolation 這種間接的方式。
直接查詢(xún)autocommit的參數(shù)在PostgreSQL中是不支持的。因?yàn)閍utocommit實(shí)際上不是一個(gè)配置參數(shù),而是個(gè)概念,是由default_transaction_isolation參數(shù)決定的。
沒(méi)有辦法直接用SHOW或者SELECT方式獲取autocommit的狀態(tài)。
之所以我們常用 default_transaction_isolation 來(lái)判斷,是因?yàn)檫@兩個(gè)設(shè)置在PostgreSQL內(nèi)部是耦合的:
- read committed 表示關(guān)閉了autocommit
- read uncommitted 表示開(kāi)啟了autocommit
所以default_transaction_isolation等于read committed的時(shí)候,就可以確定autocommit是關(guān)閉的。
四、工作遇到的問(wèn)題
1. pg報(bào)錯(cuò):unexpected EOF on client connection
在 PostgreSQL 中,“unexpected EOF on client connection” 這個(gè)錯(cuò)誤通常表示客戶(hù)端應(yīng)用程序異常中斷了與數(shù)據(jù)庫(kù)的連接,導(dǎo)致有一個(gè)未完成的打開(kāi)事務(wù)狀態(tài)。
這種錯(cuò)誤的常見(jiàn)原因包括:
- 應(yīng)用程序進(jìn)程崩潰或異常退出,沒(méi)有正常關(guān)閉數(shù)據(jù)庫(kù)連接。
- 網(wǎng)絡(luò)連接異常中斷。比如網(wǎng)絡(luò)閃斷,客戶(hù)端機(jī)器宕機(jī)等。
- 客戶(hù)端沒(méi)有正確處理查詢(xún)超時(shí)或者服務(wù)器重啟的情況。
- 由于編程錯(cuò)誤或者資源問(wèn)題,客戶(hù)端忘記提交/回滾一個(gè)長(zhǎng)時(shí)間運(yùn)行的事務(wù)。
- 數(shù)據(jù)庫(kù)連接泄露,占用了所有可用連接。
當(dāng)出現(xiàn)這個(gè)錯(cuò)誤時(shí),該未提交的事務(wù)會(huì)一直持有鎖占用連接,阻塞其他事務(wù),因此需要進(jìn)行以下處理:
- 檢查網(wǎng)絡(luò)鏈接情況,確保客戶(hù)端和數(shù)據(jù)庫(kù)服務(wù)器網(wǎng)絡(luò)暢通。
- 檢查客戶(hù)端應(yīng)用程序代碼,確保正確處理超時(shí)、事務(wù)提交等情況。
- 在數(shù)據(jù)庫(kù)端執(zhí)行以下命令,手動(dòng)終止該占用連接的事務(wù):
SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE state = 'idle in transaction';
你執(zhí)行這個(gè)語(yǔ)句后,處在 idle in transaction 狀態(tài)的后端進(jìn)程將被強(qiáng)制終止,相關(guān)的事務(wù)也會(huì)被回滾。
注:pg_terminate_backend() 函數(shù)的返回值 ‘t’ 表示終止后端成功。
終止后端進(jìn)程后,還需要幾個(gè)后續(xù)步驟:
- 再次執(zhí)行同樣的查詢(xún),檢查是否還有處在 idle in transaction 狀態(tài)的進(jìn)程:
SELECT * FROM pg_stat_activity WHERE state = 'idle in transaction';
- 分析日志,找到未提交事務(wù)的根本原因,比如編程錯(cuò)誤、資源問(wèn)題等,從代碼級(jí)別解決問(wèn)題。
- 合理配置數(shù)據(jù)庫(kù)連接池,避免連接泄露。
- 調(diào)整數(shù)據(jù)庫(kù)配置,適當(dāng)增大max_connections數(shù)值。
2. 報(bào)錯(cuò):sqlalchemy.exc.ArgumentError: autocommit=True is no longer supported
早期版本的 SQLAlchemy 支持在 Engine 創(chuàng)建時(shí)設(shè)置 autocommit=True,將該引擎設(shè)置為自動(dòng)提交事務(wù)模式。但是從版本 1.4 開(kāi)始,這個(gè)參數(shù)就不再支持了。
導(dǎo)致這個(gè)錯(cuò)誤的典型代碼如:
engine = create_engine(URL, autocommit=True)
要修復(fù)這個(gè)錯(cuò)誤,需要移除 autocommit 參數(shù),改為手動(dòng)管理事務(wù):文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-727293.html
- 去掉 autocommit=True
engine = create_engine(URL)
- 在代碼中手動(dòng)提交事務(wù):
with engine.begin() as conn:
conn.execute(...)
conn.commit()
- 或者開(kāi)啟自動(dòng)提交模式:
connection = engine.connect()
connection.autocommit = True
總之,Engine 對(duì)象不再支持 autocommit 參數(shù)。需要通過(guò)上述方式自行控制事務(wù)提交。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-727293.html
到了這里,關(guān)于python常用庫(kù)之?dāng)?shù)據(jù)庫(kù)orm框架之SQLAlchemy的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!