BATJTMD 等大廠的面試難度愈來愈高,但不管從大廠仍是到小公司,一直不變的重點就是對 SQL 優化經驗的考察。一提到數據庫,面試官就會問「先說一說你對 SQL 優化的看法吧?」。前端
圖片來自 Pexelsjava
SQL 優化已經成爲衡量程序猿優秀與否的硬性指標,甚至在各大廠招聘崗位職能上都有明碼標註,若是是你,在這個問題上能吊打面試官仍是會被吊打呢?python
有朋友疑問到,SQL 優化真的有這麼重要麼?以下圖所示,SQL 優化在提高系統性能中是:成本最低和優化效果最明顯的途徑。web
優化成本:硬件>系統配置>數據庫表結構>SQL 及索引。面試
優化效果:硬件<系統配置<數據庫表結構<SQL 及索引。數據庫
String result = "嗯,不錯,";
if ("SQL優化經驗足") {
if ("熟悉事務鎖") {
if ("併發場景處理666") {
if ("會打王者榮耀") {
result += "明天入職"
}
}
}
} else {
result += "先回去等消息吧";
}
Logger.info("面試官:" + result );
減小數據訪問:設置合理的字段類型,啓用壓縮,經過索引訪問等減小磁盤 IO。小程序
返回更少的數據:只返回須要的字段和數據分頁處理,減小磁盤 IO 及網絡 IO。緩存
減小交互次數:批量 DML 操做,函數存儲等減小數據鏈接次數。服務器
減小服務器 CPU 開銷:儘可能減小數據庫排序操做以及全表查詢,減小 CPU 內存佔用。微信
利用更多資源:使用表分區,能夠增長並行操做,更大限度利用 CPU 資源。
最大化利用索引。
儘量避免全表掃描。
減小無效數據的查詢。
SELECT 語句,語法順序以下:
1. SELECT
2. DISTINCT <select_list>
3. FROM <left_table>
4. <join_type> JOIN <right_table>
5. ON <join_condition>
6. WHERE <where_condition>
7. GROUP BY <group_by_list>
8. HAVING <having_condition>
9. ORDER BY <order_by_condition>
10.LIMIT <limit_number>
SELECT 語句,執行順序以下:
FROM
<表名> # 選取表,將多個表數據經過笛卡爾積變成一個表。
ON
<篩選條件> # 對笛卡爾積的虛表進行篩選
JOIN <join, left join, right join...>
<join表> # 指定join,用於添加數據到on以後的虛表中,例如left join會將左表的剩餘數據添加到虛表中
WHERE
<where條件> # 對上述虛表進行篩選
GROUP BY
<分組條件> # 分組
<SUM()等聚合函數> # 用於having子句進行判斷,在書寫上這類聚合函數是寫在having判斷裏面的
HAVING
<分組篩選> # 對分組後的結果進行聚合篩選
SELECT
<返回數據列表> # 返回的單列必須在group by子句中,聚合函數除外
DISTINCT
# 數據除重
ORDER BY
<排序條件> # 排序
LIMIT
<行數限制>
如下 SQL 優化策略適用於數據量較大的場景下,若是數據量較小,不必以此爲準,以避免多此一舉。
避免不走索引的場景
SELECT * FROM t WHERE username LIKE '%陳%'
SELECT * FROM t WHERE username LIKE '陳%'
使用 MySQL 內置函數 INSTR(str,substr)來匹配,做用相似於 Java 中的 indexOf(),查詢字符串出現的角標位置。
使用 FullText 全文索引,用 match against 檢索。
數據量較大的狀況,建議引用 ElasticSearch、Solr,億級數據量檢索速度秒級。
當表數據量較少(幾千條兒那種),別整花裏胡哨的,直接用 like '%xx%'。
以下:
SELECT * FROM t WHERE id IN (2,3)
以下:
SELECT * FROM t WHERE id BETWEEN 2 AND 3
以下:
-- 不走索引
select * from A where A.id in (select id from B);
-- 走索引
select * from A where exists (select * from B where B.id = A.id);
以下:
SELECT * FROM t WHERE id = 1 OR id = 3
以下:
SELECT * FROM t WHERE id = 1
UNION
SELECT * FROM t WHERE id = 3
以下:
SELECT * FROM t WHERE score IS NULL
以下:
SELECT * FROM t WHERE score = 0
-- 全表掃描
SELECT * FROM T WHERE score/10 = 9
-- 走索引
SELECT * FROM T WHERE score = 10*9
以下:
SELECT username, age, sex FROM T WHERE 1=1
⑦查詢條件不能用 <> 或者 !=
以下:複合(聯合)索引包含 key_part1,key_part2,key_part3 三列,但 SQL 語句沒有包含索引前置列"key_part1",按照 MySQL 聯合索引的最左匹配原則,不會走聯合索引。
select col1 from table where key_part2=1 and key_part3=2
以下 SQL 語句因爲索引對列類型爲 varchar,但給定的值爲數值,涉及隱式類型轉換,形成不能正確走索引。
select col1 from table where col_varchar=123;
以下:
-- 不走age索引
SELECT * FROM t order by age;
-- 走age索引
SELECT * FROM t where age > 0 order by age;
第一步:根據 where 條件和統計信息生成執行計劃,獲得數據。
第二步:將獲得的數據排序。當執行處理數據(order by)時,數據庫會先查看第一步的執行計劃,看 order by 的字段是否在執行計劃中利用了索引。若是是,則能夠利用索引順序而直接取得已經排好序的數據。若是不是,則從新進行排序操做。
第三步:返回排序後的數據。
USE INDEX 在你查詢語句中表名的後面,添加 USE INDEX 來提供但願 MySQL 去參考的索引列表,就可讓 MySQL 再也不考慮其餘可用的索引。
例子: SELECT col1 FROM table USE INDEX (mod_time, name)...
IGNORE INDEX 若是隻是單純的想讓 MySQL 忽略一個或者多個索引,可使用 IGNORE INDEX 做爲 Hint。
例子: SELECT col1 FROM table IGNORE INDEX (priority) ...
FORCE INDEX 爲強制 MySQL 使用一個特定的索引,可在查詢中使用FORCE INDEX 做爲 Hint。
例子: SELECT col1 FROM table FORCE INDEX (mod_time) ...
例如:
SELECT * FROM students FORCE INDEX (idx_class_id) WHERE class_id = 1 ORDER BY id DESC;
SELECT 語句其餘優化
②避免出現不肯定結果的函數
③多表關聯查詢時,小表在前,大表在後
增刪改 DML 語句優化
方法一:
insert into T values(1,2);
insert into T values(1,3);
insert into T values(1,4);
方法二:
Insert into T values(1,2),(1,3),(1,4);
減小 SQL 語句解析的操做,MySQL 沒有相似 Oracle 的 share pool,採用方法二,只須要解析一次就能進行數據的插入操做。
在特定場景能夠減小對 DB 鏈接次數。
SQL 語句較短,能夠減小網絡傳輸的 IO。
事務佔用的 undo 數據塊。
事務在 redo log 中記錄的數據塊。
釋放事務施加的,減小鎖爭用影響性能。特別是在須要使用 delete 刪除大量數據的時候,必須分解刪除量並按期 commit。
③避免重複查詢更新的數據
簡單方法實現:
Update t1 set time=now() where col1=1;
Select time from t1 where id =1;
使用變量,能夠重寫爲如下方式:
Update t1 set time=now () where col1=1 and @now: = now ();
Select @now;
寫入操做優先於讀取操做。
對某張數據表的寫入操做某一時刻只能發生一次,寫入請求按照它們到達的次序來處理。
對某張數據表的多個讀取操做能夠同時地進行。
LOW_PRIORITY 關鍵字應用於 DELETE、INSERT、LOAD DATA、REPLACE 和 UPDATE。
HIGH_PRIORITY 關鍵字應用於 SELECT 和 INSERT 語句。
DELAYED 關鍵字應用於 INSERT 和 REPLACE 語句。
查詢條件優化
例如:
SELECT col1, col2, COUNT(*) FROM table GROUP BY col1, col2 ORDER BY NULL ;
SELECT col1 FROM customerinfo WHERE CustomerID NOT in (SELECT CustomerID FROM salesinfo )
尤爲是當 salesinfo 表中對 CustomerID 建有索引的話,性能將會更好,查詢以下:
SELECT col1 FROM customerinfo
LEFT JOIN salesinfoON customerinfo.CustomerID=salesinfo.CustomerID
WHERE salesinfo.CustomerID IS NULL
高效:
SELECT COL1, COL2, COL3 FROM TABLE WHERE COL1 = 10
UNION ALL
SELECT COL1, COL2, COL3 FROM TABLE WHERE COL3= 'TEST';
低效:
SELECT COL1, COL2, COL3 FROM TABLE WHERE COL1 = 10
UNION
SELECT COL1, COL2, COL3 FROM TABLE WHERE COL3= 'TEST';
簡單的 SQL 容易使用到 MySQL 的 QUERY CACHE。
減小鎖表時間特別是使用 MyISAM 存儲引擎的表。
可使用多核 CPU。
案例 1:
select * from t where thread_id = 10000 and deleted = 0
order by gmt_create asc limit 0, 15;
select t.* from (select id from t where thread_id = 10000 and deleted = 0
order by gmt_create asc limit 0, 15) a, t
where a.id = t.id;
建表優化
②儘可能使用數字型字段(如性別,男:1 女:2),若只含數值信息的字段儘可能不要設計爲字符型,這會下降查詢和鏈接的性能,並會增長存儲開銷。
這是由於引擎在處理查詢和鏈接時會 逐個比較字符串中每個字符,而對於數字型而言只須要比較一次就夠了。
③查詢數據量大的表 會形成查詢緩慢。主要的緣由是掃描行數過多。這個時候能夠經過程序,分段分頁進行查詢,循環遍歷,將結果合併處理進行展現。
SELECT * FROM (SELECT ROW_NUMBER() OVER(ORDER BY ID ASC) AS rowid,*
FROM infoTab)t WHERE t.rowid > 100000 AND t.rowid <= 100050
做者:_陳哈哈
編輯:陶家龍
出處:https://sohu.gg/FGG98i
在公衆號菜單中可自行獲取專屬架構視頻資料,包括不限於 java架構、python系列、人工智能系列、架構系列,以及最新面試、小程序、大前端均無私奉獻,你會感謝個人哈
往期熱門文章:
1,架構的本質:如何打造一個有序的系統?
2,
分佈式高可靠之負載均衡,今天看了你確定會
3,
分佈式數據之緩存技術,一塊兒來揭開其神祕面紗
4,分佈式數據複製技術,今天就教你真正分身術
5,
數據分佈方式之哈希與一致性哈希,我就是個神算子
6
,分佈式存儲系統三要素,掌握這些就離成功不遠了
7
,想要設計一個好的分佈式系統,必須搞定這個理論
8
,
分佈式通訊技術之發佈訂閱,乾貨滿滿
9,
分佈式通訊技術之遠程調用:RPC
10
,秒殺系統每秒上萬次下單請求,咱們該怎麼去設計
本文分享自微信公衆號 - 架構師修煉(jiagouxiulian)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。