史上最全mysql刪庫跑路必會姿勢【轉載】

基礎篇:MySql架構與存儲引擎

邏輯架構圖:

 

 

 

 連接層:

 

mysql啓動後(可以把mysql類比爲一個後臺的服務器),等待客戶端請求,當請求到來後,mysql建立一個一個線程處理(線程池則分配一個空線程,當然也可使用nio線程模型。),每個線程獨立,擁有獨自內存空間。當請求爲select請求則沒有關係,但是請求爲update時,多線程同時修改一塊內存,就會引發一系列問題,由此引出 “鎖“的概念。

查看mysql當前連接數:

show VARIABLES like '%max_connections%'

修改mysql連接數爲200
set GLOBAL max_connections = 200

 

同時當客戶端連接到mysql服務器時,服務器會對其進行權限驗證,包括,ip,用戶名,密碼的驗證,同時還要驗證是否有對操作某一個庫,表的權限。

 

SQL處理層:

 

 

show variables like '%query_cache_type%'

SET GLOBAL query_cache_size = 188888888;   

是否打開mysql查詢結果緩存,默認關閉,打開後,mysql會對查詢出來的結果進行緩存,實際應用中業務數據一般不在db層緩存

mysql默認打開sql解析緩存;平時我們說的sql緩存,一般指的sql解析緩存。

解析查詢:

 

 

mysql對客戶端傳入的sql語句會按照一定的規則進行sql解析,而後進行sql優化,最後執行優化過的sql語句。而不是直接執行。

存儲引擎:

show engines;查看存儲引擎

 

MyISAM

 

進入show VARIABLES like 'datadir'  查看mysql數據文件所在路徑,發現myisam引擎會生產三個數據文件,xxx.frm存儲表結構文件,所有引擎都有此文件,xxx.MYD存儲表數據文件,xxx.MYI存儲表索引文件

特性:

併發性

支持表級鎖,

支持全文檢索,

支持數據文件壓縮

試用場景:

只讀類應用;

非事物行應用(數據倉庫,報表,數據)

空間類應用(座標)

 Innodb

 

show VARIABLES like 'innodb_file_per_table' 查看innodb表空間類型
set global innodb_file_per_table=off      設置innodb使用系統表空間

mysql5.6以前默認使用系統表空間

系統表空間無法簡單的收縮文件大小。

獨立表空可以通過optimize table 收縮系統文件

系統表空間會產生io瓶頸

獨立表空間可以同時向多個文件刷新數據

推薦使用獨立表空間

特性:

Innodb是一種事務性存儲引擎

完全支持事物的acid特性

redo Log和Undo Log

Innodb支持行級鎖(併發度更高)

試用場景

適合大多數的oltp應用。

 

 

 csv

 

 

 特點:

    以csv格式進行數據存儲,

    所有列都不能爲空,

    不支持索引,

    可以直接對數據文件直接編輯(直接修改文本文件,達到修改表的目的)。

create table mycsv(id int not null,c1 VARCHAR(10) not null,c2 char(10) not null)
engine=csv;
insert into mycsv values(1,'aaa','bbb'),(2,'cccc','dddd');
修改文本數據
flush TABLES;
select * from mycsv
create index idx_id on mycsv(id)

應用場景:

需要頻繁導入導出表數據的場景,如財務報表類

 

Archive

 

 

應用場景:日誌以及數據採集。

 

 Memory

 

show VARIABLES like 'max_heap_table_size' 查看memory引擎最大空間。

 

 

使用場景:

hash索引用於查找或者是映射表(郵編和地區對應)

用於保存數據分析中產生的中間表

用於緩存週期性聚合數據的結果表

memory數據容易丟失,所以要求數據可再生。

 

Ferderted

特點:提供了遠程訪問mysql服務器上表的方法

           本地不存儲數據,數據全部放到遠程服務器上

           本地需要保存表結構和遠程服務器的連接信息

使用場景:邊界數據庫,表 同步

該引擎默認禁止,啓用時需增加federated參數

 

 

表明,列名需要與遠程表相同

