友情連結:http://enmotech.com/web/classify/43.html
http://enmotech.com/web/classify/28.html
更多精彩:請關注“資料和雲”公衆号。
事件背景:
SELECT * FROM girls WHERE age BETWEEN 18 and 24 and boyfriend='no';
上周在朋友圈看到一張照片,随手轉發并且提出了一個問題。
面試題一枚可好:請問以下SQL有什麼可能的邏輯問題、文法問題、性能問題,可以怎樣進行索引優化、邏輯優化以提升性能?
這個問題在朋友圈引起了很多朋友的興趣,轉發并且提問,希望有一個标準答案輸出作為參考。
标準答案沒有,但是我可以說說我的看法,當然前提是『這個SQL中的一切都是可以質疑的』。管中窺豹,博方家一笑。
開發規範
首先從開發規範上來講,『SELECT *』一般不是最佳實踐,因為你不清楚這個表中有多少個字段(Column),這樣的輸出是無法格式化和預期的,其輸出結果可能也不全部是你需要的,是以盡量明确定義你需要的字段名。
那麼第二個問題來了,『SELECT *』和 『SELECT col1,col2 ..』,除了語義上,還有什麼差別?
你要知道的是,當做出這樣的改寫,這條SQL的長度增加,SQLID改變,占用的Shared Pool共享記憶體也較以前增加了。進行資料字典的列驗證增多,消耗也會增加,也就是CPU占用會增加(當然這可能是微微的改變);
其次,SQL語句中的,age 和 boyfriend 字段查詢,都沒有使用綁定變量,這對于不同查詢,如 between 20 and 24,無法共享SQL,會進一步導緻硬解析上的高CPU消耗,更多的Shared Pool共享記憶體的占用;
再次,當 age 和 boyfriend 查詢不适用綁定變量時,可能為資料庫帶來安全注入的風險;
最後,根據代碼格式規範,BETWEEN 之後的 and 也應該以大寫 AND 形式出現。
這樣的開發規範有助于大家改善代碼品質,提高效率,規避風險,可是如果都靠DBA手工去做,顯然難度太大,雲和恩墨的SQM - SQL品質管控平台,可以自動的幫助我們稽核SQL,管控SQL規範,詳情請參閱:SQL稽核,提升開發規範和性能。
資料結構和中繼資料
我們再從資料結構、資料模型和中繼資料的角度來思考一下,我相信大家見識過各種各樣的資料結構命名法,我見過的遠遠超出想象,各種各樣詭異的命名會坑到我們懷疑人生。
是以,對于以上的SQL查詢,你一定要确定 girls 這個表名的真實定義,是否和你想想的相符合,難道你就笃定這裡是『對面的女孩』大學營?
其次,很少有資料庫裡存在 age 字段的設計,因為那會是一個不斷改變的事實,更合理的做法是存儲『出生日期』,這是基本資料模型設計的守則。
再次,boyfriend 字段,你需要确認這裡存儲的确定是你期望的含義『男朋友』,如果是,并且存儲的資料是 Yes | No,如果你不确定其大小寫和格式規範,是應該用 upper 或者 lower 去對一端進行轉換,否則你的查詢可能無法順利比對;
最後,為什麼我們戶籍重要的登記資訊是『婚姻狀況』?因為 boyfriend 的狀态可能極不穩定,也可能不是1:1的關系,更加該字段需要頻繁變更,可能很難以維持一緻性。
并且,如果這個字段存在,應該以 0 | 1 辨別,可以極大減少存儲的占用,并且提高查詢效率。
當然,很多朋友說,應當去掉 boyfriend 這個限定條件,因為一旦加上這個限制,你可能面對的情況是 no rows return 的尴尬局面。
關于這個條件,有朋友給了這樣幾個建議:
- 去掉 boyfriend ='no' ,你懂的 (這位是老司機)
- boyfriend 的名字叫 no 就尴尬了(這個直追 Null 那個梗)
- 有朋友還要加幾個條件 and cost < 2000 RMB and beautiful > 85分 and height > 165 order by cost,beautiful desc,height desc (我不得不表示,孩子,你還是嫩啊);
- 用 boyfriend is NULL (這是技術流)
那麼如何去檢查核定資料字典的資訊,如何確定SQL的性能,及時發現和解決問題?雲和恩墨的自動化巡檢診斷平台 - Bethune 正可以幫助你自動發現資料庫中安全隐患,參考:免費的白求恩自動巡檢平台助你資料庫平安。
關于性能
我們再來讨論一下性能。
首先,在這個SQL中,Between AND 可以進行優化改寫,between 18 and 24,最簡單的有兩種改寫方法:
- 将 between and 改寫成 >= 和 <= ,這會減少Oracle自己的轉換,同時減少了SQL字元長度,縮減了網絡傳輸,Shared Pool占用;
- 可以将 between and 改寫成 IN (18,19 .. 24 ),資料庫會将 In 值清單轉換成幾個等值比較,然後 CONCATENATION,其成本通常更低;
其次,這個表可能不是很小,而是很大,因為可能 boys 已經被分離出去,是以這個表應當考慮分區,輔助其他條件,通過分區剪裁快速縮減查詢結果。
是以有朋友建議的SQL是這個樣子的:
SELECT * FROM girls
WHERE (age between 18 and 24) and NOT EXISTS (SELECT * FROM boys WHERE girls.boyfriend_id = boys.boyfriend_id);
再次,有朋友建議的索引優化:對 age 加 bitmap 索引,開并行。
注意,bitmap 對于這個頻繁變更的表不是一個好選擇,鑒于我們的判斷,boyfriend = 'no' 的記錄數極少,那麼關于 age + boyfriend 的複合索引就能快速的找到記錄,如果你是樂觀主義者,就加個 rownum 的限制,如果你是一個悲觀主義者,那麼就可以去掉boyfriend字段,然後加個 rownum 的限制。
最後,其實我們很容易發現對于age和boyfriend的存儲,行存不如列式存儲,如果使用Oracle 12c的IN-Memory,在記憶體中進行列式壓縮,可以極大的提高查詢性能。
如果您非常關注資料庫的性能,那麼雲和恩墨的資料庫性能監控平台,将會讓您對資料庫的性能一目了然,及時預警。參考:洞若觀火,ZONE助力性能提升。
可是注意,以上我們的推斷,是基于Oracle資料庫的考慮,如果資料庫是 MySQL、Redis,或是其他産品,該如何去調整和優化呢?
如果是MySQL,是否資料量相當龐大之後,如何分庫分表?
那麼多資料庫,那麼多管理、監控和優化工作,你一定要關注一下雲和恩墨最新推出的 zCloud 雲管平台,讓多雲、多資料管理、優化,融為一體,何以解多資料庫管理之憂?唯有zCloud。
關于隐私和安全
如果你以為就隻有這些?
那麼我還可以告訴你,如果完成以上查詢,并且碰巧獲得了輸出,那麼你可能已經違反了歐盟的GDPR法案,涉嫌侵犯了使用者隐私(哈哈哈哈哈哈,開心嗎)。
在2018年5月25日正式生效的 GDPR 法案,對于使用者的隐私做出了嚴格的界定,如果侵犯濫用使用者隐私資訊,将遭遇高達2000萬歐元或企業年營業額4%的高額懲罰,注意最後一條保護的隐私内容:
公民基本的身份資訊,如姓名、位址和身份證号等;
網絡資料,如位置、IP位址、Cookie資料和RFID标簽等;
醫療保健 和 遺傳資料;
生物識别資料,如指紋、虹膜等;
種族或民族資料;
政治觀點;
性取向;
參考前文:GDPR 法案帶來的思考。是以對于企業資料的管理者,如何保護資料安全,確定使用者隐私不被任意通路,不被DBA不授權通路,都是一個值得重視的問題,雲和恩墨已經提供針對 GDPR 的安全增強解決方案,歡迎垂詢。
千言萬語,千頭萬緒,彙成最後的答案:這條SQL最終不應該被執行,也不會有傳回結果。
加油吧,少年!