MySQL 學習記錄系列(三)

目錄

1、MySQL體系結構

2、存儲引擎

2.1、官網簡介

2.2、各種存儲引擎特性

2.3、存儲引擎的選擇

3、優化SQL步驟

3.1、查看SQL執行頻率

3.2、定位低效率執行SQL

3.3、explain分析執行計劃

3.4、show profile分析SQL

3.5、trace分析優化器執行計劃

4、索引的使用

4.1、驗證索引提升查詢效率

4.2、索引的使用 - 全值匹配

4.3、查看索引使用情況

5、SQL優化

5.1、大批量插入數據

5.2、優化insert語句

5.3、優化order by語句

5.5、優化嵌套查詢

5.6、優化OR條件

5.7、優化分頁查詢

5.8、使用SQL提示


1、MySQL體系結構

最上層代表着客戶端,包含了常見的連接客戶端,如JDBC、ODBC等

整個MySQL Server由以下組成

  1. Connection Pool:連接池組件(客戶端發起連接之後,將會到連接池中獲得連接,然後進行認證、執行對應請求等)
  2. Management Services&Utilities:管理服務和工具組件(安全、系統配置等)
  • SQL Interface(SQL的接口,執行/封裝DML、視圖、存儲過程等)

  • Parser(解析器,負責解析客戶端發起的SQL請求,並且進行相關的過濾)

  • Optimizer(MySQL內部優化器,對SQL語句進行優化)

  • Caches & Butters(優化完成之後,進行緩存的操作,先查詢緩存中是否存在相對應的數據)

  1. Pluggable Storage Engines(插件式存儲引擎,MyISAM InnoDB。。MySQL5.5以後默認的存儲引擎就是InnoDB)
  • 引擎:類似汽車的發動機(根據存儲的數據選擇不同的引擎進行相關處理)
  1. Flle systemFlles & Logs(存儲層,不管是增刪改查都是操作的文件系統)

1)連接層
最上層是一些客戶端和鏈接服務,包含本地socket通信和大多數基於客戶端/服務端工具實現的類似於TCP/IP的通信。

主要完成一些類似於連接處理、授權認證、及相關的安全方案。在該層上引入了線程池的概念,爲通過認證安全接入的客戶端提供線程。同樣在該層上可以實現基於SSL的安全鏈接。

服務器也會爲安全接入的每個客戶端驗證它所具有的操作權限。

2)服務層
第二層架構主要完成大多數的核心服務功能,如SQL接口,並完成緩存的查詢,SQL的分析和優化,部分內置函數的執行。所有跨存儲引擎的功能也在這一層實現,如過程、函數等。

在該層,服務器會解析查詢並創建相應的內部解析樹,並對其完成相應的優化如確定表的查詢的順序,是否利用索引等,最後生成相應的執行操作。

如果是select語句,服務器還會查詢內部的緩存,

如果緩存空間足夠大,這樣在解決大量讀操作的環境中能夠很好的提升系統的性能。

3)引擎層
存儲引擎層,存儲引擎真正的負責了MySQL中數據的存儲和提取,服務器通過APl和存儲引擎進行通信。

不同的存儲引擎具有不同的功能,可以根據自己的需要,來選取合適的存儲引擎。

4)存儲層
數據存儲層,主要是將數據存儲在文件系統之上,並完成與存儲引擎的交互。

和其他數據庫相比,MySQL有點與衆不同,它的架構可以在多種不同場景中應用併發揮良好作用。

主要體現在存儲引擎上,插件式的存儲引擎架構,將查詢處理和其他的系統任務以及數據的存儲提取分離。

這種架構可以根據業務的需求和實際需要選擇合適的存儲引擎

 

2、存儲引擎

2.1、官網簡介

和大多數的數據庫不同,MySQL中有一個存儲引擎的概念,針對不同的存儲需求可以選擇最優的存儲引擎。

存儲引擎就是存儲數據,建立索引,更新查詢數據等等技術的實現方式。存儲引擎是基於表的,而不是基於庫的。

所以存儲引擎也可被稱爲表類型。

Oracle,SqlServer等數據庫只有一種存儲引擎。

MySQL提供了插件式的存儲引擎架構。所以MySQL存在多種存儲引擎,可以根據需要使用相應引擎,或者編寫存儲引擎。

MySQL5.0支持的存儲引擎包含:InnoDB、MyISAM、BDB、MEMORY、MERGE、EXAMPLE、NDB Cluster、ARCHIVE、CSV、BLACKHOLE、FEDERATED等。

其中lnnoDB和BDB提供事務安全表,其他存儲引擎是非事務安全表。

可以通過指定 show engines,來查詢當前數據庫支持的存儲引擎

