如何修復使用 Python ORM 工具 SQLAlchemy 時的常見陷阱 | Linux 中國

2021-12-31 Linux中國

對象關係映射Object-relational mapping(ORM)使應用程式開發人員的工作更輕鬆,在很大程度是因為它允許你使用你可能知道的語言(例如 Python)與資料庫交互,而不是使用原始 SQL 語句查詢。SQLAlchemy 是一個 Python ORM 工具包,它提供使用 Python 訪問 SQL 資料庫的功能。它是一個成熟的 ORM 工具,增加了模型關係、強大的查詢構造範式、簡單的序列化等優點。然而,它的易用性使得人們很容易忘記其背後發生了什麼。使用 SQLAlchemy 時做出的看似很小的選擇可能產生非常大的性能影響。

本文解釋了開發人員在使用 SQLAlchemy 時遇到的一些最重要的性能問題,以及如何解決這些問題。

只需要計數但檢索整個結果集

有時開發人員只需要一個結果計數,但是沒有使用資料庫計數功能,而是獲取了所有結果,然後使用 Python 中的 len 完成計數。

count = len(User.query.filter_by(acct_active=True).all())

相反,使用 SQLAlchemy 的 count 方法將在伺服器端執行計數,從而減少發送到客戶端的數據。在前面的例子中調用 all() 也會導致模型對象的實例化,如果有很多數據,那麼時間代價可能會非常昂貴。

除非還需要做其他的事情,否則只需使用 count 方法:

count = User.query.filter_by(acct_active=True).count()

只需要幾列時檢索整個模型

在許多情況下,發出查詢時只需要幾列數據。SQLAlchemy 可以只獲取你想要的列,而不是返回整個模型實例。這不僅減少了發送的數據量,還避免了實例化整個對象。使用列數據的元組而不是模型可以快得多。

result = User.query.all()    print(user.name, user.email)

反之,使用 with_entities 方法只選擇所需要的內容:

result = User.query.with_entities(User.name, User.email).all()for (username, email) in result:

每次循環都更新一個對象

避免使用循環來單獨更新集合。雖然資料庫可以非常快地執行單個更新,但應用程式和資料庫伺服器之間的往返時間將快速累加。通常,在合理的情況下爭取更少的查詢。

for user in users_to_update:

改用批量更新方法:

query = User.query.filter(user.id.in_([user.id for user in users_to_update]))query.update({"acct_active": True}, synchronize_session=False)

觸發級聯刪除

ORM 允許在模型關係上進行簡單的配置,但是有一些微妙的行為可能會令人吃驚。大多數資料庫通過外鍵和各種級聯選項維護關係完整性。SQLAlchemy 允許你使用外鍵和級聯選項定義模型,但是 ORM 具有自己的級聯邏輯,可以取代資料庫。

考慮以下模型:

    id = Column(Integer, primary_key=True)    songs = relationship("Song", cascade="all, delete")    id = Column(Integer, primary_key=True)    artist_id = Column(Integer, ForeignKey("artist.id", ondelete="CASCADE"))

刪除歌手將導致 ORM 在 song 表上發出 delete 查詢,從而防止由於外鍵導致的刪除操作。這種行為可能會成為複雜關係和大量記錄的瓶頸。

請包含 passive_deletes 選項,以確保讓資料庫來管理關係。但是,請確保你的資料庫具有此功能。例如,SQLite 默認情況下不管理外鍵。