CREATE TABLE `local_fed` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`c1` varchar(10) NOT NULL DEFAULT '',
`c2` char(10) NOT NULL DEFAULT '',
PRIMARY KEY (`id`)
) ENGINE=federated CONNECTION
='mysql://root:[email protected]:3306/remote/remote_fed'

應用篇:鎖,事物,索引   

 

MyISAM表級鎖的兩種模式:table read lock 表共享讀鎖;table write Lock 表獨佔寫鎖;

 

 

加共享表級讀鎖: lock table 表名 read

1. lock table testmysam READ

啓動另外一個session select * from
testmysam 可以查詢
2. insert into testmysam value(2);
update testmysam set id=2 where id=1;
報錯
3.在另外一個session中
insert into testmysam value(2); 等待
4.在同一個session中
insert into testdemo value(2,'2','3'); 報錯
select * from testdemo ; 報錯
5.在另外一個session中
insert into testdemo value(2,'2','3'); 成功
6.加鎖在同一個session 中 select s.* from testmysam s 報錯
lock table 表名 as 別名 read;
查看 show status LIKE 'table_locks_waited' 表被鎖過幾次

 

 加獨佔表級寫鎖:lock table 表名 write

1.lock table testmysam WRITE
在同一個session中
insert testmysam value(3);
delete from testmysam where id = 3
select * from testmysam
2.對不同的表操作(報錯)
select s.* from testmysam s
insert into testdemo value(2,'2','3');
3.在其他session中 (等待)
select * from testmysam

 

Innodb行鎖

 共享行鎖又稱讀鎖;當一個事務對某幾行上讀鎖時,允許其他事務對這幾行讀操作,但是不予許其進行寫操作,也不予許其他事務給這幾行上排它鎖,但允許上讀鎖。

排它鎖又稱寫鎖;當一個事務對某幾行上寫鎖時,允許其他事務對這幾行讀操作,不予許其進行寫操作,更不予許其他事務給這幾行上鎖,包括讀鎖。

注意:1.兩個事務不能鎖同一個索引。

           2.insert,delete,update在事務中會默認加上排它鎖

           3.行鎖必須有索引才能實現,否則會自動寫全表,那麼就不是行鎖了

1.
BEGIN
select * from testdemo where id =1 for update
在另外一個session中
update testdemo set c1 = '1' where id = 2 成功
update testdemo set c1 = '1' where id = 1 等待
2.BEGIN
update testdemo set c1 = '1' where id = 1
在另外一個session中
update testdemo set c1 = '1' where id = 1 等待
3.
BEGIN
update testdemo set c1 = '1' where c1 = '1'
在另外一個session中
update testdemo set c1 = '2' where c1 = '2' 等待

 

爲什麼需要事務

現在的很多軟件都是多用戶,多程序,多線程的,對同一個表可能同時有很多人在用,爲保持數據的一致性,所以提出了事務的概念。

 

事務的特性

事務應該具有4個屬性:原子性、一致性、隔離性、持久性。這四個屬性通常稱爲ACID特性。

原子性(atomicity)。一個事務是一個不可分割的工作單位,事務中包括的諸操作要麼都做,要麼都不做。

一致性(consistency)。事務必須是使數據庫從一個一致性狀態變到另一個一致性狀態。一致性與原子性是密切相關的。

隔離性(isolation)。一個事務的執行不能被其他事務干擾。即一個事務內部的操作及使用的數據對併發的其他事務是隔離的,併發執行的各個事務之間不能互相干擾。

持久性(durability)。持久性也稱永久性(permanence),指一個事務一旦提交,它對數據庫中數據的改變就應該是永久性的。接下來的其他操作或故障不應該對其有任何影響。

 

事務隔離級別

mysql默認的事務隔離級別爲repeatable-read

show variables like '%tx_isolation%';

 

 

未提交讀(READ UNCOMMITED) 解決的障礙:無; 引入的問題:髒讀

 

set SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

 

測試:

 

啓動兩個session

 

一個session中

 

  start TRANSACTION

 

  update account set balance = balance -50 where id = 1

 

