一、什麼是索引?
索引(index)是幫助MySQL高效獲取數據的數據結構。
二、索引的優劣勢?
優勢
1) 類似於書籍的目錄索引,提高數據檢索的效率,降低數據庫的IO成本。
2) 通過索引列對數據進行排序,降低數據排序的成本,降低CPU的消耗。
劣勢
1) 實際上索引也是一張表,該表中保存了主鍵與索引字段,並指向實體類的記錄,所以索引列也是要佔用空間的。
2) 雖然索引大大提高了查詢效率,同時卻也降低更新表的速度,如對錶進行INSERT、UPDATE、DELETE。因爲更新表時,MySQL 不僅要保存數據,還要保存一下索引文件每次更新添加了索引列的字段,都會調整因爲更新所帶來的鍵值變化後的索引信息。
三、MySQL的索引結構?
MySQL目前提供了以下4種索引類型:
- BTREE 索引 : 最常見的索引類型,大部分索引都支持 B 樹索引。
- HASH 索引:只有Memory引擎支持 , 使用場景簡單 。
- R-tree 索引(空間索引):空間索引是MyISAM引擎的一個特殊索引類型,主要用於地理空間數據類型,通常使用較少,不做特別介紹。
- Full-text (全文索引) :全文索引也是MyISAM的一個特殊索引類型,主要用於全文索引,InnoDB從
Mysql5.6版本開始支持全文索引
我們平常所說的索引,如果沒有特別指明,都是指B+樹(多路搜索樹,並不一定是二叉的)結構組織的索引。其中聚集索引、複合索引、前綴索引、唯一索引默認都是使用 B+tree 索引,統稱爲 索引。
四、BTree和B+Tree的結構?
五、索引的分類?
1) 單值索引 :即一個索引只包含單個列,一個表可以有多個單列索引
2) 唯一索引 :索引列的值必須唯一,但允許有空值
3) 複合索引 :即一個索引包含多個列
六、如何創建、查看、刪除索引?
索引在創建表的時候,可以同時創建, 也可以隨時增加新的索引。
語法:
CREATE [UNIQUE|FULLTEXT|SPATIAL] INDEX index_name
[USING index_type]
ON tbl_name(index_col_name,...)
示例: 爲city表中的city_name字段創建索引 ;
create index idx_city_name on city(city_name);
其中 idx_city_name 爲索引名稱 city爲表名 city_name爲city表的列名
查看索引
語法:
show index from table_name;
刪除索引
語法 :
DROP INDEX index_name ON tbl_name;
七、索引設計的原則?
- 1、對查詢頻次較高,且數據量比較大的表建立索引。
- 2、索引字段的選擇,最佳候選列應當從 where子句的條件中提取,如果where子句中的組合比較多,那麼應當挑選最常用、過濾效果最好的列的組合。
- 3、使用唯一索引,區分度越高,使用索引的效率越高。
- 4、索引可以有效的提升查詢數據的效率,但索引數量不是多多益善,索引越多,維護索引的代價自然也就水漲船高。對於插入、更新、刪除等DML操作比較頻繁的表來說,索引過多,會引入相當高的維護代價,降低DML操作的效率,增加相應操作的時間消耗。另外索引過多的話,MySQL也會犯選擇困難病,雖然最終仍然會找到一個可用的索引,但無疑提高了選擇的代價。
- 5、使用短索引,索引創建之後也是使用硬盤來存儲的,因此提升索引訪問的 I/O效率,也可以提升總體的訪問效率。假如構成索引的字段總長度比較短,那麼在給定大小的存儲塊內可以存儲更多的索引值,相應的可以有效的提升MySQL訪問索引的I/O效率。
- 6、利用最左前綴, N個列組合而成的組合索引,那麼相當於是創建了N個索引,如果查詢時where子句中使用了組成該索引的前幾個字段,那麼這條查詢SQL可以利用組合索引來提升查詢效率。
聯合索引的最左原則
聯合索引的最左原則就是建立索引KEY union_index (a,b,c)時,等於建立了(a)、(a,b)、(a,b,c)三個索引,從形式上看就是索引向左側聚集,所以叫做最左原則,因此最常用的條件應該放到聯合索引的組左側
創建複合索引 :
CREATE INDEX idx_name_email_status ON tb_seller(NAME,email,STATUS);
就相當於
對name 創建索引 ;
對name , email 創建了索引 ;
對name , email, status 創建了索引 ;
八、什麼是視圖?
視圖(View)是一種虛擬存在的表。視圖並不在數據庫中實際存在,行和列數據來自定義視圖的查詢中使用的表,並且是在使用視圖時動態生成的。通俗的講,視圖就是一條SELECT語句執行後返回的結果集。所以我們在創建視圖的時候,主要的工作就落在創建這條SQL查詢語句上。
視圖相對於普通的表的優勢主要包括以下幾項。
簡單:使用視圖的用戶完全不需要關心後面對應的表的結構、關聯條件和篩選條件,對用戶來說已經是過濾好的複合條件的結果集。
安全:使用視圖的用戶只能訪問他們被允許查詢的結果集,對錶的權限管理並不能限制到某個行某個列,但是通過視圖就可以簡單的實現。
數據獨立:一旦視圖的結構確定了,可以屏蔽表結構變化對用戶的影響,源表增加列對視圖沒有影響;源表修改列名,則可以通過修改視圖來解決,不會造成對訪問者的影響。
九、如何創建、修改、查看、刪除視圖?
創建視圖的語法爲:
CREATE [OR REPLACE] [ALGORITHM = {UNDEFINED | MERGE | TEMPTABLE}]
VIEW view_name [(column_list)]
AS select_statement
[WITH [CASCADED | LOCAL] CHECK OPTION]
修改視圖的語法爲:
ALTER [ALGORITHM = {UNDEFINED | MERGE | TEMPTABLE}]
VIEW view_name [(column_list)]
AS select_statement
[WITH [CASCADED | LOCAL] CHECK OPTION]
選項 :
WITH [CASCADED | LOCAL] CHECK OPTION 決定了是否允許更新數據使記錄不再滿足視圖的條件。
LOCAL : 只要滿足本視圖的條件就可以更新。
CASCADED : 必須滿足所有針對該視圖的所有視圖的條件纔可以更新。 默認值.
示例 , 創建city_country_view視圖 , 執行如下SQL :
create or replace view city_country_view
as
select t.*,c.country_name from country c , city t where c.country_id = t.country_id;
查看視圖
從 MySQL 5.1 版本開始,使用 SHOW TABLES 命令的時候不僅顯示錶的名字,同時也會顯示視圖的名字,而不存在單獨顯示視圖的 SHOW VIEWS 命令。
刪除視圖
DROP VIEW [IF EXISTS] view_name [, view_name] ...[RESTRICT | CASCADE]
刪除視圖示例:
DROP VIEW city_country_view ;
十、什麼是存儲過程?
存儲過程和函數是 事先經過編譯並存儲在數據庫中的一段 SQL 語句的集合,調用存儲過程和函數可以簡化應用開發人員的很多工作,減少數據在數據庫和應用服務器之間的傳輸,對於提高數據處理的效率是有好處的。
存儲過程和函數的區別在於函數必須有返回值,而存儲過程沒有
十一、如何創建、調用、查看存儲過程?
語法
CREATE PROCEDURE procedure_name ([proc_parameter[,...]])
begin
-- SQL語句
end ;
示例:
delimiter $
create procedure pro_test1()
begin
select 'Hello Mysql' ;
end$
delimiter ;
DELIMITER
該關鍵字用來聲明SQL語句的分隔符 , 告訴 MySQL 解釋器,該段命令是否已經結束了,mysql是否可以執行了。
默認情況下,delimiter是分號;。在命令行客戶端中,如果有一行命令以分號結束,那麼回車後,mysql將會執行該命令。
調用存儲過程語法:
call procedure_name() ;
查看存儲過程
-- 查詢db_name數據庫中的所有的存儲過程
select name from mysql.proc where db='db_name';
-- 查詢存儲過程的狀態信息
show procedure status;
-- 查詢某個存儲過程的定義
show create procedure test.pro_test1 \G;
刪除存儲過程
DROP PROCEDURE [IF EXISTS] sp_name ;
十二、什麼是觸發器?
觸發器是與表有關的數據庫對象,指在 insert/update/delete 之前或之後,觸發並執行觸發器中定義的SQL語句集合。觸發器的這種特性可以協助應用在數據庫端確保數據的完整性 , 日誌記錄 , 數據校驗等操作 。
使用別名 OLD 和 NEW 來引用觸發器中發生變化的記錄內容,這與其他的數據庫是相似的。現在觸發器還只支持行級觸發,不支持語句級觸發。
十三、如何創建、刪除、查看觸發器?
創建觸發器語法結構 :
create trigger trigger_name
before/after insert/update/delete
on tbl_name
[ for each row ] -- 行級觸發器
begin
trigger_stmt ;
end;
示例
需求:通過觸發器記錄 emp 表的數據變更日誌 , 包含增加, 修改 , 刪除 ;
首先創建一張日誌表 :
create table emp_logs(
id int(11) not null auto_increment,
operation varchar(20) not null comment '操作類型, insert/update/delete',
operate_time datetime not null comment '操作時間',
operate_id int(11) not null comment '操作表的ID',
operate_params varchar(500) comment '操作參數',
primary key(`id`)
)engine=innodb default charset=utf8;
創建 insert 型觸發器,完成插入數據時的日誌記錄
DELIMITER $
create trigger emp_logs_insert_trigger
after insert
on emp
for each row
begin
insert into emp_logs (id,operation,operate_time,operate_id,operate_params)
values(null,'insert',now(),new.id,concat('插入後(id:',new.id,', name:',new.name,',
age:',new.age,', salary:',new.salary,')'));
end $
DELIMITER ;
創建 update 型觸發器,完成更新數據時的日誌記錄 :
DELIMITER $
create trigger emp_logs_update_trigger
after update
on emp
for each row
begin
insert into emp_logs (id,operation,operate_time,operate_id,operate_params)
values(null,'update',now(),new.id,concat('修改前(id:',old.id,', name:',old.name,',
age:',old.age,', salary:',old.salary,') , 修改後(id',new.id, 'name:',new.name,',
age:',new.age,', salary:',new.salary,')'));
end $
DELIMITER ;
創建delete 行的觸發器 , 完成刪除數據時的日誌記錄 :
DELIMITER $
create trigger emp_logs_delete_trigger
after delete
on emp
for each row
begin
insert into emp_logs (id,operation,operate_time,operate_id,operate_params)
values(null,'delete',now(),old.id,concat('刪除前(id:',old.id,', name:',old.name,',
age:',old.age,', salary:',old.salary,')'));
end $
DELIMITER ;
測試:
insert into emp(id,name,age,salary) values(null, '光明左使',30,3500);
insert into emp(id,name,age,salary) values(null, '光明右使',33,3200);
update emp set age = 39 where id = 3;
delete from emp where id = 5;
刪除觸發器
drop trigger [schema_name.]trigger_name
如果沒有指定 schema_name,默認爲當前數據庫 。
查看觸發器
可以通過執行 SHOW TRIGGERS 命令查看觸發器的狀態、語法等信息。
show triggers
十四、MySQL的體系結構?
整個 MySQL Server由以下組成
- Connection Pool : 連接池組件
- Management Services & Utilities : 管理服務和工具組件
- SQL Interface : SQL 接口組件
- Parser : 查詢分析器組件
- Optimizer : 優化器組件
- Caches & Buffers : 緩衝池組件
- Pluggable Storage Engines : 存儲引擎
- File System : 文件系統
十五、MySQL的存儲引擎?
可以通過指定 show engines , 來查詢當前數據庫支持的存儲引擎 :
注意: MySQL5.5之前的默認存儲引擎是MyISAM,5.5之後就改爲了InnoDB。
查看Mysql數據庫默認的存儲引擎 , 指令 :
show variables like '%storage_engine%'
十六、MyISAM和InnoDB存儲引擎的區別?
InnoDB存儲引擎是Mysql5.5版本後的默認存儲引擎。InnoDB存儲引擎提供了具有提交、回滾、崩潰恢復能力的事務安全。
但是對比MyISAM的存儲引擎,InnoDB寫的處理效率差一些,並且會佔用更多的磁盤空間以保留數據和索引。
InnoDB 支持事務、外鍵、行鎖
存儲方式:
InnoDB 存儲表和索引有以下兩種方式 :
①. 使用共享表空間存儲, 這種方式創建的表的表結構保存在.frm文件中, 數據和索引保存在
innodb_data_home_dir 和 innodb_data_file_path定義的表空間中,可以是多個文件。
②. 使用多表空間存儲, 這種方式創建的表的表結構仍然存在 .frm 文件中,但是每個表的數據和索引單獨保存在.ibd 中。
MyISAM 不支持事務、也不支持外鍵,和行鎖其優勢是訪問的速度快,對事務的完整性沒有要求或者以SELECT、INSERT爲主的應用基本上都可以使用這個引擎來創建表 。
文件存儲方式
每個MyISAM在磁盤上存儲成3個文件,其文件名都和表名相同,但拓展名分別是 :
.frm (存儲表定義);
.MYD(MYData , 存儲數據);
.MYI(MYIndex , 存儲索引);
MEMORY:
Memory存儲引擎將表的數據存放在內存中。每個MEMORY表實際對應一個磁盤文件,格式是.frm ,該文件中只存儲表的結構,而其數據文件,都是存儲在內存中,這樣有利於數據的快速處理,提高整個表的效率。MEMORY類型的表訪問非常地快,因爲他的數據是存放在內存中的,並且默認使用HASH索引 , 但是服務一旦關閉,表中的數據就會丟失。
MERGE
MERGE存儲引擎是一組MyISAM表的組合,這些MyISAM表必須結構完全相同,MERGE表本身並沒有存儲數據,對MERGE類型的表可以進行查詢、更新、刪除操作,這些操作實際上是對內部的MyISAM表進行的。對於MERGE類型表的插入操作,是通過INSERT_METHOD子句定義插入的表,可以有3個不同的值,使用FIRST 或LAST 值使得插入操作被相應地作用在第一或者最後一個表上,不定義這個子句或者定義爲NO,表示不能對這個MERGE表執行插入操作。
可以對MERGE表進行DROP操作,但是這個操作只是刪除MERGE表的定義,對內部的表是沒有任何影響的。
十七、如何選擇MySQL的存儲引擎?
在選擇存儲引擎時,應該根據應用系統的特點選擇合適的存儲引擎。對於複雜的應用系統,還可以根據實際情況選擇多種存儲引擎進行組合。以下是幾種常用的存儲引擎的使用環境。
InnoDB : 是Mysql的默認存儲引擎,用於事務處理應用程序,支持外鍵。如果應用對事務的完整性有比較高的要求,在併發條件下要求數據的一致性,數據操作除了插入和查詢意外,還包含很多的更新、刪除操作,那麼InnoDB存儲引擎是比較合適的選擇。InnoDB存儲引擎除了有效的降低由於刪除和更新導致的鎖定, 還可以確保事務的完整提交和回滾,對於類似於計費系統或者財務系統等對數據準確性要求比較高的系統,InnoDB是最合適的選擇。
MyISAM : 如果應用是以讀操作和插入操作爲主,只有很少的更新和刪除操作,並且對事務的完整性、併發性要求不是很高,那麼選擇這個存儲引擎是非常合適的。
MEMORY :將所有數據保存在RAM中,在需要快速定位記錄和其他類似數據環境下,可以提供幾塊的訪問。MEMORY的缺陷就是對錶的大小有限制,太大的表無法緩存在內存中,其次是要確保表的數據可以恢復,數據庫異常終止後表中的數據是可以恢復的。MEMORY表通常用於更新不太頻繁的小表,用以快速得到訪問結果。
MERGE :用於將一系列等同的MyISAM表以邏輯方式組合在一起,並作爲一個對象引用他們。MERGE表的優點在於可以突破對單個MyISAM表的大小限制,並且通過將不同的表分佈在多個磁盤上,可以有效的改善MERGE表的訪問效率。這對於存儲諸如數據倉儲等VLDB環境十分合適。
十八、如何優化SQL?
首先定位低效率的SQL
可以通過以下兩種方式定位執行效率較低的 SQL 語句。
慢查詢日誌 : 通過慢查詢日誌定位那些執行效率較低的 SQL 語句,用–log-slow-queries[=file_name]選項啓動時,mysqld 寫一個包含所有執行時間超過 long_query_time 秒的 SQL 語句的日誌文件。
show processlist : 慢查詢日誌在查詢結束以後才紀錄,所以在應用反映執行效率出現問題的時候查詢慢查詢日誌並不能定位問題,可以使用show processlist命令查看當前MySQL在進行的線程,包括線程的狀態、是否鎖表等,可以實時地查看 SQL 的執行情況,同時對一些鎖表操作進行優化。
explain關鍵字的使用
查詢到效率低的 SQL 語句後,可以通過 EXPLAIN或者 DESC命令獲取 MySQL如何執行 SELECT 語句
的信息,包括在 SELECT 語句執行過程中表如何連接和連接的順序。
查詢SQL語句的執行計劃 :
explain select * from tb_item where id = 1;
上面十個列的含義必須理解:
下面分別對上面表中的九個字段進行詳細解釋:
-
1、id
1) id 相同表示加載表的順序是從上到下
2) id 不同id值越大,優先級越高,越先被執行。
3) id 有相同,也有不同,同時存在。id相同的可以認爲是一組,從上往下順序執行;在所有的組中,id的值越大,優先級越高,越先執行。
-
2、select_type
表示 SELECT 的類型,常見的取值,如下表所示
-
3、table
展示這一行的數據是關於哪一張表的 -
4、type
非常重要
type 顯示的是訪問類型,是較爲重要的一個指標,可取值爲:
range where 後出現 ‘***%’ 也是range
結果值從最好到最壞依次是:
NULL > system > const > eq_ref > ref > fulltext > ref_or_null > index_merge >
unique_subquery > index_subquery > range > index > ALL
system > const > eq_ref > ref > range > index > ALL
一般來說, 我們需要保證查詢至少達到 range 級別, 最好達到ref 。
- 5、6、7、key
possible_keys : 顯示可能應用在這張表的索引, 一個或多個。
key : 實際使用的索引, 如果爲NULL, 則沒有使用索引。
key_len : 表示索引中使用的字節數, 該值爲索引字段最大可能長度,並非實際使用長度,在不損失精確性的前提下, 長度越短越好 。
-
8、rows
掃描行的數量。 -
9、extra
其他的額外的執行計劃信息,在該列展示 。
索引的使用
索引是數據庫優化最常用也是最重要的手段之一, 通過索引通常可以幫助用戶解決大多數的MySQL的性能優化問題。
使用索引帶來性能優化的示例:
A. 根據ID查詢
select * from tb_item where id = 1999\G;
查詢速度很快, 接近0s , 主要的原因是因爲id爲主鍵, 有索引;
2). 根據 title 進行精確查詢
select * from tb_item where title = 'iphoneX 移動3G 32G941'\G;
可以看到 需要9.15S 性能非常低
此時 使用explain 查看 SQL語句的執行計劃 :
可以看到 並未使用索引
處理方案 , 針對title字段, 創建索引 :
create index idx_item_title on tb_item(title);
這裏大約300w條數據 創建索引使用了 3min多
索引創建完成之後,再次進行查詢 :
可以看到性能有質的提升
通過 explain , 查看執行計劃,執行SQL時使用了剛纔創建的索引
避免索引失效
準備測試環境
創建表:
-- 創建表
create table `tb_seller` (
`sellerid` varchar (100),
`name` varchar (100),
`nickname` varchar (50),
`password` varchar (60),
`status` varchar (1),
`address` varchar (100),
`createtime` datetime,
primary key(`sellerid`)
)engine=innodb default charset=utf8mb4;
-- 插入數據
insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`,
`address`, `createtime`) values('alibaba','阿里巴巴','阿里小
店','e10adc3949ba59abbe56e057f20f883e','1','北京市','2088-01-01 12:00:00');
insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`,
`address`, `createtime`) values('baidu','百度科技有限公司','百度小
店','e10adc3949ba59abbe56e057f20f883e','1','北京市','2088-01-01 12:00:00');
insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`,
`address`, `createtime`) values('huawei','華爲科技有限公司','華爲小
店','e10adc3949ba59abbe56e057f20f883e','0','北京市','2088-01-01 12:00:00');
insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`,
`address`, `createtime`) values('itcast','傳智播客教育科技有限公司','傳智播
客','e10adc3949ba59abbe56e057f20f883e','1','北京市','2088-01-01 12:00:00');
insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`,
`address`, `createtime`) values('itheima','黑馬程序員','黑馬程序
員','e10adc3949ba59abbe56e057f20f883e','0','北京市','2088-01-01 12:00:00');
insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`,
`address`, `createtime`) values('luoji','羅技科技有限公司','羅技小
店','e10adc3949ba59abbe56e057f20f883e','1','北京市','2088-01-01 12:00:00');
insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`,
`address`, `createtime`) values('oppo','OPPO科技有限公司','OPPO官方旗艦
店','e10adc3949ba59abbe56e057f20f883e','0','北京市','2088-01-01 12:00:00');
insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`,
`address`, `createtime`) values('ourpalm','掌趣科技股份有限公司','掌趣小
店','e10adc3949ba59abbe56e057f20f883e','1','北京市','2088-01-01 12:00:00');
insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`,
`address`, `createtime`) values('qiandu','千度科技','千度小
店','e10adc3949ba59abbe56e057f20f883e','2','北京市','2088-01-01 12:00:00');
insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`,
`address`, `createtime`) values('sina','新浪科技有限公司','新浪官方旗艦
店','e10adc3949ba59abbe56e057f20f883e','1','北京市','2088-01-01 12:00:00');
insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`,
`address`, `createtime`) values('xiaomi','小米科技','小米官方旗艦
店','e10adc3949ba59abbe56e057f20f883e','1','西安市','2088-01-01 12:00:00');
insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`,
`address`, `createtime`) values('yijia','宜家家居','宜家家居旗艦
店','e10adc3949ba59abbe56e057f20f883e','1','北京市','2088-01-01 12:00:00');
-- 創建索引
create index idx_seller_name_sta_addr on tb_seller(name,status,address);
1). 全值匹配 ,對索引中所有列都指定具體值。
該情況下,索引生效,執行效率高。
explain select * from tb_seller where name='小米科技' and status='1' and address='北京市'\G;
2). 最左前綴法則
如果索引了多列,要遵守最左前綴法則。指的是查詢從索引的最左前列開始,並且不跳過索引中的列。
匹配最左前綴法則,走索引:
可以看到 type 是ref 但是 keylength 是不同的
違反最左前綴法則 , 索引失效:
如果符合最左法則,但是出現跳躍某一列,只有最左列索引生效:
3). 範圍查詢右邊的列,不能使用索引
根據前面的兩個字段 name , status 查詢是走索引的, 但是最後一個條件address 沒有用到索引
4). 不要在索引列上進行運算操作,否則 索引將失效
5). 字符串不加單引號,造成索引失效
由於,在查詢時,沒有對字符串加單引號, MySQL的查詢優化器,會自動的進行類型轉換,造成索引失效。
**6). 儘量使用覆蓋索引,避免select ***
儘量使用覆蓋索引(只訪問索引的查詢(索引列完全包含查詢列)),減少select *
如果查詢列,超出索引列,也會降低性能
TIP :
using index :使用覆蓋索引的時候就會出現
using where:在查找使用索引的情況下,需要回表去查詢所需的數據
using index condition:查找使用了索引,但是需要回表查詢數據
using index ; using where:查找使用了索引,但是需要的數據都在索引列中能找到,所以不需要回表
查詢數據
7). 用or分割開的條件, 如果or前的條件中的列有索引,而後面的列中沒有索引,那麼涉及的索引都不會被用到。
示例,name字段是索引列 , 而createtime不是索引列,中間是or進行連接是不走索引的 :
8). 以%開頭的Like模糊查詢,索引失效。
如果僅僅是尾部模糊匹配,索引不會失效。如果是頭部模糊匹配,索引失效
解決方案 :
通過覆蓋索引來解決
9). 如果MySQL評估使用索引比全表更慢,則不使用索引。
10). is NULL , is NOT NULL 有時索引失效
11). in 走索引, not in 索引失效。
12). 單列索引和複合索引。
儘量使用複合索引,而少使用單列索引 。
創建複合索引
create index idx_name_sta_address on tb_seller(name, status, address);
就相當於創建了三個索引 :
name
name + status
name + status + address
創建單列索引
create index idx_seller_name on tb_seller(name);
create index idx_seller_status on tb_seller(status);
create index idx_seller_address on tb_seller(address);
查看索引使用情況
show status like 'Handler_read%';
show global status like 'Handler_read%';
Handler_read_first :索引中第一條被讀的次數。如果較高,表示服務器正執行大量全索引掃描(這個值越低越好)。
Handler_read_key:如果索引正在工作,這個值代表一個行被索引值讀的次數,如果值越低,表示索引得到的性能改善不高,因爲索引不經常使用(這個值越高越好)。
Handler_read_next :按照鍵順序讀下一行的請求數。如果你用範圍約束或如果執行索引掃描來查詢索引列,該值增加。
Handler_read_prev:按照鍵順序讀前一行的請求數。該讀方法主要用於優化ORDER BY … DESC。
Handler_read_rnd :根據固定位置讀一行的請求數。如果你正執行大量查詢並需要對結果進行排序該值較高。
你可能使用了大量需要MySQL掃描整個表的查詢或你的連接沒有正確使用鍵。這個值較高,意味着運行效率低,應該建立索引來補救。
Handler_read_rnd_next:在數據文件中讀下一行的請求數。如果你正進行大量的表掃描,該值較高。通常說明你的表索引不正確或寫入的查詢沒有利用索引。
十九、如何進行SQL語句的優化?
優化insert語句
如果需要同時對一張表插入很多行數據時,應該儘量使用多個值表的 insert語句,這種方式將大大的縮減客戶端與數據庫之間的連接、關閉等消耗。使得效率比分開執行的單個insert語句快。
示例, 原始方式爲:
insert into tb_test values(1,'Tom');
insert into tb_test values(2,'Cat');
insert into tb_test values(3,'Jerry');
優化後的方案爲 :
insert into tb_test values(1,'Tom'),(2,'Cat'),(3,'Jerry');
在事務中進行數據插入。
start transaction;
insert into tb_test values(1,'Tom');
insert into tb_test values(2,'Cat');
insert into tb_test values(3,'Jerry');
commit;
數據有序插入
insert into tb_test values(4,'Tim');
insert into tb_test values(1,'Tom');
insert into tb_test values(3,'Jerry');
insert into tb_test values(5,'Rose');
insert into tb_test values(2,'Cat');
優化後
insert into tb_test values(1,'Tom');
insert into tb_test values(2,'Cat');
insert into tb_test values(3,'Jerry');
insert into tb_test values(4,'Tim');
insert into tb_test values(5,'Rose');
優化order by語句
兩種排序方式
1). 第一種是通過對返回數據進行排序,也就是通常說的 filesort 排序,所有不是通過索引直接返回排序結果的排序都叫 FileSort 排序。
2). 第二種通過有序索引順序掃描直接返回有序數據,這種情況即爲 using index,不需要額外排序,操作效率高。
多字段排序
瞭解了MySQL的排序方式,優化目標就清晰了:儘量減少額外的排序,通過索引直接返回有序數據。where 條件和Order by 使用相同的索引,並且Order By 的順序和索引順序相同, 並且Order by 的字段都是升序,或者都是降序。否則肯定需要額外的操作,這樣就會出現FileSort。
Filesort 的優化
通過創建合適的索引,能夠減少 Filesort 的出現,但是在某些情況下,條件限制不能讓Filesort消失,那就需要加快 Filesort的排序操作。對於Filesort , MySQL 有兩種排序算法:
1) 兩次掃描算法 :MySQL4.1 之前,使用該方式排序。首先根據條件取出排序字段和行指針信息,然後在排序區sort buffer 中排序,如果sort buffer不夠,則在臨時表 temporary table 中存儲排序結果。完成排序之後,再根據行指針回表讀取記錄,該操作可能會導致大量隨機I/O操作。
2 )一次掃描算法:一次性取出滿足條件的所有字段,然後在排序區 sort buffer 中排序後直接輸出結果集。排序時內存開銷較大,但是排序效率比兩次掃描算法要高
MySQL 通過比較系統變量 max_length_for_sort_data 的大小和Query語句取出的字段總大小, 來判定是否那種排序算法,如果max_length_for_sort_data 更大,那麼使用第二種優化之後的算法;否則使用第一種。
可以適當提高 sort_buffer_size 和 max_length_for_sort_data 系統變量,來增大排序區的大小,提高排序的效率。
優化group by 語句
由於GROUP BY 實際上也同樣會進行排序操作,而且與ORDER BY 相比,GROUP BY 主要只是多了排序之後的分組操作。當然,如果在分組的時候還使用了其他的一些聚合函數,那麼還需要一些聚合函數的計算。所以,在GROUP BY 的實現過程中,與 ORDER BY 一樣也可以利用到索引。
如果查詢包含 group by 但是用戶想要避免排序結果的消耗, 則可以執行order by null 禁止排序。如下 :
drop index idx_emp_age_salary on emp;
explain select age,count(*) from emp group by age;
優化後
explain select age,count(*) from emp group by age order by null;
從上面的例子可以看出,第一個 SQL語句需要進行"filesort",而第二個SQL由於order by null 不需要進行
“filesort”, 而上文提過Filesort往往非常耗費時間。
創建索引 :
create index idx_emp_age_salary on emp(age,salary);
優化嵌套查詢
Mysql4.1版本之後,開始支持SQL的子查詢。這個技術可以使用SELECT語句來創建一個單列的查詢結果,然後把
這個結果作爲過濾條件用在另一個查詢中。使用子查詢可以一次性的完成很多邏輯上需要多個步驟才能完成的SQL
操作,同時也可以避免事務或者表鎖死,並且寫起來也很容易。但是,有些情況下,子查詢是可以被更高效的連接
(JOIN)替代。
示例 ,查找有角色的所有的用戶信息 :
explain select * from t_user where id in (select user_id from user_role );
執行計劃爲 :
優化後 (使用隱式內連接):
explain select * from t_user u , user_role ur where u.id = ur.user_id;
連接(Join)查詢之所以更有效率一些 ,是因爲MySQL不需要在內存中創建臨時表來完成這個邏輯上需要兩個步驟的查詢工作。
優化OR條件
對於包含 OR的查詢子句,如果要利用索引,則OR之間的每個條件列都必須用到索引 , 而且不能使用到複合索引; 如果沒有索引,則應該考慮增加索引。
示例 :
explain select * from emp where id = 1 or age = 30;
建議使用 union 替換 or :
我們來比較下重要指標,發現主要差別是 type 和 ref 這兩項
type 顯示的是訪問類型,是較爲重要的一個指標,結果值從好到壞依次是:
system > const > eq_ref > ref > fulltext > ref_or_null > index_merge >
unique_subquery > index_subquery > range > index > ALL
UNION 語句的 type 值爲 ref,OR 語句的 type 值爲 range,可以看到這是一個很明顯的差距
UNION 語句的 ref 值爲 const,OR 語句的 type 值爲 null,const 表示是常量值引用,非常快
這兩項的差距就說明了 UNION 要優於 OR
** 優化分頁查詢**
一般分頁查詢時,通過創建覆蓋索引能夠比較好地提高性能。一個常見又非常頭疼的問題就是 limit 2000000,10 ,此時需要MySQL排序前2000010 記錄,僅僅返回2000000 - 2000010 的記錄,其他記錄丟棄,查詢排序的代價非常大
** 優化思路一**
在索引上完成排序分頁操作,最後根據主鍵關聯回原表查詢所需要的其他列內容。
優化思路二
該方案適用於主鍵自增的表,可以把Limit 查詢轉換成某個位置的查詢 。
使用SQL提示
SQL提示,是優化數據庫的一個重要手段,簡單來說,就是在SQL語句中加入一些人爲的提示來達到優化操作的目的。
USE INDEX
在查詢語句中表名的後面,添加 use index 來提供希望MySQL去參考的索引列表,就可以讓MySQL不再考慮其他可用的索引。
create index idx_seller_name on tb_seller(name);
IGNORE INDEX
如果用戶只是單純的想讓MySQL忽略一個或者多個索引,則可以使用 ignore index 作爲 hint 。
explain select * from tb_seller ignore index(idx_seller_name) where name = ' 小米科技';
FORCE INDEX
爲強制MySQL使用一個特定的索引,可在查詢中使用 force index 作爲hint 。
create index idx_seller_address on tb_seller(address);
二十、Mysql中查詢緩存優化的操作流程?
開啓Mysql的查詢緩存,當執行完全相同的SQL語句的時候,服務器就會直接從緩存中讀取結果,當數據被修改,之前的緩存會失效,修改比較頻繁的表不適合做查詢緩存。
操作流程
1 . 客戶端發送一條查詢給服務器;
2. 服務器先會檢查查詢緩存,如果命中了緩存,則立即返回存儲在緩存中的結果。否則進入下一階段;
3. 服務器端進行SQL解析、預處理,再由優化器生成對應的執行計劃;
4. MySQL根據優化器生成的執行計劃,調用存儲引擎的API來執行查詢;
5. 將結果返回給客戶端。
緩存大小一般設置爲服務器內存大小的20%
二十一、MySQL鎖機制?
從對數據操作的粒度分 :
1) 表鎖:操作時,會鎖定整個表。
2) 行鎖:操作時,會鎖定當前操作行。
從對數據操作的類型分:
1) 讀鎖(共享鎖):針對同一份數據,多個讀操作可以同時進行而不會互相影響。
2) 寫鎖(排它鎖):當前操作沒有完成之前,它會阻斷其他寫鎖和讀鎖。
僅從鎖的角度來說:表級鎖更適合於以查詢爲主,只有少量按索引條件更新數據的應用,如Web 應用;而行級鎖則更適合於有大量按索引條件併發更新少量不同數據,同時又有並查詢的應用,如一些在線事務處理(OLTP)系統。
如何加表鎖
MyISAM 在執行查詢語句(SELECT)前,會自動給涉及的所有表加讀鎖,在執行更新操作(UPDATE、DELETE、INSERT 等)前,會自動給涉及的表加寫鎖,這個過程並不需要用戶干預,因此,用戶一般不需要直接用 LOCKTABLE 命令給 MyISAM 表顯式加鎖。
顯示加表鎖語法:
加讀鎖 : lock table table_name read;
加寫鎖 : lock table table_name write;
鎖模式的相互兼容性如表中所示:
由上表可見:
1) 對MyISAM 表的讀操作,不會阻塞其他用戶對同一表的讀請求,但會阻塞對同一表的寫請求;
2) 對MyISAM 表的寫操作,則會阻塞其他用戶對同一表的讀和寫操作;
簡而言之,就是讀鎖會阻塞寫,但是不會阻塞讀。而寫鎖,則既會阻塞讀,又會阻塞寫。
此外,MyISAM 的讀寫鎖調度是寫優先,這也是MyISAM不適合做寫爲主的表的存儲引擎的原因。因爲寫鎖後,其他線程不能做任何操作,大量的更新會使查詢很難得到鎖,從而造成永遠阻塞
** InnoDB 行鎖**
行鎖介紹
行鎖特點 :偏向InnoDB 存儲引擎,開銷大,加鎖慢;會出現死鎖;鎖定粒度最小,發生鎖衝突的概率最低,併發度也最高。
InnoDB 與 MyISAM 的最大不同有兩點:一是支持事務;二是 採用了行級鎖。
背景知識(需要掌握)
事務及其ACID屬性
事務是由一組SQL語句組成的邏輯處理單元。
事務具有以下4個特性,簡稱爲事務ACID屬性。
事務隔離級別
爲了解決上述提到的事務併發問題,數據庫提供一定的事務隔離機制來解決這個問題。數據庫的事務隔離越嚴格,併發副作用越小,但付出的代價也就越大,因爲事務隔離實質上就是使用事務在一定程度上“串行化” 進行,這顯然與“併發” 是矛盾的。
數據庫的隔離級別有4個,由低到高依次爲Read uncommitted、Read committed、Repeatable read、
Serializable,這四個級別可以逐個解決髒寫、髒讀、不可重複讀、幻讀這幾類問題。
注意:丟失更新是mysql設置隔離級別無法解決的問題。
例如
有一個表 t_user
username | age |
---|---|
張三 | 23 |
事務A 讀取到 數據 張三 23 把 張三 改爲 李四 正準備提交 還未提交 此時 另一個B事務讀取到數據庫內容是 張三 23 然後 把 23改爲24 還未提交 這時 事務A提交了 緊接着事務B也提交了
那麼 數據庫最終的數據是 張三 24
對於事務A 對數據庫的更新把張三改爲李四 這個更新操作 就丟失了 這個現象就叫做丟失更新
參考文章MySQL丟失更新的問題
InnoDB 的行鎖模式
InnoDB 實現了以下兩種類型的行鎖。
共享鎖( S):又稱爲讀鎖,簡稱S鎖,共享鎖就是多個事務對於同一數據可以共享一把鎖,都能訪問到數據,但是隻能讀不能修改。
排他鎖( X):又稱爲寫鎖,簡稱X鎖,排他鎖就是不能與其他鎖並存,如一個事務獲取了一個數據行的排他鎖,其他事務就不能再獲取該行的其他鎖,包括共享鎖和排他鎖,但是獲取排他鎖的事務是可以對數據就行讀取和修改。
對於UPDATE、DELETE和INSERT語句,InnoDB會自動給涉及數據集加排他鎖(X);
對於普通SELECT語句,InnoDB不會加任何鎖;
可以通過以下語句顯示給記錄集加共享鎖或排他鎖 。
共享鎖( S):SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE
排他鎖(X) :SELECT * FROM table_name WHERE ... FOR UPDATE
無索引行鎖升級爲表鎖
如果不通過索引條件檢索數據,那麼InnoDB將對錶中的所有記錄加鎖,實際效果跟表鎖一樣。
間隙鎖危害
當我們用範圍條件,而不是使用相等條件檢索數據,並請求共享或排他鎖時,InnoDB會給符合條件的已有數據進行加鎖; 對於鍵值在條件範圍內但並不存在的記錄,叫做 “間隙(GAP)” , InnoDB也會對這個 “間隙” 加鎖,這種鎖機制就是所謂的 間隙鎖(Next-Key鎖) 。
總結
InnoDB 存儲引擎由於實現了行級鎖定,雖然在鎖定機制的實現方面帶來了性能損耗可能比表鎖會更高一些,但是在整體併發處理能力方面要遠遠由於MyISAM的表鎖的。當系統併發量較高的時候,InnoDB的整體性能和MyISAM相比就會有比較明顯的優勢。
但是,InnoDB的行級鎖同樣也有其脆弱的一面,當我們使用不當的時候,可能會讓InnoDB的整體性能表現不僅不能比MyISAM高,甚至可能會更差。
優化建議:
- 儘可能讓所有數據檢索都能通過索引來完成,避免無索引行鎖升級爲表鎖。
- 合理設計索引,儘量縮小鎖的範圍
- 儘可能減少索引條件,及索引範圍,避免間隙鎖
- 儘量控制事務大小,減少鎖定資源量和時間長度
- 儘可使用低級別事務隔離(但是需要業務層面滿足需求)
二十二、SQL語句的執行順序?
編寫順序
SELECT DISTINCT
<select list>
FROM
<left_table> <join_type>
JOIN
<right_table> ON <join_condition>
WHERE
<where_condition>
GROUP BY
<group_by_list>
HAVING
<having_condition>
ORDER BY
<order_by_condition>
LIMIT
<limit_params>
執行順序
FROM <left_table>
ON <join_condition>
<join_type> JOIN <right_table>
WHERE <where_condition>
GROUP BY <group_by_list>
HAVING <having_condition>
SELECT DISTINCT <select list>
ORDER BY <order_by_condition>
LIMIT <limit_params>
二十三、MySQL常用函數?
字符串函數
日期函數
小Tip
對於字符串 也就是 MySQL 數據類型 爲 char 或varchar的字段 查詢時必須使用單引號 例如 xx = ‘abc’ 防止索引失效
Mybatis 批量插入數據 最多一次500