數據結構-索引-實驗7:索引優化(MySQL-8.0)

數據結構-索引-實驗7:索引優化(MySQL-8.0)

一、實驗目的及要求

1、 理解索引優化的相關概念,如:基數、回表;

2、 掌握索引優化的規則;

二、實驗環境及相關情況(包含使用軟件、實驗設備、主要儀器及材料等)

1、實驗設備:

(1)微型計算機:i7處理器、2G內存
在這裏插入圖片描述

2、軟件系統:

(1)VMware Workstation 15 Player:虛擬機,用於安裝Windows 7操作系統。在虛擬機上安裝Windows 7,然後再安裝MySQL-8.0.13-winx64;
在這裏插入圖片描述

(2)Windows 7操作系統:

(3)MySQL-8.0.13-winx64:

(4)Navicat Premium 12:數據庫管理工具。

三、實驗內容

1、數據準備。

2、查看索引的使用情況 。

3、索引優化的相關概念。

4、索引優化的規則。

四、實驗步驟及結果(包含簡要的實驗步驟流程、結論陳述,可附頁)

(一)數據準備

1、新建表結構

-- 時間: 0s
SET FOREIGN_KEY_CHECKS=0;

-- 時間: 0s
DROP TABLE IF EXISTS `user_account`;

-- 時間: 0.054s
CREATE TABLE `user_account` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(50) DEFAULT NULL COMMENT '賬號',
  `password` varchar(50) DEFAULT NULL COMMENT '密碼',
  `salt` int(11) DEFAULT 0 COMMENT '鹽值',
  `sort` int(11) DEFAULT 0 COMMENT '排序',
  `state` int(1) DEFAULT '1' COMMENT '狀態:0無效1有效',
  `remark` VARCHAR(100) DEFAULT null COMMENT '備註',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=COMPACT;

-- 時間: 0s
SET FOREIGN_KEY_CHECKS=1;

2、檢查

(1)表結構
desc user_account ;

MySQL-5.6:
在這裏插入圖片描述

說明:

MySQL-8.0:
在這裏插入圖片描述

說明:

(2)索引
show index from user_account;

MySQL-5.6:
在這裏插入圖片描述

MySQL-8.0:
在這裏插入圖片描述

(3)表信息
show table status from test where name='user_account';

MySQL-5.6:

在這裏插入圖片描述

MySQL-8.0
在這裏插入圖片描述

4、插入基礎數據

-- Affected rows: 1 時間: 0.521s 
INSERT INTO `user_account` VALUES (null, '1', '1', 1, 1, 1, '備註');
-- Affected rows: 1 時間: 0.018s
INSERT INTO `user_account` VALUES (null, '2', '2', 2, 2, 1, '備註');
-- Affected rows: 1 時間: 0.016s
INSERT INTO `user_account` VALUES (null, '3', '3', 3, 3, 1, '備註');
-- Affected rows: 1 時間: 0.051s
INSERT INTO `user_account` VALUES (null, '4', '4', 4, 4, 1, '備註');
-- Affected rows: 1 時間: 0.017s
INSERT INTO `user_account` VALUES (null, '5', '5', 5, 5, 0, '備註');

5、插入千萬級別數據

-- ------------------ MySQL-5.6
-- Affected rows: 5        時間: 0.001s   
-- Affected rows: 10       時間: 0s       
-- Affected rows: 20       時間: 0.001s   
-- Affected rows: 40       時間: 0s      
-- Affected rows: 80       時間: 0.001s   
-- Affected rows: 160      時間: 0.001s
-- Affected rows: 320      時間: 0.003s
-- Affected rows: 640      時間: 0.003s
-- Affected rows: 1280     時間: 0.007s
-- Affected rows: 2560     時間: 0.015s
-- Affected rows: 5120     時間: 0.024s
-- Affected rows: 10240    時間: 0.044s
-- Affected rows: 20480    時間: 0.077s
-- Affected rows: 40960    時間: 0.181s
-- Affected rows: 81920    時間: 0.421s
-- Affected rows: 163840   時間: 0.74s
-- Affected rows: 327680   時間: 1.592s
-- Affected rows: 655360   時間: 2.406s
-- Affected rows: 1310720  時間: 5.578s
-- Affected rows: 2621440  時間: 9.642s
-- Affected rows: 5242880  時間: 17.658s
-- ------------------ MySQL-5.6

