當db的量達到必定數量級以後,每次進行全表掃描效率就會很低,所以一個常見的方案是創建一些必要的索引做爲優化手段,那麼問題就來了:mysql
MySQL官方對索引的定義爲:索引是幫助MySQL高效獲取數據的數據結構。簡而言之,索引是數據結構git
單來講就是一種爲磁盤或者其餘存儲設備而設計的一種平衡二叉樹,在B+tree中全部記錄都按照key的大小存放在葉子結點上,各葉子結點直接用指針鏈接github
二叉樹的規則是父節點大於左孩子節點,小於右孩子節點算法
首先是一個二叉樹,可是要求任意一個節點的左右孩子節點的高度差不大於1sql
首先是一個平衡二叉樹,可是又要求每一個葉子節點到根節點的距離相等數據庫
那麼B樹和B+樹的區別是什麼呢?緩存
mysql的InnnoDB引擎採用的B+樹,只有葉子節點存儲對應的數據列,有如下好處服務器
hash索引,相比較於B樹而言,不須要從根節點到葉子節點的遍歷,能夠一次定位到位置,查詢效率更高,但缺點也很明顯數據結構
InnoDB的數據文件自己就是索引文件,B+Tree的葉子節點上的data就是數據自己,key爲主鍵,非葉子節點存放<key,address>,address就是下一層的地址hexo
聚簇索引的結構圖:
非聚簇索引,葉子節點上的data是主鍵(即聚簇索引的主鍵,因此聚簇索引的key,不能過長)。爲何存放的主鍵,而不是記錄所在地址呢,理由至關簡單,由於記錄所在地址並不能保證必定不會變,但主鍵能夠保證
非聚簇索引結構圖:
從非彙集索引的結構上,能夠看出這種場景下的定位流程:
索引並非適用於任何狀況。對於中型、大型表適用。對於小型表全表掃描更高效。而對於特大型表,考慮」分區」技術
通常咱們在建立表的時候,須要指定primary key, 這樣就能夠肯定彙集索引了,那麼如何添加非彙集索引呢?
建立索引
-- 建立索引
create index `idx_img` on newuser(`img`);
-- 查看
show create table newuser\G;
複製代碼
輸出
show create table newuser\G
*************************** 1. row ***************************
Table: newuser
Create Table: CREATE TABLE `newuser` (
`userId` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '用戶id',
`username` varchar(30) DEFAULT '' COMMENT '用戶登陸名',
`nickname` varchar(30) NOT NULL DEFAULT '' COMMENT '用戶暱稱',
`password` varchar(50) DEFAULT '' COMMENT '用戶登陸密碼 & 密文根式',
`address` text COMMENT '用戶地址',
`email` varchar(50) NOT NULL DEFAULT '' COMMENT '用戶郵箱',
`phone` bigint(20) NOT NULL DEFAULT '0' COMMENT '用戶手機號',
`img` varchar(100) DEFAULT '' COMMENT '用戶頭像',
`extra` text,
`isDeleted` tinyint(1) unsigned NOT NULL DEFAULT '0',
`created` int(11) NOT NULL,
`updated` int(11) NOT NULL,
PRIMARY KEY (`userId`),
KEY `idx_username` (`username`),
KEY `idx_nickname` (`nickname`),
KEY `idx_email` (`email`),
KEY `idx_phone` (`phone`),
KEY `idx_img` (`img`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8
複製代碼
另外一種常見的添加索引方式
alter table newuser add index `idx_extra_img`(`isDeleted`, `img`);
-- 查看索引
show index from newuser;
複製代碼
輸出結果
+---------+------------+---------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+---------+------------+---------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| newuser | 0 | PRIMARY | 1 | userId | A | 3 | NULL | NULL | | BTREE | | |
| newuser | 1 | idx_username | 1 | username | A | 3 | NULL | NULL | YES | BTREE | | |
| newuser | 1 | idx_nickname | 1 | nickname | A | 3 | NULL | NULL | | BTREE | | |
| newuser | 1 | idx_email | 1 | email | A | 3 | NULL | NULL | | BTREE | | |
| newuser | 1 | idx_phone | 1 | phone | A | 3 | NULL | NULL | | BTREE | | |
| newuser | 1 | idx_img | 1 | img | A | 3 | NULL | NULL | YES | BTREE | | |
| newuser | 1 | idx_extra_img | 1 | isDeleted | A | 3 | NULL | NULL | | BTREE | | |
| newuser | 1 | idx_extra_img | 2 | img | A | 3 | NULL | NULL | YES | BTREE | | |
+---------+------------+---------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
複製代碼
刪除索引
drop index `idx_extra_img` on newuser;
drop index `idx_img` on newuser;
-- 查看索引
show index from newuser;
複製代碼
輸出
show index from newuser;
+---------+------------+--------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+---------+------------+--------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| newuser | 0 | PRIMARY | 1 | userId | A | 3 | NULL | NULL | | BTREE | | |
| newuser | 1 | idx_username | 1 | username | A | 3 | NULL | NULL | YES | BTREE | | |
| newuser | 1 | idx_nickname | 1 | nickname | A | 3 | NULL | NULL | | BTREE | | |
| newuser | 1 | idx_email | 1 | email | A | 3 | NULL | NULL | | BTREE | | |
| newuser | 1 | idx_phone | 1 | phone | A | 3 | NULL | NULL | | BTREE | | |
+---------+------------+--------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
複製代碼
強制走索引的一種方式
語法: select * from table force index(索引) where xxx
explain select * from newuser force index(PRIMARY) where userId not in (3, 2, 5);
-- +----+-------------+---------+-------+---------------+---------+---------+------+------+-------------+
-- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
-- +----+-------------+---------+-------+---------------+---------+---------+------+------+-------------+
-- | 1 | SIMPLE | newuser | range | PRIMARY | PRIMARY | 8 | NULL | 4 | Using where |
-- +----+-------------+---------+-------+---------------+---------+---------+------+------+-------------+
explain select * from newuser where userId not in (3, 2, 5);
-- +----+-------------+---------+------+---------------+------+---------+------+------+-------------+
-- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
-- +----+-------------+---------+------+---------------+------+---------+------+------+-------------+
-- | 1 | SIMPLE | newuser | ALL | PRIMARY | NULL | NULL | NULL | 3 | Using where |
-- +----+-------------+---------+------+---------------+------+---------+------+------+-------------+
複製代碼
當一個表內有多個索引時,如何判斷本身的sql是否走到了索引,走的是哪一個索引呢?
能夠經過 explain
關鍵字來進行輔助判斷,固然在實際寫sql時,咱們也有必要了解下索引匹配的規則,避免設置了一些冗餘的索引,或者寫出一些走不到索引的sql
測試的表結構以下
*************************** 1. row ***************************
Table: newuser
Create Table: CREATE TABLE `newuser` (
`userId` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '用戶id',
`username` varchar(30) DEFAULT '' COMMENT '用戶登陸名',
`nickname` varchar(30) NOT NULL DEFAULT '' COMMENT '用戶暱稱',
`password` varchar(50) DEFAULT '' COMMENT '用戶登陸密碼 & 密文根式',
`address` text COMMENT '用戶地址',
`email` varchar(50) NOT NULL DEFAULT '' COMMENT '用戶郵箱',
`phone` bigint(20) NOT NULL DEFAULT '0' COMMENT '用戶手機號',
`img` varchar(100) DEFAULT '' COMMENT '用戶頭像',
`extra` text,
`isDeleted` tinyint(1) unsigned NOT NULL DEFAULT '0',
`created` int(11) NOT NULL,
`updated` int(11) NOT NULL,
PRIMARY KEY (`userId`),
KEY `idx_username` (`username`),
KEY `idx_nickname_email_phone` (`nickname`,`email`,`phone`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8
複製代碼
這個主要是針對多列非聚簇索引而言,好比有下面這個索引idx_nickname_email_phone(nickname, email, phone)
, nickname 定義在email的前面,那麼下面這幾個語句對應的狀況是
-- 走索引
explain select * from newuser where nickname='小灰灰' and email='greywolf@xxx.com';
-- 1. 匹配nickname,能夠走索引
explain select * from newuser where nickname='小灰灰';
-- 輸出:
-- +----+-------------+---------+------+--------------------+--------------------+---------+-------+------+-----------------------+
-- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
-- +----+-------------+---------+------+--------------------+--------------------+---------+-------+------+-----------------------+
-- | 1 | SIMPLE | newuser | ref | idx_nickname_email | idx_nickname_email | 92 | const | 1 | Using index condition |
-- +----+-------------+---------+------+--------------------+--------------------+---------+-------+------+-----------------------+
-- 2. 雖然匹配了email, 可是不知足最左匹配,不走索引
explain select * from newuser where email='greywolf@xxx.com';
-- 輸出
-- +----+-------------+---------+------+---------------+------+---------+------+------+-------------+
-- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
-- +----+-------------+---------+------+---------------+------+---------+------+------+-------------+
-- | 1 | SIMPLE | newuser | ALL | NULL | NULL | NULL | NULL | 3 | Using where |
-- +----+-------------+---------+------+---------------+------+---------+------+------+-------------+
複製代碼
即對索引idx_nickname_email_phone(nickname, email, phone)
, 若是你的sql中,只有 nickname 和 phone, 那麼phone走不到索引,由於不能跳過中間的email走索引
如 >, <, between, like這種就是範圍查詢,下面的sql中,email 和phone都沒法走到索引,由於nickname使用了範圍查詢
select * from newuser where nickname like '小灰%' and email='greywolf@xxx.com' and phone=15971112301 limit 10;
複製代碼
-- 走不到索引
explain select * from newuser where userId+1=2 limit 1;
-- 輸出
-- +----+-------------+---------+------+---------------+------+---------+------+------+-------------+
-- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
-- +----+-------------+---------+------+---------------+------+---------+------+------+-------------+
-- | 1 | SIMPLE | newuser | ALL | NULL | NULL | NULL | NULL | 3 | Using where |
-- +----+-------------+---------+------+---------------+------+---------+------+------+-------------+
複製代碼
一般建議是使用一個sql來替代多個sql的查詢
固然若sql執行效率很低,或者出現delete等致使鎖表的操做時,也能夠採用多個sql,避免阻塞其餘sql
將關聯join儘可能放在應用中來作,儘可能執行小而簡單的的sql
如 limit 1000, 20
則會查詢出知足條件的1020條數據,而後將最後的20個返回,因此儘可能避免大翻頁查詢
須要將where、order by、limit 這些限制放入到每一個子查詢,才能重分提高效率。另外如非必須,儘可能使用Union all,由於union會給每一個子查詢的臨時表加入distinct,對每一個臨時表作惟一性檢查,效率較差。
-- 單位爲GB
SELECT CONCAT(ROUND(SUM(index_length)/(1024*1024*1024), 6), ' GB') AS 'Total Index Size'
FROM information_schema.TABLES WHERE table_schema LIKE 'databaseName';
複製代碼
SELECT CONCAT(ROUND(SUM(data_length)/(1024*1024*1024), 6), ' GB') AS 'Total Data Size'
FROM information_schema.TABLES WHERE table_schema LIKE 'databaseName';
複製代碼
SELECT CONCAT(table_schema,'.',table_name) AS 'Table Name',
table_rows AS 'Number of Rows',
CONCAT(ROUND(data_length/(1024*1024*1024),6),' G') AS 'Data Size',
CONCAT(ROUND(index_length/(1024*1024*1024),6),' G') AS 'Index Size' ,
CONCAT(ROUND((data_length+index_length)/(1024*1024*1024),6),' G') AS'Total'
FROM information_schema.TABLES
WHERE table_schema LIKE 'databaseName';
複製代碼
基於hexo + github pages搭建的我的博客,記錄全部學習和工做中的博文,歡迎你們前去逛逛
盡信書則不如,已上內容,純屬一家之言,因本人能力通常,見識有限,如發現bug或者有更好的建議,隨時歡迎批評指正