[英]Spring JPA query always uses Sequence Scan instead of an Index Scan
我有一個簡單的查詢
@Query(value = "select * from some_table where consumer_id=:consumerId and store_id=:storeId and cancelled_at is null", nativeQuery = true)
fun checkIfNewConsumer(consumerId: BigInteger, storeId: BigInteger): List<SomeClass?>
當我運行查詢時直接對超過3000萬行的表進行解釋
Index Scan using select_index on some_table (cost=0.56..8.59 rows=1 width=86) (actual time=0.015..0.015 rows=0 loops=1) Index Cond: ((consumer_id = 1234) AND (store_id = 4) AND (cancelled_at IS NULL)) Planning time: 0.130 ms Execution time: 0.042 ms
當我使用spring boot通過請求運行相同的查詢時:
{"Plan"=>{"Total Cost"=>1317517.92, "Relation Name"=>"some_table", "Parallel Aware"=>"?", "Filter"=>"?", "Alias"=>"some_table", "Node Type"=>"Seq Scan", "Plan Width"=>86, "Startup Cost"=>0.0, "Plan Rows"=>912}} Execution time: 9613 ms
上面的彈簧靴計划來自新的遺物。 如您所見,它默認為每個查詢的Seq掃描而不是索引掃描 。 假設它是數據庫(沒有骰子),我已經進行了真空分析,我嘗試了查詢的變體,沒有骰子。 它總是在plsql,通過spring的borks看起來很完美。
任何建議都將受到高度贊賞。
編輯2:潛在的解決方案
我們發現通過禁用預處理語句將?preferQueryMode=simple
添加到你的連接url: jdbc:postgresql://localhost:5432/postgres?preferQueryMode=simple
讓查詢使用索引掃描。
我們需要了解如何? 為什么? 為什么現在?
編輯1:技術堆棧
編輯:解決方案@Vlad Mihalcea
請不要使用preferQueryMode = simple,除非你完全確定它意味着什么。 顯然,您的問題在https://gist.github.com/vlsi/df08cbef370b2e86a5c1中有所描述。 我猜你在數據庫中有BigInt,在Kotlin代碼中有BigInteger。 你能在Kotlin使用Long嗎?
-Vladimir Sitnikov
由於PostgreSQL不需要執行任何執行計划緩存,並且PreparedStatement(s)
實際上是模擬的,直到達到給定的執行閾值(例如5),我認為這是你在這里遇到的索引選擇性問題。
如果此查詢僅返回少量記錄,則數據庫將使用該索引。
如果此查詢將返回大量記錄,則數據庫將不使用索引,因為隨機訪問頁讀取的成本將高於順序掃描的成本。
因此,可能是您在此處使用不同的綁定參數值集。
此外,在pgsql上,解釋計划不會考慮將所有記錄發送到JDBC驅動程序的網絡開銷。 但是,這是對您的問題的補充,而不是實際的根本原因。
現在,要確切了解實際的執行計划,請嘗試在PostgreSQL中啟用auto_explain
模式。
或者,您可以編寫一個運行查詢的測試方法,如下所示:
List<Object[]> executionPlanLines = doInJPA(entityManager -> {
try(Stream<Object[]> postStream = entityManager
.createNativeQuery(
"EXPLAIN ANALYZE " +
"select * from some_table where consumer_id=:consumerId and store_id=:storeId and cancelled_at is null ")
.setParameter("consumerId", consumerId)
.setParameter("storeId", storeId)
.unwrap(Query.class)
.stream()
) {
return postStream.collect( Collectors.toList() );
}
});
LOGGER.info( "Execution plan: {}",
executionPlanLines
.stream()
.map( line -> (String) line[0] )
.collect( Collectors.joining( "\n" ) )
);
這樣,您將看到生產中運行的實際執行計划。
請不要使用preferQueryMode=simple
除非您完全確定它的含義(例如,它可能有助於處理邏輯復制流)。
顯然你的問題在https://gist.github.com/vlsi/df08cbef370b2e86a5c1中有描述。 我猜你在數據庫中有bigint
,在Kotlin代碼中有BigInteger
。 你能在Kotlin使用Long
嗎?
以防萬一:PostgreSQL中的bigint
意味着int8
,所以應該在應用程序中使用Long
。
替代選項是添加如下的顯式轉換: consumer_id=cast(:consumerId as bigint) and store_id=cast(:storeId as bigint)
。
問題與“字符列與數值比較”相同,但是,這里的差異有點微妙(int8與數字相比)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.