創建新表時如果不指定存儲引擎,那麼系統就會使用默認的存儲引擎,MySQL5.5之前的默認存儲引擎是MyISAM,5.5之後就改爲了InnoDB。

查看數據庫中的一些環境變量

show VARIABLES like '%storage_engine%'

2.2、各種存儲引擎特性

2.2.1、 InnoDB

InnoDB存儲引擎是Mysql的默認存儲引擎。

InnoDB存儲引擎提供了具有提交、回滾、崩潰恢復能力的事務安全。

但是對比MyISAM的存儲引擎,InnoDB寫的處理效率差一些,並且會佔用更多的磁盤空間以保留數據和索引。

InnoDB存儲引擎不同於其他存儲引擎的特點:

事務控制

CREATE TABLE goods_innodb( 
	id int NOT NULL AUTO_INCREMENT, 
	`NAME` VARCHAR ( 20 ) NOT NULL, 
	PRIMARY KEY ( id ) 
) ENGINE = INNODB DEFAULT CHARSET = utf8;
start transaction; 

insert into goods_innodb(id,name)values(null,'Meta20'); 

commit;

外鍵約束

MySQL支持外鍵的存儲引擎只有InnoDB,在創建外鍵的時候,要求父表必須有對應的索引,子表在創建外鍵的時候,也會自動的創建對應的索引。

下面兩張表中,country_innodb是父表,country_id爲主鍵索引,city_innodb表是子表,country_id字段爲外鍵,對應於country_innodb表的主鍵countryid。

create table country_innodb(
	country_id int NOT NULL AUTO_INCREMENT, 
	country_name varchar(100)NOT NULL, 
	primary key(country_id)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
create table city_innodb(
	city_id int NOT NULL AUTO_INCREMENT, 
	city_name varchar(50)NOT NULL, 
	country_id int NOT NULL, primary key(city_id), 
	key idx_fk_country_id(country_id), 
	CONSTRAINT `fk_city_country` FOREIGN KEY(country_id) REFERENCES country_innodb(country_id) ON DELETE RESTRICT ON UPDATE CASCADE
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert into country_innodb values(null,'China'),(null,'America'),(null,'Japan');

insert into city_innodb values(null,'Xian',1),(null,'NewYork',2),(null,'BeiJing',1);

ON DELETE RESTRICT ON UPDATE CASCADE

ON DELETE RESTRICT:在刪除主表數據時,如果有關聯記錄則不刪除 

ON UPDATE CASCADE:在更新主表時候,如果子表有關聯記錄時更新該記錄

在創建索引時,可以指定在刪除、更新父表時,對子表進行的相應操作,包括 RESTRICT、CASCADE、SET NULL和NO ACTION。

RESTPICT和NO ACTION相同,是指限制在子表有關聯記錄的情況下,父表不能更新;

CASCADE表示父表在更新或者刪除時,更新或者刪除子表對應的記錄;SET NULL則表示父表在更新或者刪除的時候,子表的對應字段被SETNULL。

針對上面創建的兩個表,子表的外鍵指定是ON DELETE RESTRICT ON UPDATE CASCADE方式的,那麼在主表刪除記錄的時候,如果子表有對應記錄,則不允許刪除,主表在更新記錄的時候,如果子表有對應記錄,則子表對應更新。

存儲方式

InnoDB存儲表和索引有以下兩種方式:

①.使用共享表空間存儲,這種方式創建的表的表結構保存在.frm文件中,數據和索引保存在innodb_data_home_dir 和innodb_data_file_path定義的表空間中,可以是多個文件。

②.使用多表空間存儲,這種方式創建的表的表結構仍然存在.frm文件中,但是每個表的數據和索引單獨保存在.ibd中。

2.2.2、MyISAM

MyISAM不支持事務、也不支持外鍵,其優勢是訪問的速度快,對事務的完整性沒有要求或者以SELECT、INSERT爲主的應用基本上都可以使用這個引擎來創建表。

有以下兩個比較重要的特點:

不支持事務

create table goods_myisam(
	id int NOT NULL AUTO_INCREMENT, 
	name varchar(20)NOT NULL, 
	primary key(id)
)ENGINE=myisam DEFAULT CHARSET=utf8;

通過測試,在MyISAM存儲引擎中,是沒有事務控制的;

文件存儲方式

每個MyISAM在磁盤上存儲成3個文件,其文件名都和表名相同,但拓展名分別是:

  • .frm(存儲表定義);
  • .MYD(MYData,存儲數據);
  • .MYI(MYIndex,存儲索引);

2.2.3、 MEMORY (摘自官網)

Memory存儲引擎將表的數據存放在內存中。

每個MEMORY表實際對應一個磁盤文件,格式是.frm,該文件中只存儲表的結構,而其數據文件,都是存儲在內存中,這樣有利於數據的快速處理,提高整個表的效率。

MEMORY類型的表訪問非常地快,因爲他的數據是存放在內存中的,並且默認使用HASH索引,但是服務一旦關閉,表中的數據就會丟失。

2.2.4、MERGE(摘自官網)

MERGE存儲引擎是一組MyISAM表的組合,這些MyISAM表必須結構完全相同,MERGE表本身並沒有存儲數據,對MERGE類型的表可以進行查詢、更新、刪除操作,這些操作實際上是對內部的MyISAM表進行的。

對於MERGE類型表的插入操作,是通過INSERT_METHOD子句定插入的表,可以有3個不同的值,使用FIRST或LAST值使得插入操作被相應地作用在第一或者最後一個表上,不定義這個子句或者定義爲NO,表示不能對這個MERGE表執行插入操作。

可以對MERGE表進行DROP操作,但是這個操作只是刪除MERGE表的定義,對內部的表是沒有任何影響的。

1).創建3個測試表payment_2006,payment2007,paymentall,其中payment_all是前兩個表的MERGE表:

create table order_1990(
	order_id int, 
	order_money double(10,2), 
	order_address varchar(50), 
	primary key (order_id)
)engine=myisam default charset=utf8;
create table order_1991(
	order_id int, 
	order_money double(10,2), 
	order_address varchar(50), 
	primary key (order_id)
)engine=myisam default charset=utf8;
create table order_all(
	order_id int, 
	order_money double(10,2), 
	order_address varchar(50), 
	primary key(order_id)
)engine=merge union=(order_1990,order_1991) INSERT_METHOD=LAST default charset=utf8;

2).分別向兩張表中插入記錄