-- ------------------ MySQL-8.0
-- Affected rows: 5       時間: 0.066s
-- Affected rows: 10      時間: 0.04s
-- Affected rows: 20      時間: 0.056s
-- Affected rows: 40      時間: 0.025s
-- Affected rows: 80      時間: 0.018s
-- Affected rows: 160     時間: 0.014s
-- Affected rows: 320     時間: 0.095s
-- Affected rows: 640     時間: 0.077s
-- Affected rows: 1280    時間: 0.094s
-- Affected rows: 2560    時間: 0.304s
-- Affected rows: 5120    時間: 0.302s
-- Affected rows: 10240   時間: 0.959s
-- Affected rows: 20480   時間: 0.677s
-- Affected rows: 40960   時間: 1.481s
-- Affected rows: 81920   時間: 4.912s
-- Affected rows: 163840  時間: 6.598s
-- Affected rows: 327680  時間: 7.989s
-- Affected rows: 655360  時間: 11.656s
-- Affected rows: 1310720 時間: 16.586s
-- Affected rows: 2621440 時間: 34.713s
-- Affected rows: 5242880 時間: 86.015s
-- ------------------ MySQL-8.0
-- 第一步:執行21次,數據量大概爲10485760,爲千萬級別
INSERT into user_account SELECT null,t.username,t.`password`,t.salt,t.sort,t.state,t.remark FROM `user_account` t; 

-- MySQL-5.6:數量:10485760   第一次查詢時間: 12.48s   第二次查詢時間: 1.968s   第三次查詢時間: 1.968s
-- MySQL-8.0:數量:10485760   第一次查詢時間: 9.837s   第二次查詢時間: 5.44s    第三次查詢時間: 3.592s 時間: 2.626s 時間: 2.634s 時間: 2.656s
SELECT count(*) FROM user_account t ;

-- 第二步:更新username、password、salt和sort
-- MySQL-5.6:Affected rows: 10485755 時間: 125.239s
-- MySQL-8.0:Affected rows: 10485755 時間: 479.257s
update user_account set `username` = id,`password` = id,`salt`=id,`sort`=id ;

-- 第三步:更新remark
-- MySQL-5.6:Affected rows: 943384   時間: 55.905s
-- MySQL-8.0:Affected rows: 943384   時間: 35.478s
update user_account set remark = null where id < 1139965;

-- MySQL-5.6:Affected rows: 1393115  時間: 60.492s
-- MySQL-8.0:Affected rows: 1393115  時間: 31.097s
update user_account set remark = null where id > 9485760;

SELECT * FROM user_account t limit 100;

6、索引建立與刪除SQL

後面的實驗環節會經常用到

ALTER TABLE user_account ADD INDEX index_user_account_username (username);
ALTER TABLE user_account ADD INDEX index_user_account_salt (salt);
ALTER TABLE user_account ADD INDEX index_user_account_sort (sort);
ALTER TABLE user_account ADD INDEX index_user_account_state (state);
ALTER TABLE user_account ADD INDEX index_user_account_remark (remark);
ALTER TABLE user_account ADD INDEX index_user_account_username_password (username,password);

drop INDEX index_user_account_username on user_account ;
drop INDEX index_user_account_salt on user_account ;
drop INDEX index_user_account_sort on user_account ;
drop INDEX index_user_account_state on user_account ;
drop INDEX index_user_account_remark on user_account ;
drop INDEX index_user_account_username_password on user_account ;

SHOW INDEX FROM user_account;

(二)概念:基數

1、準備

(1)索引準備

-- MySQL-5.6:時間: 32.383s   時間: 25.747s
-- MySQL-8.0:時間: 60.349s
ALTER TABLE user_account ADD INDEX index_user_account_username (username);
-- MySQL-5.6:時間: 55.055s   時間: 19.067s
-- MySQL-8.0:時間: 51.075s
ALTER TABLE user_account ADD INDEX index_user_account_state (state);

