日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

SQLAlchemy in 查询空列表问题分析

發布時間:2023/12/15 数据库 19 豆豆
生活随笔 收集整理的這篇文章主要介紹了 SQLAlchemy in 查询空列表问题分析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

問題場景

有model Account,SQLAlchemy 查詢語句如下:

query = Account.query.filter(Account.id.in_(account_ids)).order_by(Account.date_created.desc()) 復制代碼

這里 uids 如果為空,執行查詢會有如下警告:

/usr/local/lib/python2.7/site-packages/sqlalchemy/sql/default_comparator.py:35: SAWarning: The IN-predicate on "account.id" was invoked with an empty sequence. This results in a contradiction, which nonetheless can be expensive to evaluate. Consider alternative strategies for improved performance.return o[0](self, self.expr, op, *(other + o[1:]), **kwargs) 復制代碼

這里的意思是使用一個空的列表會花費較長的時間,需要優化以提高性能。

為什么會有這個提示呢?一個空列表為什么會影響性能呢?

首先打印 query 可得到如下 sql 語句:

SELECT * // 字段使用 “*” 代替 FROM account WHERE account.id != account.id ORDER BY account.date_created DESC 復制代碼

會發現生成的語句中過濾條件是 WHERE account.id != account.id,使用 PostgreSQL Explain ANALYZE 命令,

  • EXPLAIN:顯示PostgreSQL計劃程序為提供的語句生成的執行計劃。
  • ANALYZE:收集有關數據庫中表的內容的統計信息。

分析查詢成本結果如下:

postgres=> EXPLAIN ANALYZE SELECT * FROM account WHERE account.id != account.id ORDER BY account.date_created DESC;QUERY PLAN ----------------------------------------------------------------------------------Sort (cost=797159.14..808338.40 rows=4471702 width=29) (actual time=574.002..574.002 rows=0 loops=1)Sort Key: date_created DESCSort Method: quicksort Memory: 25kB-> Seq Scan on account (cost=0.00..89223.16 rows=4471702 width=29) (actual time=573.991..573.991 rows=0 loops=1)Filter: (id <> id)Rows Removed by Filter: 4494173Planning time: 0.162 msExecution time: 574.052 ms (8 rows) 復制代碼

先看Postgresql提供的語句生成的執行計劃,通過結果可以看到,雖然返回值為空,但是查詢成本卻還是特別高,執行計劃部分幾乎所有的時間都耗費在排序上,但是和執行時間相比,查詢計劃的時間可以忽略不計。(結果是先遍歷全表,查出所有數據,然后再使用 Filter: (id <> id) 把所有數據過濾。)