insert into order_1990 values(1,100.0,'北京');
insert into order_1990 values(2,100.0,'上海');
insert into order_1991 values(10,200.0,'北京');
insert into order_1991 values(11,200.0,'上海');

3).查詢3張表中的數據。

order_1990中的數據:

order_1991中的數據:

order_all中的數據:

2.3、存儲引擎的選擇

在選擇存儲引擎時,應該根據應用系統的特點選擇合適的存儲引擎。對於複雜的應用系統,還可以根據實際情況選擇多種存儲引擎進行組合。

以下是幾種常用的存儲引擎的使用環境:

(1)·InnoDB:

是Mysql的默認存儲引擎,用於事務處理應用程序,支持外鍵。

如果應用對事務的完整性有比較高的要求,在併發條件下要求數據的一致性,數據操作除了插入和查詢意外,還包含很多的更新、刪除操作,那麼InnoDB存儲引擎是比較合適的選擇。

InnoDB存儲引擎除了有效的降低由於刪除和更新導致的鎖定,還可以確保事務的完整提交和回滾,對於類似於計費系統或者財務系統等對數據準確性要求比較高的系統,InnoDB是最合適的選擇。

(2)MyISAM:

如果應用是以讀操作和插入操作爲主,只有很少的更新和刪除操作,並且對事務的完整性、併發性要求不是很高,那麼選擇這個存儲引擎是非常合適的。

(3)MEMORY:

將所有數據保存在RAM中,在需要快速定位記錄和其他類似數據環境下,可以提供幾塊的訪問。

MEMORY的缺陷就是對錶的大小有限制,太大的表無法緩存在內存中,其次是要確保表的數據可以恢復,數據庫異常終止後表中的數據是可以恢復的。

MEMORY表通常用於更新不太頻繁的小表,用以快速得到訪問結果。

(4)MERGE:

用於將一系列等同的MyISAM表以邏輯方式組合在一起,並作爲一個對象引用他們。

MERGE表的優點在於可以突破對單個MyISAM表的大小限制,並且通過將不同的表分佈在多個磁盤上,可以有效的改善MERGE表的訪問效率。

這對於存儲諸如數據倉儲等VLDB環境十分合適。

 

3、優化SQL步驟

在應用的的開發過程中,由於初期數據量小,開發人員寫SQL語句時更重視功能上的實現。

但是當應用系統正式上線後,隨着生產數據量的急劇增長,很多SQL語句開始逐漸顯露出性能問題,對生產的影響也越來越大,此時這些有問題的SQL語句就成爲整個系統性能的瓶頸。因此必須要對它們進行優化,

當面對一個有SQL性能問題的數據庫時,應該從何處入手來進行系統的分析,使得能夠儘快定位問題SQL並儘快解決問題。

3.1、查看SQL執行頻率

MySQL 客戶端連接成功後,通過 show[session l global] status 命令可以提供服務器狀態信息。