songs = relationship("Song", cascade all, delete", passive_deletes=True)

當要使用貪婪加載時,應使用延遲加載

延遲加載是 SQLAlchemy 處理關係的默認方法。從上一個例子構建來看,加載一個歌手時不會同時加載他或她的歌曲。這通常是一個好主意,但是如果總是需要加載某些關係,單獨的查詢可能會造成浪費。

如果允許以延遲方式加載關係,像 Marshmallow 這樣流行的序列化框架可以觸發級聯查詢。

有幾種方法可以控制此行為。最簡單的方法是通過 relationship 函數本身。

songs = relationship("Song", lazy="joined", cascade="all, delete")

這將導致一個左連接被添加到任何歌手的查詢中,因此,songs 集合將立即可用。儘管有更多數據返回給客戶端,但往返次數可能會少得多。

SQLAlchemy 為無法採用這種綜合方法的情況提供了更細粒度的控制,可以使用 joinedload() 函數在每個查詢的基礎上切換連接的加載。

from sqlalchemy.orm import joinedloadartists = Artist.query.options(joinedload(Artist.songs))print(artists.songs) # Does not incur a roundtrip to load

使用 ORM 進行批量記錄導入

導入成千上萬條記錄時,構建完整模型實例的開銷會成為主要瓶頸。想像一下,從一個文件中加載數千首歌曲記錄,其中每首歌曲都先被轉換為字典。

    db.session.add(Song(`song))

相反,繞過 ORM,只使用核心的 SQLAlchemy 參數綁定功能。

insert_stmt = Song.__table__.insert()       db.session.execute(insert_stmt, batch)    db.session.execute(insert_stmt, batch)

請記住,此方法會自然而然地跳過你可能依賴的任何客戶端 ORM 邏輯,例如基於 Python 的列默認值。儘管此方法比將對象加載為完整的模型實例要快,但是你的資料庫可能具有更快的批量加載方法。例如,PostgreSQL 的 COPY 命令為加載大量記錄提供了最佳性能。

過早調用提交或刷新

在很多情況下,你需要將子記錄與其父記錄相關聯,反之亦然。一種顯然的方法是刷新會話,以便為有問題的記錄分配一個 ID。

artist = Artist(name="Bob Dylan")song = Song(title="Mr. Tambourine Man")song.artist_id = artist.id

對於每個請求,多次提交或刷新通常是不必要的,也是不可取的。資料庫刷新涉及強制在資料庫伺服器上進行磁碟寫入,在大多數情況下,客戶端將阻塞,直到伺服器確認已寫入數據為止。

SQLAlchemy 可以在幕後跟蹤關係和管理相關鍵。

artist = Artist(name="Bob Dylan")song = Song(title="Mr. Tambourine Man")artist.songs.append(song)

總結

我希望這一系列常見的陷阱可以幫助你避免這些問題,並使你的應用平穩運行。通常,在診斷性能問題時,測量是關鍵。大多數資料庫都提供性能診斷功能,可以幫助你定位問題,例如 PostgreSQL 的 pg_stat_statements 模塊。

via: https://opensource.com/article/19/9/common-pitfalls-python

作者:Zach Todd 選題:lujun9972 譯者:MjSeven 校對:wxy

本文由 LCTT 原創編譯,Linux中國 榮譽推出

相關焦點

  • SQLAlchemy 1.3.8 發布,Python ORM 框架
    SQLAlchemy 是一個 Python 的 SQL 工具包以及資料庫對象映射(ORM)框架。它包含整套企業級持久化模式,專門用於高效和高性能的資料庫訪問。新版本包含對新的 psycopg2 「執行值」性能的修復,以適應由編譯器掛鈎修改的 INSERT 語句。
  • Python資料庫ORM工具sqlalchemy的學習筆記
    SQLAlchemy是python的一個資料庫ORM工具,提供了強大的對象模型間的轉換,可以滿足絕大多數資料庫操作的需求,並且支持多種資料庫引擎(sqlite,mysql,postgres, mongodb等),在這裡記錄基本用法和學習筆記
  • 用 Python 連接 MySQL 的幾種姿勢
    之禪(微信ID:vttalk)儘管很多 NoSQL 資料庫近幾年大放異彩,但是像 MySQL 這樣的關係型資料庫依然是網際網路的主流資料庫之一,每個學 Python 的都有必要學好一門資料庫,不管你是做數據分析,還是網絡爬蟲,Web 開發、亦或是機器學習,你都離不開要和資料庫打交道,而 MySQL 又是最流行的一種資料庫,這篇文章介紹 Python 操作 MySQL 的幾種方式,你可以在實際開發過程中根據實際情況合理選擇
  • SQLAlchemy連接與搭建
    上一節分享了PyMySQL連接MySQL資料庫的操作示例,但是是通過編寫SQL語句的方式,這一節使用python中的SQLAlchemy包,直接通過代碼的方式操作
  • Python數據科學實踐 | 資料庫1
    資料庫永遠是數據管理上最值得使用的工具。而把所收集的大量數據放入資料庫之後再處理是數據科學實踐項目中必不可少的一步。通過前面章節的學習,我們已經掌握了利用Python分析數據的眾多模塊,特別地也展示了如何利用爬蟲技術爬取本書中最常使用的火鍋團購數據的全過程。本章假設的分析場景是火鍋團購數據被爬取後,由於數據量過大已經進入資料庫保存。
  • SQLAlchemy介紹
    SQLAlchemy相比原生的SQL如何?但如果你使用Flask,Bottle或其他web框架來編寫web程序時最好看下以下擴展。它們提供了一些代碼以及一些幫助工具可以減少程序使用SQLAlchemy庫時所編寫的代碼量。
  • Python黑帽編程1.3 Python運行時與包管理工具
    Python黑帽編程1.3  Python運行時與包管理工具 0.1  本系列教程說明本系列教程,採用的大綱母本為《Understanding
  • python web學習路線知識點分享!
    如果你想做python web相關的東西,下邊這些東西可以參考學習:基礎:linux命令,計算機網絡,pythonmarkdown/sphinx/docstring/readthedoc等(使用vim的話推薦python-mode插件,或者直接用IDE工具pycharm)相關框架(庫):django/flask/tornado/requests/sqlalchemy/unittest/celery等等資料庫:
  • Python 學習路線圖
    •《Python3 網絡爬蟲實戰》Linux系統大部分Python應用都是跑在Linux伺服器上的,大部分開源伺服器軟體使用的也是linux系統,即使日常工作不使用linux,一些基本的linux命令也要了解。 比如常用的文件操作,目錄操作,進程操作等。
  • sqlalchemy操作資料庫(二)
    sqlalchemy的基本操作表結構如下:from sqlalchemy import create_enginefrom
  • SQLAlchemy 1.2.16 發布,Python ORM 框架
    SQLAlchemy 1.2.16 發布了,SQLAlchemy 是一個 Python 的 SQL 工具包以及資料庫對象關係映射框架
  • 315道Python面試題,歡迎挑戰!
    55、如何生成一個隨機數?56、如何使用python刪除一個文件?57、談談你對面向對象的理解?58、Python面向對象中的繼承有什麼特點?59、面向對象深度優先和廣度優先是什麼?60、面向對象中super的作用?61、是否使用過functools中的函數?其作用是什麼?
  • 7k Star 的 Python 測試框架入門指南
    默認的prepend模式對於只使用一個特定Python版本測試應用程式並且所有測試都放在tests/文件夾中的常見場景來說已經足夠了。為了避免名稱衝突,我們只需要通過添加一個__init__.py文件將內部測試文件夾升級為模塊即可。
  • 【附答案】300+ 道 2020年 Python面試題 分享
    55、如何生成一個隨機數?56、如何使用python刪除一個文件?57、談談你對面向對象的理解?58、Python面向對象中的繼承有什麼特點?59、面向對象深度優先和廣度優先是什麼?60、面向對象中super的作用?61、是否使用過functools中的函數?其作用是什麼?
  • 不吹不擂,你想要的Python面試都在這裡了【315+道題】
    55、如何生成一個隨機數?56、如何使用python刪除一個文件?57、談談你對面向對象的理解?58、Python面向對象中的繼承有什麼特點?59、面向對象深度優先和廣度優先是什麼?60、面向對象中super的作用?61、是否使用過functools中的函數?其作用是什麼?
  • 如何在使用 Vim 時訪問/查看 Python 幫助 | Linux 中國
    如何在 vim 中顯示 python help() ?如何在不離開 vim 的情況下調用 pydoc3/pydoc 尋求幫助?pydoc 或 pydoc3 命令可以根據 Python 關鍵字、主題、函數、模塊或包的名稱顯示文本文檔,或在模塊內或包中的模塊對類或函數的引用。你可以從 Vim 中調用 pydoc。
  • Linux 平臺下 Python 腳本編程入門(一)
    在 Linux 中學習 Python 腳本編程首先,我們會使用 Python 的命令行工具,還會接觸到 Python 的面向對象特性(這篇文章的後半部分會談到它)。學習 Python 可以助力於你在桌面應用開發[1]及數據科學領域[2]的職業發展。
  • Python最佳經典學習路線
    如何學習Python python語言基礎:(帶你熟悉python語言的特性,學會使用python開發環境,使用python開發一些簡單的案例) (1)Python3入門,數據類型,字符串 (2)判斷/循環語句,函數,
  • Python打包工具--Pyinstaller詳細介紹
    然後我上網搜了一下python,當看到python之父那茂密的頭髮時,更堅定了我學習python的信念。由於我這人沉不住氣,一分鐘學習了python安裝+print輸出,就急著去網上搜索如何打包寫好的代碼,轉化為exe文件....順著這條主線安裝了pip,公司內外網隔離
  • Python 資料庫騷操作 -- MySQL
    工具MySQL 遇上 Docker增刪改查一對多一對一多對多後記前言今天這篇是三大資料庫的結尾篇,前面兩篇分別是:《Python 資料庫騷操作 -- MongoDB》《Python 資料庫騷操作 -- Redis》,這篇主要介紹 MySQL 的 orm 庫 SQLAlchemy 。