另外一個session中查詢

 

select * from account

 

回到第一個session中 回滾事務

 

ROLLBACK

 

在第二個session種

 

update account set balance = balance -50 where id = 1

 

查詢結果還是 400

 

第二個session以爲結果是350,但前面的400數據爲髒讀數據,導致最後的結果和意料中的結果並不一致。

 

已提交讀 (READ COMMITED) 解決的障礙:髒讀; 引入的問題:不可重複讀

測試

show variables like '%tx_isolation%';

set SESSION TRANSACTION ISOLATION LEVEL read committed;

一個session中

  start TRANSACTION

  update account set balance = balance -50 where id = 1

另外一個session中查詢 (數據並沒改變)

select * from account

回到第一個session中 回滾事務

commit

在第二個session種

select * from account (數據已經改變)

 

可重複讀(REPEATABLE READ)解決的障礙:不可重複讀; 引入的問題:幻讀

 

測試

show variables like '%tx_isolation%';

set SESSION TRANSACTION ISOLATION LEVEL repeatable read;

一個session中

  start TRANSACTION

  update account set balance = balance -50 where id = 1

另外一個session中查詢 (數據並沒改變)

select * from account

回到第一個session中 回滾事務

commit

在第二個session種

select * from account (數據並未改變)

可串行化(SERIALIZABLE)解決的障礙:可重複讀; 引入的問題:鎖全表,性能低下

測試

show variables like '%tx_isolation%';

set SESSION TRANSACTION ISOLATION LEVEL repeatable read;

account 表有3條記錄,業務規定,最多允許4條記錄。

開啓一個事務

begin

select * from account  發現3條記錄 

開啓另外一個事務

begin

select * from account  發現3條記錄 也是3條記錄

insert into account VALUES(4,'deer',500)

查詢  4條記錄

select * from account

 回到第一個session

insert into account VALUES(5,'james',500)

select * from account  4條記錄

session1 與 session2 都提交事務 

set SESSION TRANSACTION ISOLATION LEVEL serializable; 重新上面的測試發現插入報錯

 

總結:

事務隔離級別爲可重複讀時,如果有索引(包括主鍵索引)的時候,以索引列爲條件更新數據,會存在間隙鎖間、行鎖、頁鎖的問題,從而鎖住一些行;如果沒有索引,更新數據時會鎖住整張表

事務隔離級別爲串行化時,讀寫數據都會鎖住整張表

 隔離級別越高,越能保證數據的完整性和一致性,但是對併發性能的影響也越大,對於多數應用程序,可以優先考慮把數據庫系統的隔離級別設爲Read Committed,它能夠避免髒讀取,而且具有較好的併發性能。

 

 

索引是什麼

MySQL官方對索引的定義爲:索引(Index)是幫助MySQL高效獲取數據的數據結構。

可以得到索引的本質:索引是數據結構

平時我們到圖書館,首先看到的都是目錄,通過目錄去查詢想要的書籍會非常的迅速。

 

我們要去圖書館找一本書,這圖書館的書肯定不是線性存放的,它對不同的書籍內容進行了分類存放,整索引由於一個個節點組成,根節點有中間節點,中間節點下面又由子節點,最後一層是葉子節點,

可見,整個索引結構是一棵倒掛着的樹,其實它就是一種數據結構,這種數據結構比前面講到的線性目錄更好的增加了查詢的速度。

 

MySql中的索引其實也是這麼一回事,我們可以在數據庫中建立一系列的索引,比如創建主鍵的時候默認會創建主鍵索引,上圖是一種BTREE的索引。每一個節點都是主鍵的Id

當我們通過ID來查詢內容的時候,首先去查索引庫,在到索引庫後能快速的定位索引的具體位置。

 

索引得分類

 

普通索引:即一個索引只包含單個列,一個表可以有多個單列索引

 

唯一索引:索引列的值必須唯一,但允許有空值

 

複合索引:即一個索引包含多個列

 