show[session l global] status 可以根據需要加上參數“session"或者“global"來顯示session級(當前連接)的計結果和global級(自數據庫上次啓動至今)的統計結果。如果不寫,默認使用參數是“session"。

下面的命令顯示了當前 session中所有統計參數的值:(7個字符即匹配7個長度的操作名稱)

show status like 'Com_______';

上述查詢的是當前鏈接的status信息,而非整個數據庫的status

查詢全局的可以:

show global status like 'Com_______';

專門針對 InnoDB的查詢

show global status like 'Innodb_rows_%';

Com_xxx表示每個xxx語句執行的次數

Com_***:這些參數對於所有存儲引擎的表操作都會進行累計。

Innodb_***:這幾個參數只是針對lnnoDB存儲引擎的,累加的算法也略有不同。

3.2、定位低效率執行SQL

可以通過以下兩種方式定位執行效率較低的SQL語句。

慢查詢日誌:

通過慢查詢日誌定位那些執行效率較低的SQL語句,用-log-slow-queries[=file_name]選項啓動時,mysqld 寫一個包含所有執行時間超過long_query_time秒的SQL語句的日誌文件。

show processlist:

慢查詢日誌在查詢結束以後才紀錄,所以在應用反映執行效率出現問題的時候查詢慢查詢日誌並不能定位問題,可以使用show processlist 命令查看當前MySQL在進行的線程,包括線程的狀態、是否鎖表等,可以實時地查看SQL的執行情況,同時對一些鎖表操作進行優化。

1)id列,用戶登錄mysql時,系統分配的"connection_id",可以使用函數connection_id()查看

2)user列,顯示當前用戶。如果不是root,這個命令就只顯示用戶權限範圍的sql語句)host列,顯示這個語句是從哪個ip的哪個端口上發的,可以用來跟蹤出現問題語句的用戶

4)db列,顯示這個進程目前連接的是哪個數據庫

5)command列,顯示當前連接的執行的命令,一般取值爲休眠(sleep),查詢(query),連接(connect)等

6)time列,顯示這個狀態持續的時間,單位是秒

7)state列,顯示使用當前連接的sq1語句的狀態,很重要的列。state描述的是語句執行中的某一個狀態。一個sql語句,以查詢爲例,可能需要經過copying to tmp table、sorting result、sending data等狀態纔可以完成

8)info列,顯示這個sql語句,是判斷問題語句的一個重要依據

3.3、explain分析執行計劃

通過以上步驟查詢到效率低的SQL語句後,可以通過EXPLAIN或者DESC命令獲取MySQL如何執行SELECT語句的信息,包括在SELECT語句執行過程中表如何連接和連接的順序。

查詢SQL語句的執行計劃:

explain select * from tb_item where id=1;

explain select * from tb_item where title='阿爾卡特(oT-979)冰川白聯通3G手機3';

eg:

