八、mysql 基礎優化 索引管理 1、課程大綱 索引介紹 索引管理 2、執行計劃獲取及分析 mysql mysql資料庫中索引的類型介紹 BTREE:B+樹索引 (主要) HASH:HASH索引 FULLTEXT:全文索引 RTREE:R樹索引 索引管理: 索引建立的在表的列上(欄位)的。 在wh ...
八、mysql 基礎優化-索引管理
1、課程大綱
索引介紹
索引管理
2、執行計劃獲取及分析
mysql資料庫中索引的類型介紹
BTREE:B+樹索引 (主要)
HASH:HASH索引
FULLTEXT:全文索引
RTREE:R樹索引
------
索引管理:
索引建立的在表的列上(欄位)的。
在where後面的列建立索引才會加快查詢速度。
索引分類:
- 主鍵索引
- 普通索引****
- 唯一索引
添加索引
alter table test add index index_name(name);
create index index_name on test(name);
查詢表是否有索引信息:
DESC stu;看他的key列值
mysql> explain select * from stu;
mysql> explain select * from stu where stu_name='zhangsan';
查看到的type不同。
----------------------------------------------------------
索引及執行計劃
索引基本管理:
創建和刪除:
alter table stu add index idx_name(stu_name);
alter table stu drop index idx_name;
或者
create index inx_name on stu(stu_name);
drop index inx_name on stu;
查詢索引設置
desc stu;
主鍵索引: 唯一、非空
走主鍵索引的查詢效率是最高的,我們儘量每個表有一個主鍵,並且將來查詢的時候計量以主鍵為條件查詢
CREATE TABLE `test` (
`id` int(4) NOT NULL AUTO_INCREMENT,
`name` char(20) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=UTF8;
CREATE TABLE `test1` (
`id` int(4) NOT NULL,
`name` char(20) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=UTF8;
增加自增主鍵
alter table test1 change id id int(4) primary key not null auto_increment;
首碼索引:
create index index_name on stu(stu_id(8));
聯合索引:
where a女生 and b身高165 and c身材好
index(a,b,c)
特點:首碼生效特性。
a,ab,abc,ac 可以走索引。
b bc c 不走索引。
原則:把最常用來作為條件查詢的列放在前面。
走索引:
select * from people where a='nv' and b>=165 and tizhong<=120;
select * from people where a='nv' and b>=165;
select * from people where a='nv';
select * from people where a='nv' and tizhong<=120;
alter table stu add index minx(gender,age);
唯一性索引:
create unique index index_name on test(name);
3、explain 調取語句的執行計劃
主要是判斷語句是否走索引
explain select stu_name,gender,age from stu where gender='F' and age <20;
mysql> explain select name,gender,age from test where gender='F' and age <20;
+----+-------------+-------+-------+---------------+----------+---------+------+------+-----------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+----------+---------+------+------+-----------------------+
| 1 | SIMPLE | test | range | inx_test | inx_test | 7 | NULL | 1 | Using index condition |
type : 表示MySQL在表中找到所需行的方式,又稱“訪問類型”,
常見類型如下:
ALL,index, range, ref, eq_ref, const, system, NULL
從左到右,性能從最差到最好
ALL:
Full Table Scan, MySQL將遍歷全表以找到匹配的行
如果顯示ALL,說明:
查詢沒有走索引:
1、語句本身的問題
2、索引的問題,沒建立索引
index:Full Index Scan,index與ALL區別為index類型只遍歷索引樹
例子:
explain select count(*) from stu ;
range:索引範圍掃描,對索引的掃描開始於某一點,返回匹配值域的行。
顯而易見的索引範圍掃描是帶有between或者where子句裡帶有<,>查詢。
where 條件中有範圍查詢或模糊查詢時
> < >= <= between and in () or
like 'xx%'
當mysql使用索引去查找一系列值時,例如IN()和OR列表,也會顯示range(範圍掃描),當然性能上面是有差異的。
ref:使用非唯一索引掃描或者唯一索引的首碼掃描,返回匹配某個單獨值的記錄行
where stu_name='xiaoming'
explain select * from stu where stu_name='aa';
eq_ref:類似ref,區別就在使用的索引是唯一索引,對於每個索引鍵值,表中只有一條記錄匹配,簡單來說,
就是多表連接中使用primary key或者 unique key作為關聯條件
join條件使用的是primary key或者 unique key
const、system:當MySQL對查詢某部分進行優化,並轉換為一個常量時,使用這些類型訪問。
如將主鍵置於where列表中,MySQL就能將該查詢轉換為一個常量
explain select * from city where id=1;
NULL:MySQL在優化過程中分解語句,執行時甚至不用訪問表或索引,
例如從一個索引列里選取最小值可以通過單獨索引查找完成。
mysql> explain select name,population from city;
+----+-------------+-------+------+---------------+------+---------+------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+-------+
| 1 | SIMPLE | city | ALL | NULL | NULL | NULL | NULL | 4188 | NULL |
+----+-------------+-------+------+---------------+------+---------+------+------+-------+
Extra:
Using temporary
Using filesort
Using join buffer
排序 order by ,group by ,distinct,排序條件上沒有索引
explain select * from city where countrycode='CHN' order by population;
在join 的條件列上沒有建立索引
4、資料庫索引的設計原則:
一、資料庫索引的設計原則:
為了使索引的使用效率更高,在創建索引時,必須考慮在哪些欄位上創建索引和創建什麼類型的索引。
那麼索引設計原則又是怎樣的?(儘量使用主鍵索引和唯一性索引。)
1.選擇唯一性索引
唯一性索引的值是唯一的,可以更快速的通過該索引來確定某條記錄。
例如,學生表中學號是具有唯一性的欄位。為該欄位建立唯一性索引可以很快的確定某個學生的信息。
如果使用姓名的話,可能存在同名現象,從而降低查詢速度。
主鍵索引和唯一鍵索引,在查詢中使用是效率最高的。
2.為經常需要排序、分組和聯合操作的欄位建立索引
經常需要ORDER BY、GROUP BY、DISTINCT和UNION等操作的欄位,排序操作會浪費很多時間。
如果為其建立索引,可以有效地避免排序操作。
3.為常作為查詢條件的欄位建立索引
如果某個欄位經常用來做查詢條件,那麼該欄位的查詢速度會影響整個表的查詢速度。因此,
為這樣的欄位建立索引,可以提高整個表的查詢速度。
select count(DISTINCT population ) from city;
select count(*) from city;
4.儘量使用首碼來索引
如果索引欄位的值很長,最好使用值的首碼來索引。例如,TEXT和BLOG類型的欄位,進行全文檢索
會很浪費時間。如果只檢索欄位的前面的若幹個字元,這樣可以提高檢索速度。
------------------------以上的是重點關註的,以下是能保證則保證的--------------------
5.限制索引的數目
索引的數目不是越多越好。每個索引都需要占用磁碟空間,索引越多,需要的磁碟空間就越大。
修改表時,對索引的重構和更新很麻煩。越多的索引,會使更新表變得很浪費時間。
6.儘量使用數據量少的索引
如果索引的值很長,那麼查詢的速度會受到影響。例如,對一個CHAR(100)類型的欄位進行全文
檢索需要的時間肯定要比對CHAR(10)類型的欄位需要的時間要多。
7.刪除不再使用或者很少使用的索引
表中的數據被大量更新,或者數據的使用方式被改變後,原有的一些索引可能不再需要。資料庫管理
員應當定期找出這些索引,將它們刪除,從而減少索引對更新操作的影響。
5、索引的開發規範
不走索引的情況:
重點關註:
1) 沒有查詢條件,或者查詢條件沒有建立索引
select * from tab; 全表掃描。
select * from tab where 1=1;
在業務資料庫中,特別是數據量比較大的表。
是沒有全表掃描這種需求。
1、對用戶查看是非常痛苦的。
2、對伺服器來講毀滅性的。
(1)select * from tab;
SQL改寫成以下語句:
selec * from tab order by price limit 10 需要在price列上建立索引
(2)
select * from tab where name='zhangsan' name列沒有索引
改:
1、換成有索引的列作為查詢條件
2、將name列建立索引
2) 查詢結果集是原表中的大部分數據,應該是30%以上。
查詢的結果集,超過了總數行數30%,優化器覺得就沒有必要走索引了。
假如:tab表 id,name id:1-100w ,id列有索引
select * from tab where id>500000;
如果業務允許,可以使用limit控制。
怎麼改寫 ?
結合業務判斷,有沒有更好的方式。如果沒有更好的改寫方案
儘量不要在mysql存放這個數據了。放到redis裡面。
3) 索引本身失效,統計數據不真實
索引有自我維護的能力。
對於表內容變化比較頻繁的情況下,有可能會出現索引失效。
4) 查詢條件使用函數在索引列上,或者對索引列進行運算,運算包括(+,-,*,/,! 等)
例子:
錯誤的例子:select * from test where id-1=9;
正確的例子:select * from test where id=10;
5)隱式轉換導致索引失效.這一點應當引起重視.也是開發中經常會犯的錯誤.
由於表的欄位tu_mdn定義為varchar2(20),但在查詢時把該欄位作為number類型以where條件傳給資料庫,
這樣會導致索引失效. 錯誤的例子:select * from test where tu_mdn=13333333333;
正確的例子:select * from test where tu_mdn='13333333333';
------------------------
mysql> alter table tab add index inx_tel(telnum);
Query OK, 0 rows affected (0.03 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> desc tab;
+--------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------+-------------+------+-----+---------+-------+
| id | int(11) | YES | | NULL | |
| name | varchar(20) | YES | | NULL | |
| telnum | varchar(20) | YES | MUL | NULL | |
+--------+-------------+------+-----+---------+-------+
3 rows in set (0.01 sec)
mysql> select * from tab where telnum='1333333';
+------+------+---------+
| id | name | telnum |
+------+------+---------+
| 1 | a | 1333333 |
+------+------+---------+
1 row in set (0.00 sec)
mysql> select * from tab where telnum=1333333;
+------+------+---------+
| id | name | telnum |
+------+------+---------+
| 1 | a | 1333333 |
+------+------+---------+
1 row in set (0.00 sec)
mysql> explain select * from tab where telnum='1333333';
+----+-------------+-------+------+---------------+---------+---------+-------+------+-----------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+---------+---------+-------+------+-----------------------+
| 1 | SIMPLE | tab | ref | inx_tel | inx_tel | 63 | const | 1 | Using index condition |
+----+-------------+-------+------+---------------+---------+---------+-------+------+-----------------------+
1 row in set (0.00 sec)
mysql> explain select * from tab where telnum=1333333;
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| 1 | SIMPLE | tab | ALL | inx_tel | NULL | NULL | NULL | 2 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)
mysql> explain select * from tab where telnum=1555555;
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| 1 | SIMPLE | tab | ALL | inx_tel | NULL | NULL | NULL | 2 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)
mysql> explain select * from tab where telnum='1555555';
+----+-------------+-------+------+---------------+---------+---------+-------+------+-----------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+---------+---------+-------+------+-----------------------+
| 1 | SIMPLE | tab | ref | inx_tel | inx_tel | 63 | const | 1 | Using index condition |
+----+-------------+-------+------+---------------+---------+---------+-------+------+-----------------------+
1 row in set (0.00 sec)
mysql>
------------------------
6)
<> ,not in 不走索引
EXPLAIN SELECT * FROM teltab WHERE telnum <> '110';
EXPLAIN SELECT * FROM teltab WHERE telnum NOT IN ('110','119');
------------
mysql> select * from tab where telnum <> '1555555';
+------+------+---------+
| id | name | telnum |
+------+------+---------+
| 1 | a | 1333333 |
+------+------+---------+
1 row in set (0.00 sec)
mysql> explain select * from tab where telnum <> '1555555';
-----
單獨的>,<,in 有可能走,也有可能不走,和結果集有關,儘量結合業務添加limit
or或in 儘量改成union
EXPLAIN SELECT * FROM teltab WHERE telnum IN ('110','119');
改寫成:
EXPLAIN SELECT * FROM teltab WHERE telnum='110'
UNION ALL
SELECT * FROM teltab WHERE telnum='119'
-----------------------------------
7) like "%_" 百分號在最前面不走
EXPLAIN SELECT * FROM teltab WHERE telnum LIKE '31%' 走range索引掃描
EXPLAIN SELECT * FROM teltab WHERE telnum LIKE '%110' 不走索引
%linux%類的搜索需求,可以使用elasticsearch
%linux培訓%
8) 單獨引用複合索引里非第一位置的索引列.
列子:
複合索引:
DROP TABLE t1
CREATE TABLE t1 (id INT,NAME VARCHAR(20),age INT ,sex ENUM('m','f'),money INT);
ALTER TABLE t1 ADD INDEX t1_idx(money,age,sex);
DESC t1
SHOW INDEX FROM t1
走索引的情況測試:
EXPLAIN SELECT NAME,age,sex,money FROM t1 WHERE money=30 AND age=30 AND sex='m';
EXPLAIN SELECT NAME,age,sex,money FROM t1 WHERE money=30 AND age=30 ;
EXPLAIN SELECT NAME,age,sex,money FROM t1 WHERE money=30 AND sex='m'; ----->部分走索引
不走索引的:
EXPLAIN SELECT NAME,age,sex,money FROM t1 WHERE age=20
EXPLAIN SELECT NAME,age,sex,money FROM t1 WHERE age=30 AND sex='m';
EXPLAIN SELECT NAME,age,sex,money FROM t1 WHERE sex='m';