MySQL進階篇(03):合理的使用索引結構和查詢

本文源碼:GitHub·點這裏 || GitEE·點這裏mysql

1、高性能索引

一、查詢性能問題

在MySQL使用的過程當中,所謂的性能問題,在大部分的場景下都是指查詢的性能,致使查詢緩慢的根本緣由是數據量的不斷變大,解決查詢性能的最多見手段是:針對查詢的業務場景,設計合理的索引結構。git

二、索引使用原則

索引的使用並非越多越好,而是針對業務下的查詢場景,不斷的改進和優化,例如電商系統中用戶訂單的場景,假設存在以下表結構:github

CREATE TABLE `ds_user` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵id',
  `user_name` varchar(20) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用戶表';

CREATE TABLE `ds_order` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵id',
  `user_id` int(11) NOT NULL COMMENT '用戶ID',
  `order_no` varchar(60) NOT NULL COMMENT '訂單號',
  `product_name` varchar(50) DEFAULT NULL COMMENT '產品名稱',
  `number` int(11) DEFAULT '1' COMMENT '個數',
  `unit_price` decimal(10,2) DEFAULT '0.00' COMMENT '單價',
  `total_price` decimal(10,2) DEFAULT '0.00' COMMENT '總價',
  `order_state` int(2) DEFAULT '1' COMMENT '1待支付,2已支付,3已發貨,4已簽收',
  `order_remark` varchar(50) DEFAULT NULL COMMENT '訂單備註',
  `create_time` datetime DEFAULT NULL COMMENT '建立時間',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='訂單表';

用戶和訂單管理表,在電商的業務中很常見,能夠經過對該業務分析,看看經常使用的索引結構:sql

用戶方:數據庫

  • 基於用戶的查詢,多數是基於用戶ID(user_id);
  • 基於訂單號(order_no),查看物流的信息;

運營方:服務器

  • 基於時間段的流水明細(create_time)或排序;
  • 基於訂單狀態的篩選(order_state)和統計;
  • 基於產品(product_name)的數據統計分析;

這樣一個流程分析走下來,便可以在開發初期,肯定哪些結構是查詢必須用到的,預先作好索引結構,避免數據量龐大到影響性能時再去考慮使用索引。架構

有些時候會考慮放棄一些查詢條件,例如基於產品名稱的數據統計,走定時任務的方式,用來緩解表的查詢壓力,處理的方式是多樣的。併發

優秀的索引設計,都是創建在對業務數據的理解上,考慮業務數據的查詢方式,提升查詢效率。函數

2、索引建立

一、單列索引

單列索引,即索引創建在表的一個字段上,一個表能夠有多個單列索引,使用起來相對比較簡單:性能

CREATE INDEX user_id_index ON ds_order(user_id) USING BTREE;

主鍵索引,或者上述的user_id_index都是單列索引。

業務場景:基於用戶本身對訂單查詢,和管理系統,訂單和用戶的關聯查詢,因此訂單表的user_id須要一個索引。

二、組合索引

組合索引包含兩個或兩個以上的列,組合索引相比單列索引複雜不少,如何創建組合索引,和業務關聯度很是高,在使用組合索引時,還須要考慮查詢條件的順序。

CREATE INDEX state_create_time_index ON `ds_order`(`create_time`,`order_state`);

如上就是組合索引,實際包含的是2個索引 (create_time) (create_time,order_state),這樣查詢就涉及到最左前綴的原則,必須按照順序來查詢,這裏下面詳說。

業務場景:首先單說這裏組合索引,在業務開發中,常見訂單狀態的統計,基於統計結果作運營分析,另外就是在運營系統中,基於建立時間段的篩選條件是默認存在的,避免所有數據實時掃描;一些其餘的常見查詢也都是條件加時間段的查詢模式。

三、前綴索引

若是須要加索引的列是很長的字符串,那麼索引會變的龐大臃腫,起到的效果可能並非很明顯。這時候能夠截取列的前面一部分,建立索引,節省空間,這樣可能會出現索引的選擇性降低,即基於前綴索引查詢出的類似數據可能不少:

ALTER TABLE ds_order ADD KEY (order_no(30)) ;

這裏因爲訂單號太長,因此選擇前面30位做爲前綴索引,用做訂單號的查詢,固然這裏涉及到一個很是經典的業務場景,訂單號機制。

業務場景:前綴索引一個典型的應用場景就是處理訂單號,一個看似很長的訂單號,其實包含的信息很是多:

  • 時間點:就是訂單生成的時間,年月日時分秒;
  • 標識位:即一個惟一的UID,保證訂全單號惟一;
  • 埋點一:在不少業務中,在訂單號記錄產品類目;
  • 埋點二:一般會標識產品屬性,例如顏色,口味等;
  • 錯位符:防止訂單號被分析,會隨機一段錯位符號;