CREATE TABLE t_role(
id varchar(32)NOT NULL, 
role_name varchar(255)DEFAULT NULL,
role_code varchar(255)DEFAULT NULL,
 description varchar(255)DEFAULT NULL, 
 PRIMARY KEY(id), UNIQUE KEY unique_role_name(role_name)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE t_user(
	id varchar(32)NOT NULL,
  username varchar(45)NOT NULL, 
	password varchar(96)NOT NULL, 
	name varchar(45)NOT NULL, 
	PRIMARY KEY(id), 
	UNIQUE KEY unique_user_username( username)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE user_role(
	id int(11)NOT NULL auto_increment,
	user_id varchar(32)DEFAULT NULL, 
	role_id varchar(32)DEFAULT NULL, 
	PRIMARY KEY(id), 
	KEY `fk_ur_user_id`(`user_id`),
	KEY `fk_ur-role_id` (`role_id`), 
	CONSTRAINT fk_ur_role_id FOREIGN KEY ( role_id ) REFERENCES t_role (id) ON DELETE NO ACTION ON UPDATE NO ACTION, 
	CONSTRAINT fk_ur_user_id FOREIGN KEY (user_id) REFERENCES t_user(id)ON DELETE NO ACTION ON UPDATE NO ACTION
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert into t_user(id,username,password,name)
values('1','super','$2a$10STJ4Tmcdk.×4wv/tCqHW14.w70U3CC33ceVncD3SLmyMxMknstqkRe','超級管理員');

insert into t_user(id,username,password,name)
values('2','admin','$2a$10STJ4Tmcdk.×4wv/tCqHW14.w70U3CC33CeVncD3SLmyMXMknstqkRe','系統管理員');

insert into t_user(id,username,password,name)
values('3','itcast','$2a$10$8qmaHgUFUAmPR5pOuwhYwor 291WJYjHelU1Yn07k5ELF8zCrwOCui','test02');

insert into t_user(id,username,password,name)
values('4','stu1','$2a$10$pLtt2KDAFpwTWLjNsmTEi.oU1yozyIn9xkzik/y/spH5rftcpUMza','學生');

insert into t_user(id,username,password,name)
values('5','stu2','$2a$10$nxpKkYSez7uz2rQYUnwhR.z57km3yqkn3Hr/p1FR6ZKgc18u.Tvqm','學生2');

insert into t_user(id,username,password,name)
values('6','t1','$2a$10STJ4Tmcdk.X4wv/tcqHw14.w70u3CC33ceVncD3SLmyMXMknstqkRe','老師1');
INSERT INTO t_role(id, role_name, role_code,description)VALUES('5','學生', 'student', '學生');

INSERT INTO t_role(id, role_name, role_code, description)VALUES('7','老師', 'teacher', '老師');

INSERT INTO t_role(id, role_name, role_code, description)VALUES('8','教學管理員','teachmanager', '教學管理員');

INSERT INTO t_role(id, role_name, role_code, description)VALUES('9','管理員', 'admin','管理員');

INSERT INTO t_role(id, role_name, role_code, description)VALUES('10','超級管理員','super', '超級管理員');
INSERT INTO user_role(id,user_id,role_id)VALUES(NULL,'1','5'),(NULL,'1','7'),(NULL,'2','8'),(NULL,'3',
'9'),(NULL,'4','8'),(NULL,'5','10');

3.3.2、explain之id

id字段是select查詢的序列號,是一組數字,表示的是查詢中執行select子句或者是操作表的順序。id情況有三種:

1)id相同表示加載表的順序是從上到下。

explain select * from t_role r,t_user u,user_role ur where r.id=ur.role_id and u.id=ur.user_id;

2)id 不同id值越大,優先級越高,越先被執行。

EXPLAIN SELECT * FROM t_role WHERE id=(SELECT role_id FROM user_role WHERE uSer_id =(SELECT id FROM t_user
WHERE username='stu1'))

3)id有相同,也有不同,同時存在。id相同的可以認爲是一組,從上往下順序執行;在所有的組中,id的值越大,優先級越高,越先執行。

EXPLAIN SELECT * FROM t_role r,(SELECT * FROM user_role ur WHERE ur.user_id=2) a WHERE r.id=a.role_id;

3.3.3、explain 之select type

表示SELECT的類型,常見的取值,如下表所示:

3.3.4、explain 之table

展示這一行的數據是關於哪一張表的

3.3.5、explain 之 type

type顯示的是訪問類型,是較爲重要的一個指標,可取值爲:

結集值從最好到最壞以此是:

NULL  >  system  >  const  >  eq_ref  > ref  > fulltext  >  ref_or_null  > index_merge  > unique_subquery  >  index_subquery  >  range>index  >  ALL

一般來說,需要保證查詢至少達到range級別,最好達到ref。

3.3.6、explain 之 key

possible_keys:顯示可能應用在這張表的索引,一個或多個。

key:實際使用的索引,如果爲NULL,則沒有使用索引。

key_len:表示索引中使用的字節數,該值爲索引字段最大可能長度,並非實際使用長度,在不損失精確性的前提下,長度越短越好

3.3.7、explain 之rows

掃描行的數量。

3.3.8、explain 之 extra

其他的額外的執行計劃信息,在該列展示。

3.4、show profile分析SQL

Mysql從5.0.37版本開始增加了對 show profiles和show profile 語句的支持。

通過have_profiling參數,能夠看到當前MySQL是否支持profile:select @@have_profiling;

默認profiling是關閉的,可以通過set語句在Session級別開啓profiling:select @@profiling;

set profiling=1;//開啓profiling開關;

通過profile,能夠更清楚地瞭解SQL執行的過程。

show profiles;

如何才能精準的獲取時間到底耗費在哪裏?

show profile for query 5 ;

Sending data:

狀態表示MySQL線程始訪問數據行並把結果返回給客戶端,而不僅僅是返回個客戶端。由於在Sending data狀態下,MySQL線程往往需要做大量的磁盤讀取操作,所以經常是整各查詢中耗時最長的狀態。

在獲取到最消耗時間的線程狀態後,MySQL支持進一步選擇all、cpu、block io、context switch、page faults等明細類型類查看MySQL在使用什麼資源上耗費了過高的時間。

例如,選擇查看CPU的耗費時間:

show profile cpu for query 6;

3.5、trace分析優化器執行計劃

MySQL5.6提供了對SQL的跟蹤trace,通過trace文件能夠進一步瞭解爲什麼優化器選擇A計劃,而不是選擇B計劃。

打開trace,設置格式爲JSON,並設置trace最大能夠使用的內存大小,避免解析過程中因爲默認內存過小而不能夠完整展示。