(2)user_account表索引詳情:

show index from user_account;

MySQL-5.6:
在這裏插入圖片描述

MySQL-8.0:
在這裏插入圖片描述

2、含義

單個列唯一鍵(distict_keys)的數量叫做基數。執行以下SQL,結果如下圖所示:

-- MySQL-5.6:時間: 90.698s   時間: 26.843s
-- MySQL-8.0:時間: 79.946s   時間: 33.241s
SELECT COUNT(*),COUNT(DISTINCT username),COUNT(DISTINCT state) FROM user_account;

MySQL-5.6 and MySQL-8.0:
在這裏插入圖片描述

user_account表的總行數是10485760,username列的基數是10485760,state列的基數是2,說明state列裏面有大量重複值,username列的基數等於總行數,說明 username列沒有重複值,相當於主鍵。

3、測試

(1)先計算數量

-- MySQL-5.6:時間: 36.815s   時間: 1.649s
-- MySQL-8.0:時間: 10.367s
SELECT count(*) FROM user_account;
-- MySQL-5.6:時間: 6.979s
-- MySQL-8.0:第一次時間: 6.306s   第二次時間: 1.721s   第三次時間: 1.828s
SELECT count(*) FROM user_account t where t.state = 1;
-- MySQL-5.6:時間: 7.02s
-- MySQL-8.0:時間: 0.01s
SELECT count(*) FROM user_account t where t.username = '1';

總記錄數:10485760

state = 1的記錄數:8388608

username = '1’的記錄數:1

(2)對應的數據比例

比較項 基數 總數 比例
state = 1 4 5 8388608/10485760*100%=80%
username = ‘1’ 1 5 1/5*100%=20%

(3)測試點:2個

現在問題來了,假設state和username 列都有索引,那麼以下這兩個查詢

SELECT * FROM user_account t where t.state = 1;
SELECT * FROM user_account t where t.username = '1';

都能命中索引嗎?

第一個,where t.state = 1

EXPLAIN  SELECT * FROM user_account t where t.state = 1;

當數據量很大時,會命中索引。如數據量很大,即使返回數據比例爲80%,但還是走了索引。

MySQL-5.6:
在這裏插入圖片描述

MySQL-8.0:
在這裏插入圖片描述

當數據量很小時,沒有命中索引。如當總記錄數爲5,where t.state = 1返回4條記錄時,沒有命中索引。

MySQL-5.6:
在這裏插入圖片描述

注意:

extended:執行Mysql的explain 的輸出會比單純的explain多一列filtered(MySQL 5.7缺省就會輸出filtered),它指返回結果的行佔需要讀到的行(rows列的值)的百分比。filtered是個非常有用的值。如這裏的80指的是80%。

第二個,where t.username = ‘1’

EXPLAIN  SELECT * FROM user_account t where t.username = '1';

MySQL-5.6:
在這裏插入圖片描述

MySQL-8.0:
在這裏插入圖片描述

命中了索引index_user_account_username,因爲走索引直接就能找到要查詢的記錄,所以filtered的值爲100。這裏的100指的是100%。

4、小結

經測試,返回表中 8.805714%至10.871553% 的數據會走索引,返回超過這個比例數據就使用全表掃描。

估計這個比例只是一個大概的範圍,並不是絕對的比例。

詳見(五)索引優化的規則:8、範圍條件查詢可以命中索引

(四)查看索引的使用情況

有些時候雖然數據庫有索引,但是並不被優化器選擇使用。

SHOW STATUS LIKE 'Handler_read%';

MySQL-5.6:
在這裏插入圖片描述

MySQL-8.0:
在這裏插入圖片描述

參數名 說明
Handler_read_key 如果索引正在工作,Handler_read_key的值將很高。
Handler_read_rnd_next 數據文件中讀取下一行的請求數。如果正在進行大量的表掃描,值將較高,則說明索引利用不理想。

(五)索引優化的規則:16條

