最近一直忙着處理原來老項目遺留的一些SQL優化問題,因爲當初表的設計以及字段設計的問題,隨着業務的增加,出現了大量的慢SQL,致使MySQL的CPU資源飆升,基於此,給你們簡單分享下這些比較使用的易於學習和使用的經驗。mysql
此次的話簡單說下如何防止你的索引失效。程序員
再說以前我先根據我最近的經驗說下我對索引的見解,我以爲並非因此的表都須要去創建索引,對於一些業務數據,可能量比較大了,查詢數據已經有了一點壓力,那麼最簡單、快速的辦法就是創建合適的索引,可是有些業務可能表裏就沒多少數據,或者表的使用頻率很是不高的狀況下是不必必需要去作索引的。就像咱們有些表,2年了可能就10來條數據,有索引和沒索引性能方面差很少多少。sql
索引只是咱們優化業務的一種方式,千萬爲了爲了建索引而去建索引。函數
下面是我這次測試使用的一張表結構以及一些測試數據post
`CREATE TABLE
user` (
id
int(5) unsigned NOT NULL AUTO_INCREMENT,
create_time
datetime NOT NULL,
name
varchar(5) NOT NULL,
age
tinyint(2) unsigned zerofill NOT NULL,
sex
char(1) NOT NULL,
mobile
char(12) NOT NULL DEFAULT '',
address
char(120) DEFAULT NULL,
height
varchar(10) DEFAULT NULL,
PRIMARY KEY (id
),
KEY idx_createtime
(create_time
) USING BTREE,
KEY idx_name_age_sex
(name
,sex
,age
) USING BTREE,
KEY idx_ height
(height
) USING BTREE,
KEY idx_address
(address
) USING BTREE,
KEY idx_age
(age
) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=261 DEFAULT CHARSET=utf8;
複製代碼``性能
`INSERT INTO
bingfeng.
user(
id,
create_time,
name,
age,
sex,
mobile,
address,
height`) VALUES (1, '2019-09-02 10:17:47', '冰峯', 22, '男', '1', '陝西省咸陽市彬縣', '175');
INSERT INTO bingfeng
.user
(id
, create_time
, name
, age
, sex
, mobile
, address
, height
) VALUES (2, '2020-09-02 10:17:47', '松子', 13, '女', '1', NULL, '180');
INSERT INTO bingfeng
.user
(id
, create_time
, name
, age
, sex
, mobile
, address
, height
) VALUES (3, '2020-09-02 10:17:48', '蠶豆', 20, '女', '1', NULL, '180');
INSERT INTO bingfeng
.user
(id
, create_time
, name
, age
, sex
, mobile
, address
, height
) VALUES (4, '2020-09-02 10:17:47', '冰峯', 20, '男', '17765010977', '陝西省西安市', '155');
INSERT INTO bingfeng
.user
(id
, create_time
, name
, age
, sex
, mobile
, address
, height
) VALUES (255, '2020-09-02 10:17:47', '竹筍', 22, '男', '我測試下能夠儲存幾個中文', NULL, '180');
INSERT INTO bingfeng
.user
(id
, create_time
, name
, age
, sex
, mobile
, address
, height
) VALUES (256, '2020-09-03 10:17:47', '冰峯', 21, '女', '', NULL, '167');
INSERT INTO bingfeng
.user
(id
, create_time
, name
, age
, sex
, mobile
, address
, height
) VALUES (257, '2020-09-02 10:17:47', '小紅', 20, '', '', NULL, '180');
INSERT INTO bingfeng
.user
(id
, create_time
, name
, age
, sex
, mobile
, address
, height
) VALUES (258, '2020-09-02 10:17:47', '小鵬', 20, '', '', NULL, '188');
INSERT INTO bingfeng
.user
(id
, create_time
, name
, age
, sex
, mobile
, address
, height
) VALUES (259, '2020-09-02 10:17:47', '張三', 20, '', '', NULL, '180');
INSERT INTO bingfeng
.user
(id
, create_time
, name
, age
, sex
, mobile
, address
, height
) VALUES (260, '2020-09-02 10:17:47', '李四', 22, '', '', NULL, '165');
複製代碼``學習
`SELECT * FROM
user WHERE
name` != '冰峯';
複製代碼``測試
咱們給name字段創建了索引,可是若是!= 或者 <> 這種都會致使索引失效,進行全表掃描,因此若是數據量大的話,謹慎使用優化
能夠經過分析SQL看到,type類型是ALL,掃描了10行數據,進行了全表掃描。<>也是一樣的結果。spa
在說這個以前,必定要說一下設計表字段的時候,千萬、必定、必需要保持字段類型的一致性,啥意思?好比user表的id是int自增,到了用戶的帳戶表user_id這個字段,必定、必須也是int類型,千萬不要寫成varchar、char什麼的騷操做。
`SELECT * FROM
user` WHERE height= 175;
複製代碼``
這個SQL諸位必定要看清楚,height表字段類型是varchar,可是我查詢的時候使用了數字類型,由於這個中間存在一個隱式的類型轉換,因此就會致使索引失效,進行全表掃描。
如今明白我爲啥說設計字段的時候必定要保持類型的一致性了不,若是你不保證一致性,一個int一個varchar,在進行多表聯合查詢(eg: 1 = '1')必然走不了索引。
遇到這樣的表,裏面有幾千萬數據,改又不能改,那種痛可能大家暫時還體會。
少年們,切記,切記。
`SELECT * FROM
user` WHERE DATE(create_time) = '2020-09-03';
複製代碼``
若是你的索引字段使用了索引,對不起,他是真的不走索引的。
`SELECT * FROM
user` WHERE age - 1 = 20;
複製代碼``
若是你對列進行了(+,-,*,/,!), 那麼都將不會走索引。
`SELECT * FROM
user WHERE
name` = '張三' OR height = '175';
複製代碼``
OR致使索引是在特定狀況下的,並非全部的OR都是使索引失效,若是OR鏈接的是同一個字段,那麼索引不會失效,反之索引失效。
`SELECT * FROM
user WHERE
name` LIKE '%冰';
複製代碼``
這個我相信你們都明白,模糊搜索若是你前綴也進行模糊搜索,那麼不會走索引。
`SELECT s.* FROM
user s WHERE NOT EXISTS (SELECT * FROM
user u WHERE u.name = s.
name AND u.
name` = '冰峯')
複製代碼``
`SELECT * FROM
user WHERE
name` NOT IN ('冰峯');
複製代碼``
這兩種用法,也將使索引失效。可是NOT IN 仍是走索引的,千萬不要誤解爲 IN 所有是不走索引的。我以前就有誤解(丟人了...)。
`SELECT * FROM
user` WHERE address IS NULL
複製代碼``
不走索引。
`SELECT * FROM
user` WHERE address IS NOT NULL;
複製代碼``
走索引。
根據這個狀況,建議你們這設計字段的時候,若是沒有必要的要求必須爲NULL,那麼最好給個默認值空字符串,這能夠解決不少後續的麻煩(有深入的體驗<體驗=教訓>)。
`EXPLAIN SELECT * FROM
user` WHERE sex = '男';
複製代碼``
`EXPLAIN SELECT * FROM
user` WHERE name = '冰峯' AND sex = '男';
複製代碼``
測試以前,刪除其餘的單列索引。
啥叫最左匹配原則,就是對於符合索引來講,它的一個索引的順序是從左往右依次進行比較的,像第二個查詢語句,name走索引,接下來回去找age,結果條件中沒有age那麼後面的sex也將不走索引。
注意:
`SELECT * FROM
user WHERE sex = '男' AND age = 22 AND
name` = '冰峯';
複製代碼``
可能有些搬磚工可能跟我最開始有個誤解,咱們的索引順序明明是name、sex、age,你如今的查詢順序是sex、age、name,這確定不走索引啊,你要是本身沒測試過,也有這種不成熟的想法,那跟我同樣仍是太年輕了,它其實跟順序是沒有任何關係的,由於mysql的底層會幫咱們作一個優化,它會把你的SQL優化爲它認爲一個效率最高的樣子進行執行。因此千萬不要有這種誤解。
`SELECT * FROM
user WHERE sex = '男' AND
name` != '冰峯' AND age = 22;
複製代碼``
咱們在name字段使用了 != ,因爲name字段是最左邊的一個字段,根據最左匹配原則,若是name不走索引,後面的字段也將不走索引。
關於符合索引致使索引失效的狀況能說的目前就這兩種,其實我以爲對於符合索引來講,重要的是如何創建高效的索引,千萬不能說我用到那個字段我就去創建一個單獨的索引,不是就能夠全局用了嘛。這樣是能夠,可是這樣並無符合索引高效,因此爲了成爲高級的搬磚工,咱們仍是要繼續學習,如何建立高效的索引。
做者:一個程序員的成長
連接:https://juejin.im/post/686927... 來源:掘金 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。