SET optimizer_trace="enabled=on",end_markers_in_json=on; 
set optimizer_trace_max_mem_size=1000000;

執行SQL語句:

select* from tb_item where id<4;

最後,檢查information_schema.optimizer_trace就可以知道MySQL是如何執行SQL的:

select * from information_schema.optimizer_trace\G;

 

4、索引的使用

索引是數據庫優化最常用也是最重要的手段之一,通過索引通常可以解決大多數的MySQL的性能優化問題。

4.1、驗證索引提升查詢效率

(表結構tb_item中,一共存儲了300萬記錄;)

A.根據ID查詢

select * from tb_item where id=1999\G;

查詢速度很快,接近0s,主要的原因是因爲id爲主鍵,有索引;

處理方案,針對title字段,創建索引:

create index idx_item_title on tb_item(title);

4.2、索引的使用 - 全值匹配

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('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);

4.1.2、避免索引失效

1).全值匹配,對索引中所有列都指定具體值。

改情況下,索引生效,執行效率高。

explain select * from tb_seller where name='小米科技' and status='1' and address='北京市'

2).最左前綴法則(where 條件中的字段先後順序沒有關係)

如果索引了多列,要遵守最左前綴法則。指的是查詢從索引的最左前列開始,並且不跳過索引中的列。

匹配最左前綴法則,走索引:

 

 違法最左前綴法則,索引失效:

(如果有三個字段組成的聯合索引,只有1、3兩個where條件,那麼索引依然生效,但只會匹配最左索引,3是不會走索引的)

3).範圍查詢右邊的列,不能使用索引。

根據前面的兩個字段name,status 查詢是走索引的,但是最後一個條件address 沒有用到索引。

4).不要在索引列上進行運算操作,索引將失效。

5).字符串不加單引號,造成索引失效。

6).儘量使用覆蓋索引,避免select *

儘量使用覆蓋索引(只訪問索引的查詢(索引列完全包含查詢列)),減/少select*。

select * 時 如果匹配了最左索引,extra 會出現 Using index condition   雖然使用了索引並用到name字段,但在返回數據時還需要進行索引回表查詢數據

如果查詢列,超出索引列,也會降低性能。

using index:使用覆蓋索引的時候就會出現

using where:在查找使用索引的情況下,需要回表去查詢所需的數據

using index condition:查找使用了索引,但是需要回表查詢數據

using index;using where:查找使用了索引,但是需要的數據都在索引列中能找到,所以不需要回表查詢數據

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

示例,name字段是索引列,而createtime不是索引列,中間是or進行連接是不走索引的:(也即全表掃描)

如果是 and 則會走索引

explain select * from tb_seller where name='小米' or createtime='2088-01-0112:00:00'

8).以%開頭的Like模糊查詢,索引失效。如果僅僅是尾部模糊匹配,索引不會失效。如果是頭部模糊匹配,索引失效。

explain select * from tb_seller where name like '小米%'

explain select * from tb_seller where name like '%小米'

explain select * from tb_seller where name like '%小米%'

解決方案:使用覆蓋索引(也即select 索引字段 from)

explain select sellerid,name,status,address from tb_seller where name like '%科技%'

9).如果MySQL評估使用索引比全表更慢,則不使用索引。

10).is NULL,is NOT NULL 有時索引失效。

由於數據表中的數據大多是非空的,所以會走全表掃描,這時候比走索引更快

如果數據基本是not null的,則查詢 is nulll 時會走索引

11).in 走索引,not in索引失效。

12).單列索引和複合索引。儘量使用複合索引,而少使用單列索引。

創建複合索引

create index idx_name_sta_address on tb_seller(name,status,address);

就相當於創建了三個索引:
name 
name+status 
name+status+address

創建單列索引

只有單列的索引時,MySQL會選擇辨識度最高的索引列

create index idx_seller_name on tb_se1ler(name); 
create index idx_seller_status on tb_seller(status); 
create index idx_seller_address on tb_seller(address);

數據庫會選擇一個最優的索引來使用,並不會使用全部索引。

4.3、查看索引使用情況

show status like 'Handler_read%'; 

show global status like 'Handler_read%';

(1)Handler_read_first:索引中第一條被讀的次數。如果較高,表示服務器正執行大量全索引掃描(這個值越低越好)。

(2)Handler_read_key:如果索引正在工作,這個值代表一個行被索引值讀的次數,如果值越低,表示索引得到的性能改善不高,因爲索引不經常使用(這個值越高越好)

(3)Handler_read_next:按照鍵順序讀下一行的請求數。如果你用範圍約束或如果執行索引掃描來查詢索引列,該值增加。

