mysql select in 不存在返回0_MySQL索引优化看这篇文章就够了!
來(lái)源:cnblogs.com/songwenjie/p/9410009.html
本文主要討論MySQL索引的部分知識(shí)。將會(huì)從MySQL索引基礎(chǔ)、索引優(yōu)化實(shí)戰(zhàn)和數(shù)據(jù)庫(kù)索引背后的數(shù)據(jù)結(jié)構(gòu)三部分相關(guān)內(nèi)容,下面一一展開(kāi)(本文圖片可點(diǎn)開(kāi)放大)。
一、MySQL索引基礎(chǔ)
首先,我們將從索引基礎(chǔ)開(kāi)始介紹一下什么是索引,分析索引的幾種類(lèi)型,并探討一下如何創(chuàng)建索引以及索引設(shè)計(jì)的基本原則。
此部分用于測(cè)試索引創(chuàng)建的user表的結(jié)構(gòu)如下:
1. 什么是索引?
“索引(在MySQL中也叫“鍵key”)是存儲(chǔ)引擎快速找到記錄的一種數(shù)據(jù)結(jié)構(gòu)。”——《高性能MySQL》我們需要知道索引其實(shí)是一種數(shù)據(jù)結(jié)構(gòu),其功能是幫助我們快速匹配查找到需要的數(shù)據(jù)行,是數(shù)據(jù)庫(kù)性能優(yōu)化最常用的工具之一。其作用相當(dāng)于超市里的導(dǎo)購(gòu)員、書(shū)本里的目錄。
2. 索引類(lèi)型
可以使用SHOW INDEX FROM table_name;查看索引詳情:
主鍵索引 PRIMARY KEY:它是一種特殊的唯一索引,不允許有空值。一般是在建表的時(shí)候同時(shí)創(chuàng)建主鍵索引。注意:一個(gè)表只能有一個(gè)主鍵。
唯一索引 UNIQUE:唯一索引列的值必須唯一,但允許有空值。如果是組合索引,則列值的組合必須唯一。可以通過(guò)ALTER TABLE table_name ADD UNIQUE (column);創(chuàng)建唯一索引:
可以通過(guò)ALTER TABLE table_name ADD UNIQUE (column1,column2);創(chuàng)建唯一組合索引:
普通索引 INDEX:這是最基本的索引,它沒(méi)有任何限制。可以通過(guò)ALTER TABLE table_name ADD INDEX index_name (column);創(chuàng)建普通索引:
組合索引 INDEX:即一個(gè)索引包含多個(gè)列,多用于避免回表查詢(xún)。可以通過(guò)ALTER TABLE table_name ADD INDEX index_name(column1,column2, column3);創(chuàng)建組合索引:
全文索引 FULLTEXT:也稱(chēng)全文檢索,是目前搜索引擎使用的一種關(guān)鍵技術(shù)。可以通過(guò)ALTER TABLE table_name ADD FULLTEXT (column);創(chuàng)建全文索引:
索引一經(jīng)創(chuàng)建不能修改,如果要修改索引,只能刪除重建。可以使用
DROP INDEX index_name ON table_name;刪除索引。
3、索引設(shè)計(jì)的原則
1)適合索引的列是出現(xiàn)在where子句中的列,或者連接子句中指定的列;
2)基數(shù)較小的類(lèi),索引效果較差,沒(méi)有必要在此列建立索引;
3)使用短索引,如果對(duì)長(zhǎng)字符串列進(jìn)行索引,應(yīng)該指定一個(gè)前綴長(zhǎng)度,這樣能夠節(jié)省大量索引空間;
4)不要過(guò)度索引。索引需要額外的磁盤(pán)空間,并降低寫(xiě)操作的性能。在修改表內(nèi)容的時(shí)候,索引會(huì)進(jìn)行更新甚至重構(gòu),索引列越多,這個(gè)時(shí)間就會(huì)越長(zhǎng)。所以只保持需要的索引有利于查詢(xún)即可。
二、MySQL索引優(yōu)化實(shí)戰(zhàn)
上面我們介紹了索引的基本內(nèi)容,這部分我們介紹索引優(yōu)化實(shí)戰(zhàn)。在介紹索引優(yōu)化實(shí)戰(zhàn)之前,首先要介紹兩個(gè)與索引相關(guān)的重要概念,這兩個(gè)概念對(duì)于索引優(yōu)化至關(guān)重要。
此部分用于測(cè)試的user表結(jié)構(gòu):
1、索引相關(guān)的重要概念
基數(shù):單個(gè)列唯一鍵(distict_keys)的數(shù)量叫做基數(shù)。
SELECT COUNT(DISTINCT name),COUNT(DISTINCT gender) FROM user;
user表的總行數(shù)是5,gender列的基數(shù)是2,說(shuō)明gender列里面有大量重復(fù)值,name列的基數(shù)等于總行數(shù),說(shuō)明name列沒(méi)有重復(fù)值,相當(dāng)于主鍵。
返回?cái)?shù)據(jù)的比例:user表中共有5條數(shù)據(jù):
SELECT * FROM user;
查詢(xún)滿足性別為0(男)的記錄數(shù):
那么返回記錄的比例數(shù)是:
同理,查詢(xún)name為'swj'的記錄數(shù):
返回記錄的比例數(shù)是:
現(xiàn)在問(wèn)題來(lái)了,假設(shè)name、gender列都有索引,那么SELECT * FROM user WHERE gender = 0; SELECT * FROM user WHERE name = 'swj';都能命中索引嗎?
user表的索引詳情:
SELECT * FROM user WHERE gender = 0;沒(méi)有命中索引,注意filtered的值就是上面我們計(jì)算的返回記錄的比例數(shù)。
SELECT * FROM user WHERE name = 'swj';命中了索引index_name,因?yàn)樽咚饕苯泳湍苷业揭樵?xún)的記錄,所以filtered的值為100。
因此,返回表中30%內(nèi)的數(shù)據(jù)會(huì)走索引,返回超過(guò)30%數(shù)據(jù)就使用全表掃描。當(dāng)然這個(gè)結(jié)論太絕對(duì)了,也并不是絕對(duì)的30%,只是一個(gè)大概的范圍。
回表:當(dāng)對(duì)一個(gè)列創(chuàng)建索引之后,索引會(huì)包含該列的鍵值及鍵值對(duì)應(yīng)行所在的rowid。通過(guò)索引中記錄的rowid訪問(wèn)表中的數(shù)據(jù)就叫回表。回表次數(shù)太多會(huì)嚴(yán)重影響SQL性能,如果回表次數(shù)太多,就不應(yīng)該走索引掃描,應(yīng)該直接走全表掃描。
EXPLAIN命令結(jié)果中的Using Index意味著不會(huì)回表,通過(guò)索引就可以獲得主要的數(shù)據(jù)。Using Where則意味著需要回表取數(shù)據(jù)。
2. 索引優(yōu)化實(shí)戰(zhàn)
有些時(shí)候雖然數(shù)據(jù)庫(kù)有索引,但是并不被優(yōu)化器選擇使用。我們可以通過(guò)SHOW STATUS LIKE 'Handler_read%';查看索引的使用情況:
Handler_read_key:如果索引正在工作,Handler_read_key的值將很高。
Handler_read_rnd_next:數(shù)據(jù)文件中讀取下一行的請(qǐng)求數(shù),如果正在進(jìn)行大量的表掃描,值將較高,則說(shuō)明索引利用不理想。
索引優(yōu)化規(guī)則:
1)如果MySQL估計(jì)使用索引比全表掃描還慢,則不會(huì)使用索引。
返回?cái)?shù)據(jù)的比例是重要的指標(biāo),比例越低越容易命中索引。記住這個(gè)范圍值——30%,后面所講的內(nèi)容都是建立在返回?cái)?shù)據(jù)的比例在30%以?xún)?nèi)的基礎(chǔ)上。
2)前導(dǎo)模糊查詢(xún)不能命中索引。
name列創(chuàng)建普通索引:
前導(dǎo)模糊查詢(xún)不能命中索引:
EXPLAIN SELECT * FROM user WHERE name LIKE '%s%';
非前導(dǎo)模糊查詢(xún)則可以使用索引,可優(yōu)化為使用非前導(dǎo)模糊查詢(xún):
EXPLAIN SELECT * FROM user WHERE name LIKE 's%';
3)數(shù)據(jù)類(lèi)型出現(xiàn)隱式轉(zhuǎn)換的時(shí)候不會(huì)命中索引,特別是當(dāng)列類(lèi)型是字符串,一定要將字符常量值用引號(hào)引起來(lái)。
EXPLAIN SELECT * FROM user WHERE name=1;
EXPLAIN SELECT * FROM user WHERE name='1';
4)復(fù)合索引的情況下,查詢(xún)條件不包含索引列最左邊部分(不滿足最左原則),不會(huì)命中符合索引。
name,age,status列創(chuàng)建復(fù)合索引:
ALTER TABLE user ADD INDEX index_name (name,age,status);
user表索引詳情:
SHOW INDEX FROM user;
根據(jù)最左原則,可以命中復(fù)合索引index_name:
EXPLAIN SELECT * FROM user WHERE name='swj' AND status=1;
注意,最左原則并不是說(shuō)是查詢(xún)條件的順序:
EXPLAIN SELECT * FROM user WHERE status=1 AND name='swj';
而是查詢(xún)條件中是否包含索引最左列字段:
EXPLAIN SELECT * FROM user WHERE status=2 ;
5)union、in、or都能夠命中索引,建議使用in。
union:
EXPLAIN SELECT*FROM user WHERE status=1
UNION ALL
SELECT*FROM user WHERE status = 2;
in:
EXPLAIN SELECT * FROM user WHERE status IN (1,2);
or:
EXPLAIN SELECT*FROM user WHERE status=1OR status=2;
查詢(xún)的CPU消耗:or>in>union。
6)用or分割開(kāi)的條件,如果or前的條件中列有索引,而后面的列中沒(méi)有索引,那么涉及到的索引都不會(huì)被用到。
EXPLAIN SELECT * FROM payment WHERE customer_id = 203 OR amount = 3.96;
因?yàn)閛r后面的條件列中沒(méi)有索引,那么后面的查詢(xún)肯定要走全表掃描,在存在全表掃描的情況下,就沒(méi)有必要多一次索引掃描增加IO訪問(wèn)。
7)負(fù)向條件查詢(xún)不能使用索引,可以?xún)?yōu)化為in查詢(xún)。
負(fù)向條件有:!=、<>、not in、not exists、not like等。
status列創(chuàng)建索引:
ALTER TABLE user ADD INDEX index_status (status);
user表索引詳情:
SHOW INDEX FROM user;
負(fù)向條件不能命中緩存:
EXPLAIN SELECT * FROM user WHERE status !=1 AND status != 2;
可以?xún)?yōu)化為in查詢(xún),但是前提是區(qū)分度要高,返回?cái)?shù)據(jù)的比例在30%以?xún)?nèi):
EXPLAIN SELECT * FROM user WHERE status IN (0,3,4);
8)范圍條件查詢(xún)可以命中索引。范圍條件有:、>=、between等。
status,age列分別創(chuàng)建索引:
ALTER TABLE user ADD INDEX index_status (status);
ALTER TABLE user ADD INDEX index_age (age);
user表索引詳情:
SHOW INDEX FROM user;
范圍條件查詢(xún)可以命中索引:
EXPLAIN SELECT * FROM user WHERE status>5;
范圍列可以用到索引(聯(lián)合索引必須是最左前綴),但是范圍列后面的列無(wú)法用到索引,索引最多用于一個(gè)范圍列,如果查詢(xún)條件中有兩個(gè)范圍列則無(wú)法全用到索引:
EXPLAIN SELECT * FROM user WHERE status>5 AND age<24;
如果是范圍查詢(xún)和等值查詢(xún)同時(shí)存在,優(yōu)先匹配等值查詢(xún)列的索引:
EXPLAIN SELECT * FROM user WHERE status>5 AND age=24;
8)數(shù)據(jù)庫(kù)執(zhí)行計(jì)算不會(huì)命中索引。
EXPLAIN SELECT * FROM user WHERE age>24;
EXPLAIN SELECT * FROM user WHERE age+1>24;
計(jì)算邏輯應(yīng)該盡量放到業(yè)務(wù)層處理,節(jié)省數(shù)據(jù)庫(kù)的CPU的同時(shí)最大限度的命中索引。
9)利用覆蓋索引進(jìn)行查詢(xún),避免回表。
被查詢(xún)的列,數(shù)據(jù)能從索引中取得,而不用通過(guò)行定位符row-locator再到row上獲取,即“被查詢(xún)列要被所建的索引覆蓋”,這能夠加速查詢(xún)速度。
user表的索引詳情:
因?yàn)閟tatus字段是索引列,所以直接從索引中就可以獲取值,不必回表查詢(xún):
Using Index代表從索引中查詢(xún):
EXPLAIN SELECT status FROM user where status=1;
當(dāng)查詢(xún)其他列時(shí),就需要回表查詢(xún),這也是為什么要避免SELECT*的原因之一:
EXPLAIN SELECT * FROM user where status=1;
10)建立索引的列,不允許為null。
單列索引不存null值,復(fù)合索引不存全為null的值,如果列允許為null,可能會(huì)得到“不符合預(yù)期”的結(jié)果集,所以,請(qǐng)使用not null約束以及默認(rèn)值。
remark列建立索引:
ALTER TABLE user ADD INDEX index_remark (remark);
IS NULL可以命中索引:
EXPLAIN SELECT * FROM user WHERE remark IS NULL;
IS NOT NULL不能命中索引:
EXPLAIN SELECT * FROM user WHERE remark IS NOT NULL;
雖然IS NULL可以命中索引,但是NULL本身就不是一種好的數(shù)據(jù)庫(kù)設(shè)計(jì),應(yīng)該使用NOT NULL約束以及默認(rèn)值。
a. 更新十分頻繁的字段上不宜建立索引:因?yàn)楦虏僮鲿?huì)變更B+樹(shù),重建索引。這個(gè)過(guò)程是十分消耗數(shù)據(jù)庫(kù)性能的。
b. 區(qū)分度不大的字段上不宜建立索引:類(lèi)似于性別這種區(qū)分度不大的字段,建立索引的意義不大。因?yàn)椴荒苡行н^(guò)濾數(shù)據(jù),性能和全表掃描相當(dāng)。另外返回?cái)?shù)據(jù)的比例在30%以外的情況下,優(yōu)化器不會(huì)選擇使用索引。
c. 業(yè)務(wù)上具有唯一特性的字段,即使是多個(gè)字段的組合,也必須建成唯一索引。雖然唯一索引會(huì)影響insert速度,但是對(duì)于查詢(xún)的速度提升是非常明顯的。另外,即使在應(yīng)用層做了非常完善的校驗(yàn)控制,只要沒(méi)有唯一索引,在并發(fā)的情況下,依然有臟數(shù)據(jù)產(chǎn)生。
d. 多表關(guān)聯(lián)時(shí),要保證關(guān)聯(lián)字段上一定有索引。
e. 創(chuàng)建索引時(shí)避免以下錯(cuò)誤觀念:索引越多越好,認(rèn)為一個(gè)查詢(xún)就需要建一個(gè)索引;寧缺勿濫,認(rèn)為索引會(huì)消耗空間、嚴(yán)重拖慢更新和新增速度;抵制唯一索引,認(rèn)為業(yè)務(wù)的唯一性一律需要在應(yīng)用層通過(guò)“先查后插”方式解決;過(guò)早優(yōu)化,在不了解系統(tǒng)的情況下就開(kāi)始優(yōu)化。
3. 小結(jié)
對(duì)于自己編寫(xiě)的SQL查詢(xún)語(yǔ)句,要盡量使用EXPLAIN命令分析一下,做一個(gè)對(duì)SQL性能有追求的程序員。衡量一個(gè)程序員是否靠譜,SQL能力是一個(gè)重要的指標(biāo)。作為后端程序員,深以為然。
(完)
ps:最近學(xué)習(xí)資料有更新,wx公眾號(hào):Java7749
#java討論區(qū)# #編程語(yǔ)言JAVA# #MySQL#
總結(jié)
以上是生活随笔為你收集整理的mysql select in 不存在返回0_MySQL索引优化看这篇文章就够了!的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: mysql 压力测试知乎_MySQL查看
- 下一篇: linux cmake编译源码,linu