聚簇索引(聚集索引):並不是一種單獨的索引類型,而是一種數據存儲方式(索引與數據放在同一個文件裏)。具體細節取決於不同的實現,InnoDB的聚簇索引其實就是在同一個結構中保存了B-Tree索引(技術上來說是B+Tree)和數據行。

 

非聚簇索引:不是聚簇索引,就是非聚簇索引

查看索引

SHOW INDEX FROM table_name\G

創建索引

CREATE  [UNIQUE ] INDEX indexName ON mytable(columnname(length));

ALTER TABLE 表名 ADD  [UNIQUE ]  INDEX [indexName] ON (columnname(length))

刪除索引

DROP INDEX [indexName] ON mytable;

 

優化篇:慢查詢 ,執行計劃,sql優化

  什麼是慢查詢

慢查詢日誌,顧名思義,就是查詢慢的日誌,是指mysql記錄所有執行超過long_query_time參數設定的時間閾值的SQL語句的日誌。該日誌能爲SQL語句的優化帶來很好的幫助。默認情況下,慢查詢日誌是關閉的,要使用慢查詢日誌功能,首先要開啓慢查詢日誌功能。

 慢查詢基本配置

 slow_query_log 啓動停止技術慢查詢日誌

 slow_query_log_file 指定慢查詢日誌得存儲路徑及文件(默認和數據文件放一起)

 long_query_time 指定記錄慢查詢日誌SQL執行時間得伐值(單位:秒,默認10秒)

 log_queries_not_using_indexes  是否記錄未使用索引的SQL

 log_output 日誌存放的地方【TABLE】【FILE】【FILE,TABLE】

配置了慢查詢後,它會記錄符合條件的SQL

包括:

 查詢語句

數據修改語句

已經回滾得SQL

實操:

通過下面命令查看下上面的配置:

show VARIABLES like '%slow_query_log%'

show VARIABLES like '%slow_query_log_file%'

show VARIABLES like '%long_query_time%'

show VARIABLES like '%log_queries_not_using_indexes%'

show VARIABLES like 'log_output'

set global long_query_time=0;   ---默認10秒,這裏爲了演示方便設置爲0

set GLOBAL  slow_query_log = 1; --開啓慢查詢日誌

set global log_output='FILE,TABLE'  --項目開發中日誌只能記錄在日誌文件中,不能記表中

設置完成後,查詢一些列表可以發現慢查詢的日誌文件裏面有數據了。

 

 慢查詢解讀

從慢查詢日誌裏面摘選一條慢查詢日誌,數據組成如下

 

第一行:用戶名 、用戶的IP信息、線程ID號

第二行:執行花費的時間【單位:毫秒】

第三行:執行獲得鎖的時間

第四行:獲得的結果行數

第五行:掃描的數據行數

第六行:這SQL執行的具體時間

第七行:具體的SQL語句

 

 

執行計劃

  使用EXPLAIN關鍵字可以模擬優化器執行SQL查詢語句,從而知道MySQL是如何處理你的SQL語句的。分析你的查詢語句或是表結構的性能瓶頸。

 執行計劃作用

 表的讀取順序

 數據讀取操作的操作類型

 哪些索引可以使用

 哪些索引被實際使用

 表之間的引用

 每張表有多少行被優化器查詢

 

執行計劃的語法

執行計劃的語法其實非常簡單: 在SQL查詢的前面加上EXPLAIN關鍵字就行。

比如:EXPLAIN select * from table1

重點的就是EXPLAIN後面你要分析的SQL語句 

 

 

ID列

ID列:描述select查詢的序列號,包含一組數字,表示查詢中執行select子句或操作表的順序

根據ID的數值結果可以分成一下三種情況

 id相同:執行順序由上至下

 id不同:如果是子查詢,id的序號會遞增,id值越大優先級越高,越先被執行

 id相同不同:同時存在

分別舉例來看

 

如上圖所示,ID列的值全爲1,代表執行的允許從t1開始加載,依次爲t3與t2

 

EXPLAIN

select t2.* from t1,t2,t3  where t1.id = t2.id and t1.id = t3.id

and t1.other_column = '';

Id不同

 

如果是子查詢,id的序號會遞增,id值越大優先級越高,越先被執行