(4)Handler_read_prev:按照鍵順序讀前一行的請求數。該讀方法主要用於優化ORDER BY...DESC。

(5)Handler_read_rnd:根據固定位置讀一行的請求數。如果你正執行大量查詢並需要對結果進行排序該值較高。可能使用了大量需要MySQL掃描整個表的查詢或你的連接沒有正確使用鍵。這個值較高,意味着運行效率低,應該建立索引來補救。

(6)Handler_read_rnd_next:在數據文件中讀下一行的請求數。如果你正進行大量的表掃描,該值較高。通常說明你的表索引不正確或寫入的查詢沒有利用索引。

 

5、SQL優化

5.1、大批量插入數據

CREATE TABLE tb_user_2(
id int(11)NOT NULL AUTO_INCREMENT,
username varchar(45) NOT NULL,
password varchar(96) NOT NULL,
name varchar(45)NOT NULL,
birthday datetime DEFAULT NULL,
sex char(1)DEFAULT NULL,
email varchar(45)DEFAULT NULL,
phone varchar(45)DEFAULT NULL,
qq varchar(32)DEFAULT NULL,
status varchar(32)NOT NULL COMMENT '用戶狀態',
create_time datetime NOT NULL,
update_time datetime DEFAULT NULL,
PRIMARY KEY(id),UNIQUE KEY unique_user_username (username)
)ENGINE=InnoDB DEFAULT CHARSET=utf8

當使用  load  命令導入數據的時候,適當的設置可以提高導入的效率。

對於InnoDB類型的表,有以下幾種方式可以提高導入的效率:

1)主鍵順序插入

因爲InnoDB類型的表會按照主鍵的順序保存的,所以將導入的數據按照主鍵的順序排列,可以有效的提高導入數據的效率。

如果InnoDB表沒有主鍵,那麼系統會自動默認創建一個內部列作爲主鍵,所以如果可以給表創建一個主鍵,將可以利用這點,來提高導入數據的效率。

腳本文件介紹:
sq11.1og---->主鍵有序
sq12.1og---->主鍵無序

插入ID順序排列數據:

load data local infile '/root/sqll. log' into table "tb user 1' fields terminated by ',' lines terminated by '\n';

插入ID無序排列數據:

load data local infile '/root/sql2. log' into table "tb user 2' fields terminated by ',' lines terminated by '\n';

2)關閉唯一性校驗

在導入數據前執行 SET UNIQUE_CHECKS=0,關閉唯一性校驗,在導入結束後執行SETUNIQUE_CHECKS=1,恢復唯一性校驗,可以提高導入的效率。

3.)手動提交事務

如果應用使用自動提交的方式,建議在導入前執行 SET AUTOCOMMIT0,關閉自動提交,導入結束後再執行 SET AUTOCOMMIT=1,打開自動提交,也可以提高導入的效率。

5.2、優化insert語句

當進行數據的insert操作的時候,可以考慮採用以下幾種優化方案。

(1)儘量使用多個值表的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');

(2)在事務中進行數據插入。

start transaction; 

insert into tb_test values(1,'Tom');

insert into tb_test values(2,'Cat'); 

insert into tb_test values(3,'Jerry'); 

commit;

(3)數據有序插入

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);

5.3、優化order by語句

CREATE TABLE emp (
	id int ( 11 ) NOT NULL AUTO_INCREMENT,
	NAME VARCHAR ( 100 ) NOT NULL,
	age INT ( 3 ) NOT NULL,
	salary INT ( 11 ) DEFAULT NULL,
PRIMARY KEY ( id ) 
) ENGINE = INNODB DEFAULT CHARSET = utf8mb4
insert into emp(id,name,age,salary)values('1','Tom','25','2300');

insert into emp(id,name,age,salary)values('2','Jerry','30','3500'); 

insert into emp(id,name,age,salary)values('3','Luci','25','2800'); 

insert into emp(id,name,age,salary)values('4','Jay','36','3500'); 

insert into emp(id,name,age,salary)values('5','Tom2','21','2200'); 

insert into emp(id,name,age,salary)values('6','Jerry2','31','3300'); 

insert into emp(id,name,age,salary)values('7','Luci2','26','2700');

 insert into emp(id,name,age,salary)values('8','Jay2','33','3500'); 
 
 insert into emp(id,name,age,salary)values('9','Tom','23','2400'); 
 
 insert into emp(id,name,age,salary)values('10','Jerry3','32','3100');

 insert into emp(id,name,age,salary)values('11','Luci3','26','2900'); 
 
 insert into emp(id,name,age,salary)values('12','Jay3','37','4500'); 
 create index idxemp_age_salary on emp(age,salary);

5.3.2、兩種排序方式

