表字段加了索引但是查詢依然很慢

2021-09-30 17:59:59 字數 1627 閱讀 1484

之前遇到乙個問題(因為最近又遇到了,所以記錄下來),表字段加了索引但是查詢依然很慢,大概的情況如下

有個表t_order(匿名),字段若干,其中有個tx_time交易時間,merchant_no商戶號都已經新增了索引,表資料量較大5000w+

sql大致如下(實際sql比下面的複雜一點點,會做 left join 其他表,但是不多):

select * from t_order 

where

customer_no = #

and tx_time between # and #

查詢一天某商戶的資料經常超時(好幾秒,具體的忘了)。

1、首先通過debug日誌將列印出來的sql與引數拼寫成完整sql在資料庫執行,發現很快(幾十毫秒),並不是sql本身問題(其實sql本身也有一定的問題,比如表資料太大,又進行left join ,但因其不是這次分析重點,就此忽略)。

2、mybatis的#會將sql引數化,而在步驟1中的sql是具體引數的,非引數化的, 所以懷疑引數化的sql與非引數化的sql執行計畫不一樣,於是找了dba去幫忙查兩sql的執行計畫(因為本人不擅長資料庫方面的東西,並且身邊有資深dba,所以就直接請教他們了),果然不一樣。

3、於是將mybatis中該sql的#全部替換為了$,部署發現問題解決了,之前部分sql慢的問題未出現了。

1、首先說明一下,mybatis中的#與$差別,#{}的引數是引數化的,也就是在sql中是?替代的;${}的引數是拼接的,也就是在sql中直接拼接上了引數的值,所以在替換成$後,sql就與直接在資料庫執行的一致了。

2、為啥會變快呢?在dba的協助下,發現引數化的sql在資料庫的編譯會存在乙個問題,就是編譯sql的時候,會依據當前全表的量基於一定的演算法生成執行計畫,然後將執行計畫快取起來;而拼接好的sql則會基於當前拼接的多個引數實際篩選的量基於演算法生成執行計畫,然後快取起來。也就是說第二種的執行計畫是動態的,每次有可能不一樣,都是最優的。(具體的執行計畫生成演算法不是很清楚,也不是這裡的重點)

舉個例子:

t_order中,a商戶每天可產生2500w資料,而剩下2500w條交易分別資料2500w個商戶的,每個商戶一條。

若引數化,那麼執行計畫可能是,執行計畫1,優先走時間索引,再走商戶索引(或者是執行計畫2優先走商戶索引,再走時間索引,只能是兩者的其中乙個,然後快取下來,以後都走這個快取的)。

若執行計畫1,那麼查詢a商戶一天的交易就比較快,查詢剩餘某個商戶就比較慢

若執行計畫2,那麼查詢a商戶就比較慢,查詢剩餘某個商戶的就比較快

總會有不好的地方

若拼接的話,那麼情況就會變了

查詢a商戶一天交易的時候,執行計畫就會是 優先走時間索引,再走商戶索引

查詢剩餘某個商戶一天交易的時候,執行計畫就會是 優先走商戶索引,再走時間索引

1、mybatis中,高頻簡單的sql,引數化#會更好(因為編譯耗時占用sql執行耗時比例很大,幾十倍吧),大表複雜sql可以嘗試使用$拼接,會快很多(編譯耗時占用sql執行耗時的比例很小,每次編譯也就無所謂了,但可以使執行計畫更準確,但是小心sql注入)

2、資料庫上有個繫結變數窺視的設定,開啟後用#就跟用$的執行計畫一致了,這塊參考了別人的文章(

3、引數隱式轉化也影響索引生效,具體可以看這篇,執行計畫的東西可以學習一下(

MySQL表字段加索引

新增普通索引 此時key型別為mul alter table table name add index column column 例如 alter table poicity add index delete flag delete flag 新增主鍵索引 primary key alter ta...

查詢oracle表的資訊(表,字段,約束,索引)

通過搜尋摸索,總結了一下oracle中查詢表的資訊,包括表名,欄位名,字段型別,主鍵,外來鍵唯一性約束資訊,索引資訊查詢sql如下,希望對大家有所幫助 1 查詢出所有的使用者表 select from user tables 可以查詢出所有的使用者表 2 查詢出使用者所有表的索引 select fr...

oracle查詢表的資訊(表,字段,約束,索引)

查詢oracle表的資訊 表,字段,約束,索引 通過搜尋摸索,總結了一下oracle中查詢表的資訊,包括表名,欄位名,字段型別,主鍵,外來鍵唯一性約束資訊,索引資訊查詢sql如下,希望對大家有所幫助 1 查詢出所有的使用者表 select from user tables 可以查詢出所有的使用者表 ...