震惊!阿里的程序员竟被一个简单的 SQL 查询难住了!
作者 | 唐磊
責編 | Carol
來源 | 程序猿石頭
封圖 |?CSDN 付費下載于視覺中國
最近工作上遇到一個”神奇”的問題,或許對大家有幫助,因此形成本文。
問題大概是,我有兩個表 TableA,TableB,其中 TableA 表大概百萬行級別(存量業務數據),TableB 表幾行(新業務場景, 數據還未膨脹起來),語義上 ?TableA.columnA = TableB.columnA,其中?columnA?上建立了索引,但查詢的時候確巨慢無比, 基本上到5-6 秒,明顯跟預期不符合。
下面我以一個具體的例子來說明吧,模擬其中的 SQL 查詢場景、
場景重現
user_info?表, 為了場景盡量簡單, 我只 mock 了其中的三列數據。
user_score?表,其中?uid?和?user_info.uid?語義一致。
其中數據情況如下,都是很常見的場景。
索引情況是
查詢業務場景: 已知?user_score.id, 需要關聯查詢對應user_info的信息, (大家先忽略這個具體業務場景是否合理哈)。那么對應的 SQL 很自然的如下:
請忽略其中的數據,我剛開始 mock 了 100W,然后又重復導入了兩遍, 因此數據有一些重復。300W 數據, 最后查詢出來也是 1.18 秒,按道理應該更快的。老規矩?explain?看看啥情況?
發現?user_info表沒用上索引, 全表掃描近 300W 數據? 現象是這樣, 為什么呢?
你不妨思考一下, 如果你遇到這種場景, 應該怎么去排查?
(分割線, 花 10 秒想想?)
我當時也是”一頓操作猛如虎”,然并卵? 嘗試了什么多種 sql 寫法來完成這個操作,比如更換Join表的順序(驅動表/被驅動表),?再比如用子查詢。最終,還是沒有結果。但直接單表查詢寫 SQL 確能用上索引。
問題解決
嘗試更換檢索條件,比如更換 uid 直接關聯查詢,索引仍然用不上, 差點放棄了都。在準備求助 DBA 前, 看了下表的建表語句。
完全有理由懷疑因為字符集不一致的問題導致索引失效的問題了。
于是修改了小表(真實線上環境可別亂操作)的字符集與大表一致, 再測試下。
果然 work 了。
挖掘根因
其實深究原因,就是網上各種 MySQL軍規/規約所提到的, “索引列不要參與計算”。?這次這個 case,,如果知道?explain extended + show warnings?這個工具的話,(以前都不知道explain后面還能加?extended?參數),?可能就盡早”恍然大悟”了。(最新的 MySQL 8.0版本貌似不需要另外加這個關鍵字)。
看下效果。(啊, 我還得把字符集改回去!!!)
mysql> explain extended select * from user_score us inner join user_info ui on us.uid = ui.uid where us.id = 5; +----+-------------+-------+-------+-------------------+---------+---------+-------+---------+----------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+-------+-------------------+---------+---------+-------+---------+----------+-------------+ | 1 | SIMPLE | us | const | PRIMARY,index_uid | PRIMARY | 4 | const | 1 | 100.00 | NULL | | 1 | SIMPLE | ui | ALL | NULL | NULL | NULL | NULL | 2989934 | 100.00 | Using where | +----+-------------+-------+-------+-------------------+---------+---------+-------+---------+----------+-------------+ 2 rows in set, 1 warning (0.00 sec) mysql> show warnings; +-------+------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Level | Code | Message | +-------+------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Note | 1003 | /* select#1 */ select '5' AS `id`,'111111111' AS `uid`,'100' AS `score`,`test`.`ui`.`id` AS `id`,`test`.`ui`.`uid` AS `uid`,`test`.`ui`.`name` AS `name` from `test`.`user_score` `us` join `test`.`user_info` `ui` where (('111111111' = convert(`test`.`ui`.`uid` using utf8mb4))) | +-------+------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ 1 row in set (0.00 sec)(滑動看右邊)
索引列參與計算了,每次都要根據字符集去轉換, 全表掃描,你說能快得起來么?
至于這個問題為什么會發生? 綜合來看, 就是因為歷史原因,老業務場景中的原表是假?utf8, 新業務新表采用了真?utf8mb4。
考慮新表的時候, 忽略和原庫字符集的比較. 其實, 發現庫里面的不同表可能都有不同的字符集, 不同人建的時候可能都依據個人喜好去選擇了不同的字符集. 由此可見,?開發規范有多重要.
雖然知道索引列不能參與計算, 但這個場景下都是相同的類型, ?varchar(64)?最終查詢過程中仍然發生了類型轉換. 因此需要把字段字符集不一致等同于字段類型不一致.
如果這個 case, 利用?fail-fast?的理念的話, 發現不一致, 直接不讓 join 會不會更好? (就像?char v.s varchar?不能 join 一樣).
說明:?本文測試場景基于 MySQL 5.6,?另外,?本文案例只是為了說明問題,?其中的 SQL 并不規范(例如盡量別用 select * 之類的), 請勿模仿(模仿了我也不負責). ?為了寫本文, 可花了不少時間, 建 DB, mock數據, 包括排版公眾號(啊,公眾號后臺對代碼格式還是不友好, markdown 轉來代碼格式還是有問題)等等, 如果覺得有用, 還望你幫忙"在看", "轉發". 最后留一個思考題供討論, 歡迎留言說出你的看法。
留一道思考題
你能解釋如下情況嗎? 查詢結果表現為何不一致???注意一下 SQL 的執行順序, 查詢優化器工作流程,以及其中的?Using join buffer (Block Nested Loop), 可以多看看 [MySQL 官方手冊](https://dev.mysql.com/doc/refman/5.6/en/) 深入了解背后的過程和原理。
作者簡介
唐磊,碼農@阿里云,碩士畢業于清華大學,曾工作于大疆,宜信大數據創新中心,Tencent和友盟。歡迎關注,多多交流多多指教????
?
推薦閱讀
手把手教你配置VS Code 遠程開發工具,工作效率提升N倍
用大白話徹底搞懂 HBase RowKey 詳細設計
后端程序員必備:書寫高質量SQL的30條建議
Go 遠超 Python,機器學習人才極度稀缺,全球 16,655 位程序員告訴你這些真相!
任正非談“狼文化”:華為沒有 996,更沒有 007
區塊鏈必讀“上鏈”哲學:“胖鏈下”與“瘦鏈上”
在商業中,如何與人工智能建立共生關系?
真香,朕在看了!
總結
以上是生活随笔為你收集整理的震惊!阿里的程序员竟被一个简单的 SQL 查询难住了!的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 今天,Python信息量很大!
- 下一篇: 云+X案例展 | 金融类:七牛云Pand