1).第一種是通過對返回數據進行排序,也就是通常說的 filesort 排序,所有不是通過索引直接返回排序結果的排序都叫FileSort排序。

2).第二種通過有序索引順序掃描直接返回有序數據,這種情況即爲using index,不需要額外排序,操作效率高。

EXPLAIN select id from emp ORDER BY age DESC

(3)多字段排序

explain select id,age,salary from emp order by age,salary;

explain select id,age,salary from emp order by age asc,salary desc;

瞭解了MySQL的排序方式,優化目標就清晰了:

儘量減少額外的排序,通過索引直接返回有序數據。

where條件和Order by使用相同的索引,並且Order By的順序和索引順序相同,Order by的字段都是升序,或者都是降序。

否則肯定需要額外的操作,這樣就會出現FileSort。

5.3.3 、Filesort的優化

通過創建合適的索引,能夠減少Filesort的出現,但是在某些情況下,條件限制不能讓Filesort消失,那就需要加快Filesort的排序操作。對於Filesort,MySQL 有兩種排序算法:

1)兩次掃描算法:MySQL4.1之前,使用該方式排序。首先根據條件取出排序字段和行指針信息,然後在排序區sort buffer中排序,如果sort buffer不夠,則在臨時表temporary table中存儲排序結果。完成排序之後,再根據行指針回表讀取記錄,該操作可能會導致大量隨機1/O操作。

2)一次掃描算法:一次性取出滿足條件的所有字段,然後在排序區 sort buffer中排序後直接輸出結果集。排序時內存開銷較大,但是排序效率比兩次掃描算法要高。

MySQL 通過比較系統變量max_length_for_sort_data的大小和Query語句取出的字段總大小,來判定是否那種排序算法,如果max_length_for_sort_data 更大,那麼使用第二種優化之後的算法;否則使用第一種。

可以適當提高sort_buffer_size和max_length_for_sort_data系統變量,來增大排序區的大小,提高排序的效率。

5.4、優化group by語句

由於GROUPBY實際上也同樣會進行排序操作,而且與ORDER BY相比,GROUPBY主要只是多了排序之後的分組操作。當然,如果在分組的時候還使用了其他的一些聚合函數,那麼還需要一些聚合函數的計算。所以,在GROUPBY的實現過程中,與ORDER一樣也可以利用到索引

如果查詢包含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 nul 不需要進行“filesort",而上文提過Filesort往往非常耗時

通過創建索引:

create index idx_emp_age_salary on emp(age,salary);

5.5、優化嵌套查詢

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;

儘量使用多表查詢,用以替代子查詢

5.6、優化OR條件

對於包含OR的查詢子句,如果要利用索引,則OR之間的每個條件列都必須用到索引,而且不能使用到複合素引;如果沒有索引,則應該考慮增加索引。

獲取emp表中的所有的索引:

explain select * from emp where id=1 or age=30;

建議使用union 替換or

type顯示的是訪問類型,是較爲重要的一個指標,結果值從好到壞依次是:

UNION 語句的type值爲ref,OR語句的type值爲range,可以看到這是一個很明顯的差距

UNION 語句的ref值爲const,OR語句的type值爲null,const 表示是常量值引用,非常快

這兩項的差距就說明了UNION要優於OR。

5.7、優化分頁查詢

一般分頁查詢時,通過創建覆蓋索引能夠比較好地提高性能。一個常見又非常頭疼的問題就是limit 2000000,10,此時需要MySQL排序前2000010記錄,僅僅返回2000000-2000010的記錄,其他記錄丟棄,查詢排序的代價非常大。

5.7.1、優化思路一

在索引上完成排序分頁操作,最後根據主鍵關聯回原表查詢所需要的其他列內容。

explain select * from tb_item t,(select id from tb_item order by id limit 2000000,10)a where t. id=a. id;

5.7.2、優化思路二

該方案適用於主鍵自增的表,可以把Limit 查詢轉換成某個位置的查詢。

5.8、使用SQL提示

SQL提示,是優化數據庫的一個重要手段,簡單來說,就是在SQL語句中加入一些人爲的提示來達到優化操作的目的。

5.8.1 USE INDEX

在查詢語句中表名的後面,添加use index來提供希望MySQL去參考的索引列表,就可以讓MySQL不再考慮其他可用的索引。

create index idx_seller_name on tb_seller(name);

5.8.2、 IGNORE INDEX

如果用戶只是單純的想讓MySQL忽略一個或者多個索引,則可以使用ignore index作爲hint。

explain select * from tb_seller ignore index(idx_seller_name)where name= '小米科技';

5.8.3 、FORCE INDEX

爲強制MySQL使用一個特定的索引,可在查詢中使用force index作爲hint。

create index idx_seller_address on tb_seller(address);

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章