1、返回數據的比例

返回數據的比例是重要的指標,比例越低越容易命中索引。

本實驗測試的數據表明,大概爲8%。

2、前導模糊查詢

(1)索引準備

由於當前已經對username建立了索引,故本測試點無需再建立索引。

(2)user_account表索引詳情:

show index from user_account;

MySQL-5.6:
在這裏插入圖片描述

MySQL-8.0:
在這裏插入圖片描述

疑問:爲什麼state的Cardinality爲1?

(2)測試點:2個

第一,前導模糊查詢不能命中索引:

EXPLAIN  SELECT * FROM user_account t where t.username like '%10000%';

MySQL-5.6:
在這裏插入圖片描述

MySQL-8.0:
在這裏插入圖片描述

第二,非前導模糊查詢則可以使用索引,可優化爲使用非前導模糊查詢:

EXPLAIN  SELECT * FROM user_account t where t.username like '10000%';

MySQL-5.6:
在這裏插入圖片描述

MySQL-8.0:
在這裏插入圖片描述

3、隱式轉換

數據類型出現隱式轉換的時候不會命中索引,特別是當列類型是字符串,一定要將字符常量值用引號引起來

(1)索引準備

由於當前已經對username建立了索引,故本測試點無需再建立索引。

(2)user_account表索引詳情:

show index from user_account;

MySQL-5.6:
在這裏插入圖片描述

MySQL-8.0:
在這裏插入圖片描述

(2)測試點:2個

第一,出現隱式轉換的時候

EXPLAIN  SELECT * FROM user_account t where t.username  = 1;

MySQL-5.6:
在這裏插入圖片描述

MySQL-8.0:
在這裏插入圖片描述

第二,沒有出現隱式轉換的時候

EXPLAIN  SELECT * FROM user_account t where t.username  = '1';

MySQL-5.6:
在這裏插入圖片描述

MySQL-8.0:
在這裏插入圖片描述

4、最左原則

複合索引的情況下,查詢條件不包含索引列最左邊部分(不滿足最左原則),不會命中符合索引。

(1)索引準備

先暫時刪除username和password的索引,接着爲username、password列創建複合索引。

-- MySQL-5.6:時間: 0.088s
-- MySQL-8.0:時間: 0.302s
drop INDEX index_user_account_username on user_account ;
-- MySQL-5.6:時間: 0.015s
-- MySQL-8.0:時間: 0.042s
drop INDEX index_user_account_state on user_account ;

-- MySQL-5.6:時間: 66.24s
-- MySQL-8.0:時間: 64.255s
ALTER TABLE user_account ADD INDEX index_user_account_username_password (username,password);

(2)user_account表索引詳情:

show index from user_account;

MySQL-5.6:
在這裏插入圖片描述

MySQL-8.0:
在這裏插入圖片描述

(3)測試點:3個

第一,根據最左原則,where條件的username,是複合索引index_user_account_username_password中的最左列,所以可以命中:

EXPLAIN  SELECT * FROM user_account WHERE username='2' AND state=1;

MySQL-5.6:
在這裏插入圖片描述

MySQL-8.0:
在這裏插入圖片描述

第二,注意:最左原則並不是說是查詢條件的順序。現在交換一下查詢條件中的username和state:

EXPLAIN  SELECT * FROM user_account WHERE state=1 AND username='2';

MySQL-5.6:
在這裏插入圖片描述

MySQL-8.0:
在這裏插入圖片描述

第三,而是查詢條件中是否包含索引最左列字段:下面沒有包含最左列username,只要password,所以不會命中。

EXPLAIN  SELECT * FROM user_account WHERE password = '2' AND state=1;

MySQL-5.6:
在這裏插入圖片描述

MySQL-8.0:
在這裏插入圖片描述

5、union、in、or

union、in、or 都能夠命中索引,建議使用 in。

(1)索引準備

-- MySQL-5.6:時間: 0.051s
-- MySQL-8.0:時間: 0.105s
drop INDEX index_user_account_username_password on user_account ;