如此一段分析下來,實際訂單號是很是長的,因此須要引入前綴索引機制,前綴索引指望使用的索引長度能夠篩選整個列的基數,例如上面的訂單號:

  • 大部分業務基於時間節點篩選足夠,即索引長度14位;
  • 若是是併發業務,不少時間節點相同,則索引長度是時間點+標識位;

注意:若是業務容許的狀況下,通常要求前綴索引的長度有惟一性,例如上面的時間和標示位。

四、其餘索引

例如全文索引等,這些用到的場景很少,若是數據龐大,又須要檢索等,一般會選擇強大的搜索中間件來處理。顯式惟一索引,這種也會在程序上作規避,避免不友好的異常被拋出。

3、索引查詢

如何建立最優的索引,是一件不容易的事情,一樣在查詢的時候,是否使用索引也是一件難度極大的事情,經驗之談:多數是性能問題暴露的時候,纔會回頭審視查詢的SQL語句,針對性能問題,作相應的查詢優化。

一、單列查詢

這裏直接查詢主鍵索引,MySQL的主鍵通常選擇自增,因此速度很是快。

EXPLAIN SELECT * FROM ds_order WHERE id=2;
EXPLAIN SELECT * FROM ds_order WHERE id=1+1;
EXPLAIN SELECT * FROM ds_order WHERE id+1=1;

這裏,id=2,id=1+1,MySQL均可以自動解析,可是id+1是在索引列上執行運算,直接致使主鍵索引失效。這裏有一個基本策略,若是非要在單列索引上作操做,能夠將該邏輯放在程序中,到MySQL層面,SQL語句越乾淨利落越好。

二、前綴索引查詢

前綴索引的查詢,能夠基於Like對特定長度篩選,或者全訂單號查詢。

EXPLAIN SELECT * FROM ds_order WHERE order_no LIKE '202008011314158723628732871625%';
EXPLAIN SELECT * FROM ds_order WHERE order_no='20200801131415872362873287162572367';

三、組合索引查詢

查詢最麻煩的就是組合索引,或者說查詢條件組合起來,都使用了索引:

EXPLAIN SELECT * FROM ds_order 
WHERE create_time>'2020-08-01 00:00:00' AND order_state='1';

上述基於組合索引中列的順序,使用了組合索引:state_create_time_index。

EXPLAIN SELECT * FROM ds_order WHERE create_time>'2020-08-01 00:00:00';

上述只使用create_time列,也一樣使用了索引結構。

EXPLAIN SELECT * FROM ds_order WHERE order_state='1';

上述若是隻使用order_state條件,則結果顯示全表掃描。

EXPLAIN SELECT * FROM ds_order 
WHERE create_time>'2020-08-01 00:00:00' AND order_no LIKE '20200801%';

上述則基於組合索引的create_time列和單列索引order_no保證查詢條件都使用了索引。

經過上面幾個查詢案例,索引組合索引使用的注意事項以下:

  • 組合索引必須按索引最左列開始查詢;
  • 不能跳過組合字段查詢,這樣沒法使用索引;

4、索引其餘說明

一、索引的優勢

  • 基於註解或惟一索引保證數據庫表中數據的惟一性;
  • 索引經過減小掃描表的行數提升查詢的效率;

二、索引的缺點

  • 建立索引和維護索引,會耗費空間和實際;
  • 查詢之外的操做增刪改等,都須要動態維護索引;

三、索引使用總結

索引機制在MySQL中真的很是複雜,非專業的DBA(就是指開發人員),基本要熟練常見的索引結構,待過兩年所謂的大廠,每一個版本開發涉及的核心表SQL都是有專業DBA驗收,複雜的查詢都是提交需求,DBA直接輸出查詢SQL,固然在通常公司是沒有DBA,須要開發在開發的過程當中不斷的思考,逐步優化,這須要對業務數據有必定的敏感度,對核心接口有執行監控,當發現稍微出現耗時狀況,就能夠不斷優化,這個積累是個枯燥和進步的過程。

5、源代碼地址

GitHub·地址
https://github.com/cicadasmile/mysql-data-base
GitEE·地址
https://gitee.com/cicadasmile/mysql-data-base

推薦閱讀:MySQL數據庫系列

序號 文章標題
01 MySQL基礎:經典實用查詢案例,總結整理
02 MySQL基礎:從五個維度出發,審視表結構設計
03 MySQL基礎:系統和自定義函數總結,觸發器使用詳解
04 MySQL基礎:存儲過程和視圖,用法和特性詳解
05 MySQL基礎:邏輯架構圖解和InnoDB存儲引擎詳解
06 MySQL基礎:事務管理,鎖機制案例詳解
07 MySQL基礎:用戶和權限管理,日誌體系簡介
01 MySQL進階:基於多個維度,分析服務器性能
02 MySQL進階:索引體系劃分,B-Tree結構說明
相關文章
相關標籤/搜索