按照這個思路,有兩種查詢方案:

  • 如果 account_ids 為空,那么直接返回空列表不進行任何操作,查詢語句變為:
  • if account_ids:query = Account.query.filter(Account.id.in_(account_ids)).order_by(Account.date_created.desc()) 復制代碼
  • 如果 account_ids 為空,那么過濾方式,查詢語句變為:
  • query = Account.query if account_ids:query = query.filter(Account.id.in_(account_ids)) else:query = query.filter(False)query = query.order_by(Account.date_created.desc()) 復制代碼

    如果 account_ids 為空,此時生成的 SQL 語句結果為:

    SELECT * FROM account WHERE 0 = 1 ORDER BY account.date_created DESC 復制代碼

    分析結果為:

    postgres=> EXPLAIN ANALYZE SELECT * FROM account WHERE 0 = 1 ORDER BY account.date_created DESC;QUERY PLAN ---------------------------------------------------------------------------------------------------Sort (cost=77987.74..77987.75 rows=1 width=29) (actual time=0.011..0.011 rows=0 loops=1)Sort Key: date_created DESCSort Method: quicksort Memory: 25kB-> Result (cost=0.00..77987.73 rows=1 width=29) (actual time=0.001..0.001 rows=0 loops=1)One-Time Filter: false-> Seq Scan on account (cost=0.00..77987.73 rows=1 width=29) (never executed)Planning time: 0.197 msExecution time: 0.061 ms (8 rows) 復制代碼

    可以看到,查詢計劃和執行時間都有大幅提高。

    一個測試

    如果只是去掉方案1排序,查看一下分析結果

    使用 PostgreSQL Explain ANALYZE 命令分析查詢成本結果如下:

    postgres=> EXPLAIN ANALYZE SELECT * FROM account WHERE account.id != account.id;QUERY PLAN ----------------------------------------------------------------------------Seq Scan on account (cost=0.00..89223.16 rows=4471702 width=29) (actual time=550.999..550.999 rows=0 loops=1)Filter: (id <> id)Rows Removed by Filter: 4494173Planning time: 0.134 msExecution time: 551.041 ms 復制代碼

    可以看到,時間和有排序時差別不大。

    如何計算查詢成本

    執行一個分析,結果如下:

    postgres=> explain select * from account where date_created ='2016-04-07 18:51:30.371495+08';QUERY PLAN --------------------------------------------------------------------------------------Seq Scan on account (cost=0.00..127716.33 rows=1 width=211)Filter: (date_created = '2016-04-07 18:51:30.371495+08'::timestamp with time zone) (2 rows) 復制代碼

    EXPLAIN引用的數據是:

  • 0.00 預計的啟動開銷(在輸出掃描開始之前消耗的時間,比如在一個排序節點里做排續的時間)。
  • 127716.33 預計的總開銷。
  • 1 預計的該規劃節點輸出的行數。
  • 211 預計的該規劃節點的行平均寬度(單位:字節)。
  • 這里開銷(cost)的計算單位是磁盤頁面的存取數量,如1.0將表示一次順序的磁盤頁面讀取。其中上層節點的開銷將包括其所有子節點的開銷。這里的輸出行數(rows)并不是規劃節點處理/掃描的行數,通常會更少一些。一般而言,頂層的行預計數量會更接近于查詢實際返回的行數。 這里表示的就是在只有單 CPU 內核的情況下,評估成本是127716.33;

    計算成本,Postgresql 首先看表的字節數大小

    這里 account 表的大小為:

    postgres=> select pg_relation_size('account');pg_relation_size ------------------737673216 (1 row) 復制代碼

    查看塊的大小

    Postgresql 會為每個要一次讀取的快添加成本點,使用 show block_size查看塊的大小:

    postgres=> show block_size;block_size ------------8192 (1 row) 復制代碼

    計算塊的個數

    可以看到每個塊的大小為8kb,那么可以計算從表從讀取的順序塊成本值為:

    blocks = pg_relation_size/block_size = 90048 復制代碼

    90048 是account 表所占用塊的數量。

    查看每個塊需要的成本

    postgres=> show seq_page_cost;seq_page_cost ---------------1 (1 row) 復制代碼

    這里的意思是 Postgresql 為每個塊分配一個成本點,也就是說上面的查詢需要從90048個成本點。

    處理每條數據 cpu 所需時間

    • cpu_tuple_cost:處理每條記錄的CPU開銷(tuple:關系中的一行記錄)
    • cpu_operator_cost:操作符或函數帶來的CPU開銷。
    postgres=> show cpu_operator_cost;cpu_operator_cost -------------------0.0025 (1 row)postgres=> show cpu_tuple_cost;cpu_tuple_cost ----------------0.01 (1 row) 復制代碼

    計算

    cost 計算公式為:

    cost = 磁盤塊個數 * 塊成本(1) + 行數 * cpu_tuple_cost(系統參數值)+ 行數 * cpu_operator_cost

    現在用所有值來計算explain 語句中得到的值:

    number_of_records = 3013466 # account 表 countblock_size = 8192 # block size in bytespg_relation_size=737673216blocks = pg_relation_size/block_size = 90048seq_page_cost = 1 cpu_tuple_cost = 0.01 cpu_operator_cost = 0.0025cost = blocks * seq_page_cost + number_of_records * cpu_tuple_cost + number_of_records * cpu_operator_cost 復制代碼

    如何降低查詢成本?

    直接回答,使用索引。

    postgres=> explain select * from account where id=20039;QUERY PLAN ----------------------------------------------------------------------------------------Index Scan using account_pkey on account (cost=0.43..8.45 rows=1 width=211)Index Cond: (id = 20039) (2 rows) 復制代碼

    通過這個查詢可以看到,在使用有索引的字段查詢時,查詢成本顯著降低。

    索引掃描的計算比順序掃描的計算要復雜一些。它由兩個階段組成。 PostgreSQL會考慮random_page_cost和cpu_index_tuple_cost 變量,并返回一個基于索引樹的高度的值。

    參考鏈接

    • sqlalchemy-and-empty-in-clause
    • PostgreSQL查詢性能分析和優化
    • PostgreSQL學習手冊(性能提升技巧)
    • PostgreSQL 查詢成本模型
    • PostgreSQL 查詢計劃時間的計算詳解

    總結

    以上是生活随笔為你收集整理的SQLAlchemy in 查询空列表问题分析的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。