-- MySQL-5.6:時間: 24.258s
-- MySQL-8.0:時間: 63.82s
ALTER TABLE user_account ADD INDEX index_user_account_username (username);

(2)user_account表索引詳情:

show index from user_account;

MySQL-5.6:
在這裏插入圖片描述

MySQL-8.0:
在這裏插入圖片描述

(3)測試點

第一,union:

EXPLAIN 
SELECT * FROM user_account WHERE username = '1'
UNION ALL
SELECT * FROM user_account WHERE username = '2';

MySQL-5.6:
在這裏插入圖片描述

MySQL-8.0:
在這裏插入圖片描述

第二,in:

EXPLAIN  SELECT * FROM user_account WHERE username IN ('1','2');

MySQL-5.6:
在這裏插入圖片描述

MySQL-8.0:
在這裏插入圖片描述

這裏看到會走索引。

但是,當數據量很小時,有可能不走索引。因爲即使有索引,in後面的值超過一定個數後,就會分析消耗,最後如果判斷出消耗時間比走全表掃描還多,則就不走索引。

第三,or:

EXPLAIN  SELECT * FROM user_account WHERE username ='1' or username ='2';

MySQL-5.6:
在這裏插入圖片描述

MySQL-8.0:
在這裏插入圖片描述

查詢的CPU消耗:or > in >union

6、or前的條件中列有索引,而後面的列中沒有索引

用or分割開的條件,如果or前的條件中列有索引,而後面的列中沒有索引,那麼涉及到的索引都不會被用到。

(1)索引準備

由於當前已經對username建立了索引,故本測試點無需再建立索引。

(2)user_account表索引詳情:

show index from user_account;

MySQL-5.6:
在這裏插入圖片描述

MySQL-8.0:
在這裏插入圖片描述

(3)測試點

第一,or前的條件中列有索引,or後面的條件列中沒有索引

EXPLAIN  SELECT * FROM user_account WHERE username ='1' or state = 1;

MySQL-5.6:
在這裏插入圖片描述

MySQL-8.0:
在這裏插入圖片描述

因爲or後面的條件列中沒有索引,那麼後面的查詢肯定要走全表掃描,

在存在全表掃描的情況下,就沒有必要多一次索引掃描增加IO訪問。

7、負向條件查詢:!=、<>、not in、not exists、not like

負向條件查詢不能使用索引,可以優化爲 in 查詢。

(1)索引準備

由於當前已經對username建立了索引,故本測試點無需再建立索引。

(2)user_account表索引詳情:

show index from user_account;

MySQL-5.6:
在這裏插入圖片描述

MySQL-8.0:
在這裏插入圖片描述

(3)測試點

負向條件有:!=、<>、not in、not exists、not like 等。

第一,負向條件不能命中緩存:

EXPLAIN  SELECT * FROM user_account WHERE username !='1' AND username != '2';

MySQL-5.6:
在這裏插入圖片描述

MySQL-8.0:
在這裏插入圖片描述

第二,可以優化爲 in 查詢,但是前提是區分度要高,返回數據的比例在30%以內:

EXPLAIN  SELECT * FROM user_account WHERE username IN ('1','3','4');

MySQL-5.6:
在這裏插入圖片描述

MySQL-8.0:
在這裏插入圖片描述

8、範圍條件查詢:<、<=、>、>=、between

範圍條件查詢可以命中索引

(1)索引準備

-- MySQL-5.6:時間: 0.057s
-- MySQL-8.0:時間: 0.132s
drop INDEX index_user_account_username on user_account ;

-- MySQL-5.6:時間: 50.27s
-- MySQL-8.0:時間: 50.981s
ALTER TABLE user_account ADD INDEX index_user_account_salt (salt);
-- MySQL-5.6:時間: 51.675s
-- MySQL-8.0:時間: 50.249s
ALTER TABLE user_account ADD INDEX index_user_account_sort (sort);

(2)user表索引詳情:

SHOW INDEX FROM user_account;

MySQL-5.6:
在這裏插入圖片描述

MySQL-8.0:
在這裏插入圖片描述

(3)測試點:6個

範圍條件有:<、<=、>、>=、between等