EXPLAIN

select t2.* from  t2 where id = (

select id from t1 where id =  (select t3.id from t3 where t3.other_column='')

);

 Id相同又不同

 

id如果相同,可以認爲是一組,從上往下順序執行;

在所有組中,id值越大,優先級越高,越先執行

EXPLAIN

select t2.* from (

 select t3.id

from t3 where t3.other_column = ''

) s1 ,t2 where s1.id = t2.id

 select_type列

Select_type:查詢的類型,

要是用於區別:普通查詢、聯合查詢、子查詢等的複雜查詢

類型如下

 

  SIMPLE

EXPLAIN select * from t1

簡單的 select 查詢,查詢中不包含子查詢或者UNION

 

PRIMARY與SUBQUERY

PRIMARY:查詢中若包含任何複雜的子部分,最外層查詢則被標記爲

SUBQUERY:在SELECT或WHERE列表中包含了子查詢

EXPLAIN

select t1.*,(select t2.id from t2 where t2.id = 1 ) from t1 

 

 

 DERIVED

在FROM列表中包含的子查詢被標記爲DERIVED(衍生)

MySQL會遞歸執行這些子查詢, 把結果放在臨時表裏。

 

.UNION RESULT 與UNION

UNION:若第二個SELECT出現在UNION之後,則被標記爲UNION;

UNION RESULT:從UNION表獲取結果的SELECT

#UNION RESULT ,UNION

EXPLAIN

select * from t1

UNION

select * from t2

 

 table列

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

 

Type列

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

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。

 System與const

System:表只有一行記錄(等於系統表),這是const類型的特列,平時不會出現,這個也可以忽略不計

Const:表示通過索引一次就找到了

const用於比較primary key或者unique索引。因爲只匹配一行數據,所以很快

如將主鍵置於where列表中,MySQL就能將該查詢轉換爲一個常量

 

  eq_ref

 唯一性索引掃描,對於每個索引鍵,表中只有一條記錄與之匹配。常見於主鍵或唯一索引掃描

 

 Ref

 非唯一性索引掃描,返回匹配某個單獨值的所有行.

本質上也是一種索引訪問,它返回所有匹配某個單獨值的行,然而,它可能會找到多個符合條件的行,所以他應該屬於查找和掃描的混合體

 

 Range

只檢索給定範圍的行,使用一個索引來選擇行。key 列顯示使用了哪個索引

一般就是在你的where語句中出現了between、<、>、in等的查詢

這種範圍掃描索引掃描比全表掃描要好,因爲它只需要開始於索引的某一點,而結束語另一點,不用掃描全部索引。

 

 

Index

當查詢的結果全爲索引列的時候,雖然也是全部掃描,但是隻查詢的索引庫,而沒有去查詢

數據。

 

All

Full Table Scan,將遍歷全表以找到匹配的行

 

 

possible_keys 與Key 

possible_keys:可能使用的key

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

查詢中若使用了覆蓋索引,則該索引和查詢的select字段重疊

 

 key_len

Key_len表示索引中使用的字節數,可通過該列計算查詢中使用的索引的長度。在不損失精確性的情況下,長度越短越好

key_len顯示的值爲索引字段的最大可能長度,並非實際使用長度,即key_len是根據表定義計算而得,不是通過表內檢索出的

 

 

 key_len表示索引使用的字節數,

 根據這個值,就可以判斷索引使用情況,特別是在組合索引的時候,判斷所有的索引字段是否都被查詢用到。

 char和varchar跟字符編碼也有密切的聯繫,

 latin1佔用1個字節,gbk佔用2個字節,utf8佔用3個字節。(不同字符編碼佔用的存儲空間不同)

 

 

字符類型

 

字符類型-索引字段爲char類型+不可爲Null時

 

name這一列爲char(10),字符集爲utf-8佔用3個字節Keylen=10*3

字符類型-索引字段爲char類型+允許爲Null時

 

name這一列爲char(10),字符集爲utf-8佔用3個字節,外加需要存入一個null值

Keylen=10*3+1(null) 結果爲31

索引字段爲varchar類型+不可爲Null時

 

Keylen=varchar(n)變長字段+不允許Null=n*(utf8=3,gbk=2,latin1=1)+2

 索引字段爲varchar類型+允許爲Null時

 

Keylen=varchar(n)變長字段+允許Null=n*(utf8=3,gbk=2,latin1=1)+1(NULL)+2

 

總結

字符類型

變長字段需要額外的2個字節(VARCHAR值保存時只保存需要的字符數,另加一個字節來記錄長度(如果列聲明的長度超過255,則使用兩個字節),所以VARCAHR索引長度計算時候要加2),固定長度字段不需要額外的字節。

而NULL都需要1個字節的額外空間,所以索引字段最好不要爲NULL,因爲NULL讓統計更加複雜並且需要額外的存儲空間。

複合索引有最左前綴的特性,如果複合索引能全部使用上,則是複合索引字段的索引長度之和,這也可以用來判定複合索引是否部分使用,還是全部使用。

整數/浮點數/時間類型的索引長度

NOT NULL=字段本身的字段長度

        NULL=字段本身的字段長度+1(因爲需要有是否爲空的標記,這個標記需要佔用1個字節)

datetime類型在5.6中字段長度是5個字節,datetime類型在5.5中字段長度是8個字節

 

 Ref

顯示索引的哪一列被使用了,如果可能的話,是一個常數。哪些列或常量被用於查找索引列上的值

 

由key_len可知t1表的idx_col1_col2被充分使用,col1匹配t2表的col1,col2匹配了一個常量,即 'ac'

其中 【shared.t2.col1】 爲 【數據庫.表.列】

Rows

根據表統計信息及索引選用情況,大致估算出找到所需的記錄所需要讀取的行數

 

Extra

包含不適合在其他列中顯示但十分重要的額外信息。

 

 Using filesort

說明mysql會對數據使用一個外部的索引排序,而不是按照表內的索引順序進行讀取。MySQL中無法利用索引完成的排序操作稱爲“文件排序”

當發現有Using filesort 後,實際上就是發現了可以優化的地方

 

上圖其實是一種索引失效的情況,後面會講,可以看出查詢中用到了個聯合索引,索引分別爲col1,col2,col3

 

當我排序新增了個col2,發現using filesort 就沒有了。

 

Using temporary

 

使了用臨時表保存中間結果,MySQL在對查詢結果排序時使用臨時表。常見於排序 order by 和分組查詢 group by。

 

 

尤其發現在執行計劃裏面有using filesort而且還有Using temporary的時候,特別需要注意

Using index

表示相應的select操作中使用了覆蓋索引(Covering Index),避免訪問了表的數據行,效率不錯!

如果同時出現using where,表明索引被用來執行索引鍵值的查找;

 

如果沒有同時出現using where,表明索引用來讀取數據而非執行查找動作

 

 

 覆蓋索引:

覆蓋索引(Covering Index),一說爲索引覆蓋。

理解方式一:就是select的數據列只用從索引中就能夠取得,不必讀取數據行,MySQL可以利用索引返回select列表中的字段,而不必根據索引再次讀取數據文件,換句話說查詢列要被所建的索引覆蓋。

理解方式二:索引是高效找到行的一個方法,但是一般數據庫也能使用索引找到一個列的數據,因此它不必讀取整個行。畢竟索引葉子節點存儲了它們索引的數據;當能通過讀取索引就可以得到想要的數據,那就不需要讀取行了。一個索引包含了(或覆蓋了)滿足查詢結果的數據就叫做覆蓋索引

注意:

如果要使用覆蓋索引,一定要注意select列表中只取出需要的列,不可select *,

因爲如果將所有字段一起做索引會導致索引文件過大,查詢性能下降。

所以,千萬不能爲了查詢而在所有列上都建立索引,會嚴重影響修改維護的性能。

 

Using where 與 using join buffer

Using where

表明使用了where過濾

using join buffer

使用了連接緩存:

 

 impossible where

where子句的值總是false,不能用來獲取任何元組

 

 

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