第一,範圍條件查詢可以命中索引:

EXPLAIN  SELECT * FROM user_account WHERE sort < 699433;

MySQL-5.6:
在這裏插入圖片描述

MySQL-8.0:
在這裏插入圖片描述

第二,但是當數據量達到一定程度時,就不會走索引,而是會全表掃描了。

EXPLAIN  SELECT * FROM user_account WHERE sort < 699434;

MySQL-5.6:
在這裏插入圖片描述

MySQL-8.0:
在這裏插入圖片描述

第三,使用<=時,數量上又略有不同。

EXPLAIN  SELECT * FROM user_account WHERE sort <= 699431;

MySQL-5.6:
在這裏插入圖片描述

MySQL-8.0:
在這裏插入圖片描述

第四,但是當數據量達到一定程度時,就不會走索引,而是會全表掃描了。

EXPLAIN  SELECT * FROM user_account WHERE sort <= 699432;

MySQL-5.6:
在這裏插入圖片描述

MySQL-8.0:
在這裏插入圖片描述

第五,範圍列可以用到索引(聯合索引必須是最左前綴),但是範圍列後面的列無法用到索引,索引最多用於一個範圍列,如果查詢條件中有兩個範圍列則無法全用到索引。也就是說,只能用其中一個索引,而且是選擇代價最小的作爲索引:

sort、salt都是索引。

EXPLAIN  SELECT * FROM user_account WHERE salt < 100;

MySQL-5.6:
在這裏插入圖片描述

MySQL-8.0:
在這裏插入圖片描述

選擇代價最小的作爲索引,salt返回數量最少,代價最小。

EXPLAIN  SELECT * FROM user_account WHERE sort < 699433 AND salt < 100;

MySQL-5.6:
在這裏插入圖片描述

MySQL-8.0:
在這裏插入圖片描述

即使交換順序,也是salt。

EXPLAIN  SELECT * FROM user_account WHERE salt < 100 AND sort < 699433;

MySQL-5.6:
在這裏插入圖片描述

MySQL-8.0:
在這裏插入圖片描述

第六,如果是範圍查詢和等值查詢同時存在,優先匹配等值查詢列的索引:

EXPLAIN  SELECT * FROM user_account WHERE salt < 100 AND sort = 1139965;

MySQL-5.6:
在這裏插入圖片描述

MySQL-8.0:
在這裏插入圖片描述

9、數據庫執行計算

(1)索引準備

由於當前已經對sort建立了索引,故本測試點無需再建立索引。

(2)user_account表索引詳情:

show index from user_account;

MySQL-5.6:
在這裏插入圖片描述

MySQL-8.0:
在這裏插入圖片描述

數據庫執行計算不會命中索引

(3)測試點

第一,執行計算:sort < 1000

EXPLAIN  SELECT * FROM user_account WHERE sort < 1000;

MySQL-5.6:
在這裏插入圖片描述

MySQL-8.0:
在這裏插入圖片描述

第二,執行計算:sort+1 < 1000

EXPLAIN  SELECT * FROM user_account WHERE sort+1 < 1000;

MySQL-5.6:
在這裏插入圖片描述

MySQL-8.0:
在這裏插入圖片描述

計算邏輯應該儘量放到業務層處理,節省數據庫的 CPU的同時最大限度的命中索引。

10、利用覆蓋索引進行查詢,避免回表

被查詢的列,數據能從索引中取得,而不用通過行定位符 row-locator 再到 row 上獲取,即“被查詢列要被所建的索引覆蓋”,這能夠加速查詢速度。

(1)索引準備

-- MySQL-5.6:時間: 0.03s
-- MySQL-8.0:時間: 0.105s
drop INDEX index_user_account_salt on user_account ;
-- MySQL-5.6:時間: 0.026s
-- MySQL-8.0:時間: 0.175s
drop INDEX index_user_account_sort on user_account ;

-- MySQL-5.6:時間: 65.232s
-- MySQL-8.0:時間: 67.54s
ALTER TABLE user_account ADD INDEX index_user_account_username (username);

(2)user表索引詳情:

SHOW INDEX FROM user_account;

MySQL-5.6:
在這裏插入圖片描述

MySQL-8.0:
在這裏插入圖片描述

(3)測試點

第一,因爲username字段是索引列,所以直接從索引中就可以獲取值,不必回表查詢:

EXPLAIN  SELECT username FROM user_account WHERE username = '1000';

MySQL-5.6:
在這裏插入圖片描述

MySQL-8.0:
在這裏插入圖片描述

第二,當查詢其他列時,就需要回表查詢,這也是爲什麼要避免SELECT *的原因之一:

EXPLAIN  SELECT * FROM user_account WHERE username = '1000';

MySQL-5.6:
在這裏插入圖片描述

MySQL-8.0:
在這裏插入圖片描述

11、建立索引的列,不允許爲 null

(1)索引準備

-- MySQL-5.6:時間: 0.046s
-- MySQL-8.0:時間: 0.152s
drop INDEX index_user_account_username on user_account ;

-- MySQL-5.6:時間: 60.403s
-- MySQL-8.0:時間: 52.006s
ALTER TABLE user_account ADD INDEX index_user_account_remark (remark);

(2)user表索引詳情:

SHOW INDEX FROM user_account;

MySQL-5.6:
在這裏插入圖片描述

MySQL-8.0:
在這裏插入圖片描述

(3)測試點

單列索引不存 null 值,複合索引不存全爲 null 的值,如果列允許爲 null,可能會得到“不符合預期”的結果集,所以,請使用 not null 約束以及默認值。

第一,IS NULL可以命中索引:

EXPLAIN  SELECT * FROM user_account where remark IS NULL;

MySQL-5.6:
在這裏插入圖片描述

MySQL-8.0:
在這裏插入圖片描述

第二,IS NOT NULL不能命中索引:

EXPLAIN  SELECT * FROM user_account where remark IS NOT NULL;

MySQL-5.6:
在這裏插入圖片描述

MySQL-8.0:
在這裏插入圖片描述

雖然IS NULL可以命中索引,但是NULL本身就不是一種好的數據庫設計,應該使用NOT NULL 約束以及默認值

12、更新十分頻繁的字段上不宜建立索引

因爲更新操作會變更B+樹,重建索引。這個過程是十分消耗數據庫性能的。

13、區分度不大的字段上不宜建立索引

類似於性別這種區分度不大的字段,建立索引的意義不大。因爲不能有效過濾數據,性能和全表掃描相當。

另外返回數據的比例在30%以外的情況下,優化器不會選擇使用索引。

疑問:比例30%從哪裏來?本實驗測試的數據表明,大概爲8%。

14、業務上具有唯一特性的字段,即使是多個字段的組合,也必須建成唯一索引

雖然唯一索引會影響insert速度,但是對於查詢的速度提升是非常明顯的。另外,即使在應用層做了非常完善的校驗控制,只要沒有唯一索引,在併發的情況下,依然有髒數據產生。

15、多表關聯時,要保證關聯字段上一定有索引

16、創建索引時避免以下錯誤觀念

  • 索引越多越好,認爲一個查詢就需要建一個索引。
  • 寧缺勿濫,認爲索引會消耗空間、嚴重拖慢更新和新增速度。
  • 抵制唯一索引,認爲業務的唯一性一律需要在應用層通過“先查後插”方式解決。
  • 過早優化,在不瞭解系統的情況下就開始優化。

五、實驗總結(包括心得體會、問題回答及實驗改進意見)

1、通過本次實驗,提高了SQL性能優化的意識。

2、瞭解了索引對SQL的性能影響很大。

3、學會了使用EXPLAIN命令分析一下SQL

4、理解了基數、回表的概念

5、掌握了索引優化的規則。

6、通過比較了MySQL-5.6和MySQL-8.0,懂得了要學會一門技術,除了學會基本原理,還要經常關注該技術的發展,瞭解不同版本的差異。

六、參考

《深入淺出MySQL》

MySQL——索引優化實戰

發佈了33 篇原創文章 · 獲贊 15 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章