目錄
1前言
對於現在市場上的數據庫操作需求而言,如果你僅僅只會增刪改查,就太low了。日前數據量指數級增加,對高併發等問題的解決成了對數據庫操作的基本功,所有對於大廠來說,肯定會考數據庫相關的(非增刪改查)知識。
優化sql數據庫分析步驟:
1、慢查詢的開啓並捕獲
觀察,至少跑一天,看看生產的慢sql情況
開啓慢查詢日誌,設置閾值,超過閾值的就是慢sql,並將他抓取出來
2、explain+慢sql分析
3、show profile查詢sql在MySQL服務器裏面的執行細節和生命週期情況
4、sql數據庫服務器的參數調優。
2索引優化分析
2.1問題
性能下降sql慢,執行時間太長,等待時間太長
原因:
1、索引失效
2、查詢語句寫的爛
3、關聯查詢太多join(設計缺陷或不得已的需求)
4、服務器調優及各個參數設置(緩存、線程數等)
2.2常見通用的Join查詢⭐
ps:MySQL並不支持full outer語法(oracl支持)。
怎麼辦?
對於上圖左下角的查詢(AB並集):
可以把左上角和右上角的查詢加起來,就是最下角的查詢(AB並集):
select * from tableA A left join tableB B on A.key=B.key
union
select * from tableA A right join tableB B on A.key=B.key;
對於上圖右下角的查詢:
同理:
select * from tableA A left join tableB B on A.key=B.key where B.key is null
union
select * from tableA A right join tableB B on A.key=B.key where A.key is null;
2.3手寫和機讀的不同
手寫:
上面的語句再MySQL眼中的(機讀):
分析:
2.4索引
2.4.1索引的定義
索引是一種數據結構。
MySQL官方對索引的定義爲:索引(Index)是幫助MySQL高效獲取數據的數據結構。
你可以簡單理解爲“排好序的快速查找數據結構”
索引的數據結構:
在數據之外,數據庫系統還維護這滿足特定查找算法的數據結構,
這些數據結構以某種方式引用數據。
這樣就可以在這些數據結構上實現高級查找算法,
這種數據結構,就是索引。
我們平常所說的索引,如果沒有特別指明,
都是指B樹(多路搜索樹,並不一定是二叉的)數據結構組織的索引
其中聚集索引,次要索引,複合索引,前綴索引,唯一索引默認都是使用B+樹索引
統稱索引。
除了B+樹,還有哈希索引(hash index等)
關鍵字:
1、排序
2、快速查找
3、數據結構
索引的作用:
假設你去圖書館(藏書1000萬本)找玄幻小說《鬥破蒼穹》,
如果沒有這個圖書館索引,就就要一本一本的去找,即全局遍歷
如果這個圖書館有索引,玄幻小時再二樓,天蠶土豆的小說在第二個書架,
那你會很快找到書的位置,即局部遍歷
這就是索引的作用。
一種可能的索引方式示例:
2.4.2索引的優勢和劣勢
優勢:
1、類似大學圖書館建書目索引,提高數據檢索的效率,降低數據庫的IO成本
2、通過索引列對數據進行排序,降低數據排序的成本,降低CPU的消耗
劣勢:
1、實際上索引也是一張表,該表保存了主鍵與索引字段,並指向實體表的記錄,所有索引列也是要佔用空間的。
2、雖然索引大大提高了查詢速度,同時卻會降低更新表的速度。
如對錶進行insert、update和delete。
因爲更新表時,MySQL不僅要保存數據,還要保存一下索引文件每次更新索引列的字段,
都會調整因爲更新所帶來的鍵值變化後的索引信息。
3、索引註釋提高效率的一個因素,如果你的MySQL有大大數據量的表,
就需要花時間研究建立最優秀的索引,或優化查詢。
2.4.3索引分類
單值索引:即一個索引只包含單個列,一個表可以有多個單列索引。
唯一索引:索引列的值必須唯一,但允許有空值。
複合索引:一個索引包含多個列
基本語法:
創建:CREATE [UNIQUE] INDEX indexName ON mytable(colNAme(length));
ALTER mytable ADD [UNIQUE] INDEX [indexName] ON (colName(length));
刪除:DROP INDEX [indexName] ON mytable;
查看:SHOW INDEX FROM table_name\G;
2.4.4索引結構
1、BTree索引
2、Hash索引
3、full-text全文索引
4、R-Tree索引
由B+樹爲例
上圖爲一顆B+樹
淺藍色的塊我們稱之爲磁盤塊,可以看到每個磁盤塊包括幾個數據項(深藍色)和指針(黃色),
P1表示小於17的磁盤塊,P2表示在17和35之間的磁盤塊,P3表示大於35的磁盤塊。
真實的數據存在於葉子節點(第三層)。
非葉子節點不存儲真實的數據,只存儲指引搜索的方向。
查找過程
如果要查找數據項29:
首先會把磁盤塊1由磁盤加載到內存,此時發生一次IO,
在內存中用二分查找確定29在17和35之間,鎖定磁盤塊1的P2指針。
通過磁盤塊1的P2指針的磁盤地址把磁盤塊3由磁盤加載到內存,發生第二次IO,
29在26到30之間,鎖定磁盤塊3的P2指針
通過指針加載磁盤塊8到內存,發生第三次IO,
同時內存中做二分查找找到29,結束查詢。
總計三次IO。
2.4.5哪些情況需要建索引
建索引:
1、主鍵自動建立唯一索引
2、頻繁作爲查詢條件的字段應該創建索引
3、查詢中與其他表關聯的字段,外鍵關係建立索引
6、單鍵/組合索引的選擇問題,who?(在高併發下傾向創建組合索引)
7、查詢中排序的字段,排序字段若通過索引去訪問將大大提高排序速度
8、查詢中統計或者分組字段
不建索引:
1、表記錄太少
2、Where條件裏用不到的字段不創建索引
3、一個字段的值不一樣的數據太少
例如:
有性別1000條數據,而值只有男或者女。2/1000,比例太小,不適合建索引
4、經常增刪改的表
2.5性能分析
2.5.1MySQL Query Optimizer
MySQL中有專門負責優化SELECT語句的優化器模塊。
主要功能:
通過計算分析系統中收集到的統計信息,爲客戶端請求的Query提供他(MySQL的作者)認爲最優的執行計劃。
(他認爲最優,但不見得實際情況最優,這部分最耗費時間。
所以很多的大廠,像阿里巴巴,用的MySQL就會修改這個模塊的策略)
2.5.2MySQL常見的瓶頸
CPU:CPU飽和的時候一般發生在數據裝入內存或從磁盤上讀取數據的時候
IO:磁盤I/O瓶頸發生在裝入數據遠大於內存容量的時候
服務器硬件的性能瓶頸:top,free,iostat和vmstat來查看系統的性能狀態
2.5.3Explain⭐
是什麼:
使用EXPLAIN關鍵字可以模擬優化器查詢SQL查詢語句,從而知道MySQL是如何處理你的SQL語句的。
分析你的查詢語句或表結構的性能瓶頸
用法:
explain+sql語句
能幹嘛:
1、表的讀取順序(id)
2、數據讀取操作的操作類型(select_type)
3、哪些索引可以使用(possible_key)
4、哪些索引被實際使用(key)
5、表之間的引用(ref)
6、每張表有多少行被優化器查詢(rows)
查詢出來的字段:
如何判斷:
1、id---->select查詢的序列號包含一組數字,表示查詢中執行select字句或操作表的順序。
三種情況:
1、id相同,執行順序由上至下
2、id不同,如果是子查詢,id的序號會遞增,id的值越大優先級越高,越先被執行
3、id相同不同,同時存在
id值的大先執行,id相同的由上至下順序執行。
2、select_type
類型:
1、SIMPLE:簡單的select查詢,查詢中不包含子查詢或UNION。
2、PRIMARY:查詢中若包含任何複雜的子部分,最外層查詢則被標記爲。
3、SUBQUERY:在SELECT或WHERE列表在包含的子查詢。
4、DERIVED:在FROM列表中包含的子查詢被標記爲DERIVED(衍生)
MySQL會遞歸執行這些子查詢,把結果放在臨時表裏。
5、UNION:若第二個SELECT出現在UNION之後,則被標記爲UNION;
若UNION包含在FROM字句的子查詢中,外層SELECT被標記爲:DERIVED
6、UNION RESULT:從UNION表獲取結果的SELECT
3、table----->表示這一行的數據是關於哪個表的
4、type---->顯示查詢使用了何種類型
從最好到最差(最常用的):
system > const > eq_ref > ref > range > index > all
system:表只有一行記錄(等於系統表),這是const類型的特例,平時不會出現,這個可以忽略不計。
const:表示通過索引一次就找到了,const用於比較primary key或者unique索引。
因爲只匹配一行數據,所以很快。比如銀行卡號(常量)。
如將主鍵置於where列表中,MySQL就能將該查詢轉換爲一個常量。
eq_ref:唯一性索引掃描,對於每個索引建,表中只有一條記錄與之匹配。常見於主鍵或唯一索引掃描。
ref:非唯一性索引掃碼,返回匹配某個單獨值的所有行。
本質上也是一種索引訪問,他返回首頁匹配某個單獨值的行,然而,
他可能會找到多個符合條件的行,所以他應該屬於查找和掃描的混合體。
range:只檢索給定範圍的行,使用一個索引來選擇行。key列顯示使用了哪個索引。
一般檢索在你的where語句中出現了between、<、>、ind等的查詢
這種範圍掃描索引比全表掃描要好,因爲它只需要開始於索引的某個點,而結束於另一個點,
不用掃描全部索引。
比如:查詢昨天的所有訂單。
index:遍歷索引樹以找到匹配的行
all:將遍歷全表以找到匹配的行
(也就是說雖然all和index都是讀全表,但index是從索引中讀取的,而all是從硬盤中讀的)
5、possible_key--->顯示可能應用在這張表中的索引(理論上應該使用哪些索引)
6、key----->實際使用的索引。如爲NULL,則沒有使用索引
7、key_len------>表示(他希望)索引中使用的字節數
可以通過該列計算查詢中使用的索引的長度。
在不損失精度的情況下,長度越短越好。
key_len顯示的值爲索引字段的最大可能長度,並非實際使用長度,
即key_len是根據表定義計算而得,不是通過表內檢索出的。
8、ref---->顯示索引的哪一列被使用了,如果可能的話,是一個常數。哪些列或常數被用於查出索引列上的值
9、rows----->根據表統計信息及索引選用情況,大致估算出找到所需的記錄所需要讀取的行數
觀察rows列:
建索引之前一共掃描了641行,
建完索引,只掃描了143行。
10、Extra---->包含不適合在其他列顯示但十分重要的額外信息
最好不要出現:
1、Using filesort:說明mysql會對數據使用一個外部的索引排序,而不是按照表內的索引順序進行讀取。
MySQL中無法利用索引完成的排序操作稱爲“文件排序”。
2、Using temporary:使用了臨時表保存中間結果,MySQL在對查詢結果排序是使用臨時表。
常見於排序order by和分組查詢group by。會拖慢查詢時間。
最好出現:
1、Using index:表示相應的select操作中使用了覆蓋索引(Covering Index),
避免了訪問了表的數據行,效果不錯。(key=possible_key)
如果同時出現using where,表明索引被用來執行索引鍵值的查找。
如果沒有同時出現using where,表明索引用來讀取數據而非執行查找動作。
覆蓋索引:就是select的數據列只用從索引中就能夠取得,不必讀取數據行,
MySQL可以利用索引返回select列表中的字段,而不必根據索引再次讀取數據文件,
換句話說查詢列要被所建的索引覆蓋。
其他:
1、Using where:表示使用了where過濾。
2、Using join buffer:使用了連接緩存。
3、impossible where:where子句的值總是false,不能用來獲取任何元組。
4、select tables optimized away:在沒有groupby子句去情況下,基於索引優化min/max操作或者
對於myisam存儲引擎優化count(*)操作,不必等到執行階段再進行計算
查詢執行計劃生成的階段即完成優化。
5、distinct:優化distinct操作,在找到第一個匹配的元組後即停止找相同值的動作。
觀察Extra:
第一個sql語句的Extra裏出現了using filesort,說明沒有使用表內的索引排序,這是非常不好的現象
第二個sql語句做了優化。
在實際的情況中,要儘量避免寫第一種sql語句,如果出現要儘快優化。
2.5.4sql語句優化規則⭐
1、全值匹配是最好的
2、最佳左前綴法則:如果索引了多列,要遵循最佳左前綴法則。即從索引的最左前列開始並且不跳過索引中的列
3、不在索引列上做任何操作(會導致索引失效)
4、存儲引擎不能使用索引中範圍條件右邊的列
5、儘量使用覆蓋索引,減少select *
6、mysql在使用不等於(!=)的時候會導致索引失效
7、is null,is not null會導致索引失效
8、like以通配符開頭(%abc。。。)會導致索引失效
9、字符串不加單引號會導致索引失效
10、用or來連接,會導致索引失效
口訣:
帶頭大哥不能死(1、2)
中間兄弟不能斷(2)
索引列上不計算(3、9)
like%加右邊(8)
範圍之後全失效(2)
例題:
面試題:如果我一定要使用like “%abc”作爲查詢條件,如何對其優化,使得索引不失效?
select 後面跟需要查詢的字段,不用 select *
針對需要查詢的字段,建立多值索引。
例如:select * from user where name like '%ab';
優化:
create index idx_nameAge on user(name,age);
select name,age from user where name like '%ab';
一般性建議
1、對於單間索引,儘量選擇針對當前query過濾性更好的索引。
2、在選擇組合索引的時候,當前Query中過濾器最好的字段在索引字段順序,位置越靠前越好。
3、在選擇組合索引的時候,儘量選擇可以能夠包含當前query中的where字句中更好字段的索引。
4、儘可能通過分析統計信息和調整query的寫法來達到選擇合適索引的目的。
3查詢截取分析
3.1查詢優化
3.1.1永遠小表驅動大表
3.1.2order by關鍵字優化⭐
提高order by的速度
1、order by時select *是個大忌,只query需要的字段非常重要。
1.1、當Query的字段總和小於max_length_for_sort_data而且排序字段不是TEXT|BLOB類型時,
會用改進後的算法--單路排序,否則用老算法--多路排序。
1.2、兩種算法的數據都有可能超出sort_buffer的容量,超出之後,會創建temp文件進行合併排序,
導致多次I/O,但是用單路排序算法的風險更大一些,所以要提高sort_buffer_size
2、嘗試提高sort_buffer_size
不管用那種算法,提高這個參數都會提高效率,當然,要根據系統的能力去提高,
因爲這個參數是針對每個進程的。
3、嘗試提高max_length_for_sort_data
提高這個參數,會增加用改進算法的概率。但是如果設的太高,
數據總量超出sort_buffer_size的概率就增大,明顯症狀是高的磁盤I/O活動和低的處理器使用率。
例題
優化策略
1、group by是指是先排序後進行分組,遵循索引建的最佳左前綴。
2、當無法使用索引列,增大max_length_for_sort_data參數的設置+增大sort_buffer_size參數的設置。
3、where高於having,能寫在where限定的條件就不要去having限定了。
3.2慢查詢日誌
3.2.1slow_query_log⭐
MySQL的慢查詢日誌是MySQL提供的一種查詢日誌記錄,它用了記錄在MySQL中響應時間查過閾值的語句,具體指運行時間超過long_query_time值的sql,則會被記錄到慢查詢日誌中。
默認情況下,MySQL數據庫沒有開啓慢查詢日誌,需要手動設置。
set global slow_query_log=1;
默認情況下,long_query_time的值爲10秒(大於而非大於等於)。
show variables like 'long_query_time%';
查看是否開啓慢查詢日誌,和慢查詢日誌的地址。
show variables like '%slow_query_log%';
3.2.2mysqldumpslow⭐
日誌分析工具mysqldumpslow。使用此命令可以對慢查詢日誌進行分析。
得到返回數據集最多的10個SQL。
mysqldumpslow -s r -t 10 /var/lib/mysql/root-slow.log;
得到訪問次數最多的10個SQL。
mysqldumpslow -s c -t 10 /var/lib/mysql/root-slow.log;
得到按照時間排序的前10條裏面含有左連接的查詢語句。
mysqldumpslow -s t -t 10 -g "left join" /var/lib/mysql/root-slow.log;
另外建議在使用這些命令時結合 | 和more使用,否則有可能出現爆屏情況。
mysqldumpslow -s r -t 10 /var/lib/mysql/root-slow.log | more;
查看mysqldumpslow的幫助信息
s:是表示按照何種方式訪問
c:訪問次數
l:鎖定時間
r:返回記錄
t:查詢時間
al:平均鎖定時間
ar:平均返回記錄數
at:平均查詢數據
t:即爲返回前面多少條的數據
g:後面搭配一個正則匹配模式,大小寫不敏感的
3.3批量數據腳本⭐
向數據庫自動插入(1000萬)隨機數據
1、建庫和建表
# 新建庫
create database dbtest;
use dbtest;
#1 建表dept
CREATE TABLE dept(
id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
deptno MEDIUMINT UNSIGNED NOT NULL DEFAULT 0,
dname VARCHAR(20) NOT NULL DEFAULT "",
loc VARCHAR(13) NOT NULL DEFAULT ""
) ENGINE=INNODB DEFAULT CHARSET=UTF8 ;
#2 建表emp
CREATE TABLE emp
(
id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
empno MEDIUMINT UNSIGNED NOT NULL DEFAULT 0, /*編號*/
ename VARCHAR(20) NOT NULL DEFAULT "", /*名字*/
job VARCHAR(9) NOT NULL DEFAULT "",/*工作*/
mgr MEDIUMINT UNSIGNED NOT NULL DEFAULT 0,/*上級編號*/
hiredate DATE NOT NULL,/*入職時間*/
sal DECIMAL(7,2) NOT NULL,/*薪水*/
comm DECIMAL(7,2) NOT NULL,/*紅利*/
deptno MEDIUMINT UNSIGNED NOT NULL DEFAULT 0 /*部門編號*/
)ENGINE=INNODB DEFAULT CHARSET=UTF8 ;
2、設置參數log_bin_trust_function_creators
創建函數,假如報錯:This function has none of DETERMINISTIC......
# 由於開啓過慢查詢日誌,因爲我們開啓了 bin-log, 我們就必須爲我們的function指定一個參數。
show variables like 'log_bin_trust_function_creators';
set global log_bin_trust_function_creators=1;
# 這樣添加了參數以後,如果mysqld重啓,上述參數又會消失,永久方法:
windows下my.ini[mysqld]加上log_bin_trust_function_creators=1
linux下/etc/my.cnf下my.cnf[mysqld]加上log_bin_trust_function_creators=1
3、創建函數,保證每條數據都不同
#用於隨機產生字符串
DELIMITER $$
CREATE FUNCTION rand_string(n INT) RETURNS VARCHAR(255)
BEGIN ##方法開始
DECLARE chars_str VARCHAR(100) DEFAULT 'abcdefghijklmnopqrstuvwxyzABCDEFJHIJKLMNOPQRSTUVWXYZ';
##聲明一個 字符竄長度爲 100 的變量 chars_str ,默認值
DECLARE return_str VARCHAR(255) DEFAULT '';
DECLARE i INT DEFAULT 0;
##循環開始
WHILE i < n DO
SET return_str =CONCAT(return_str,SUBSTRING(chars_str,FLOOR(1+RAND()*52),1));
##concat 連接函數 ,substring(a,index,length) 從index處開始截取
SET i = i + 1;
END WHILE;
RETURN return_str;
END $$
#假如要刪除
#drop function rand_string;
#用於隨機產生部門編號
DELIMITER $$
CREATE FUNCTION rand_num( )
RETURNS INT(5)
BEGIN
DECLARE i INT DEFAULT 0;
SET i = FLOOR(100+RAND()*10);
RETURN i;
END $$
#假如要刪除
#drop function rand_num;
4、創建存儲過程
#創建往emp表中插入數據的存儲過程
DELIMITER $$
CREATE PROCEDURE insert_emp(IN START INT(10),IN max_num INT(10))
BEGIN
DECLARE i INT DEFAULT 0;
#set autocommit =0 把autocommit設置成0 ;提高執行效率
SET autocommit = 0;
REPEAT ##重複
SET i = i + 1;
INSERT INTO emp(empno, ename ,job ,mgr ,hiredate ,sal ,comm ,deptno ) VALUES ((START+i) ,rand_string(6),'SALESMAN',0001,CURDATE(),FLOOR(1+RAND()*20000),FLOOR(1+RAND()*1000),rand_num());
UNTIL i = max_num ##直到 上面也是一個循環
END REPEAT; ##滿足條件後結束循環
COMMIT; ##執行完成後一起提交
END $$
#刪除
# DELIMITER ;
# drop PROCEDURE insert_emp;
#執行存儲過程,往dept表添加隨機數據
DELIMITER $$
CREATE PROCEDURE insert_dept(IN START INT(10),IN max_num INT(10))
BEGIN
DECLARE i INT DEFAULT 0;
SET autocommit = 0;
REPEAT
SET i = i + 1;
INSERT INTO dept (deptno ,dname,loc ) VALUES (START +i ,rand_string(10),rand_string(8));
UNTIL i = max_num
END REPEAT;
COMMIT;
END $$
#刪除
# DELIMITER ;
# drop PROCEDURE insert_dept;
5、調用存儲過程
#執行存儲過程,往dept表添加10條數據測試一下,deptno從100開始到110
DELIMITER ;
CALL insert_dept(100,10);
#執行存儲過程,往emp表添加50萬條數據(我的mysql執行了46s,大約1秒插入一萬條數據)
CALL insert_emp(100001,500000);
要想在windows下用navicat連接linux的MySQL,需要關閉Linux的防火牆或者開發端口,還要設置mysql的權限。
navicat連接Linux MySQL教程:
https://blog.csdn.net/qq_42391248/article/details/105483808
3.4Show Profiles⭐
是MySQL提供可以用來分析當前會話中語句執行的資源消耗情況。可以用於sql的調優的測量。
默認關閉狀態(MySQL牛逼的功能都是默認關閉的呀)
#查看是否開啓profiling
show variables like 'profiling';
打開profiling
set profiling=on;
查看結果
show profiles;
診斷sql
show profile cpu,block io for query 6;
#數字爲上一把的query_id
日常開發需要注意的結論(status出現了以下四個結果,說明這個sql有問題,需要優化):
converting HEAP to MyISAM:查詢結果太大,內存不夠用了往磁盤上搬。
Creating tmp table:創建臨時表。
1、拷貝數據到臨時表。
2、用完再刪除。
Copying to tmp table on disk:把內存中臨時表複製到磁盤,危險!
locked
4、MySQL鎖機制
從對數據操作的類型(讀/寫):
1、讀鎖(共享鎖):針對同一份數據,多個讀操作可以同時進行而不會互相影響。
2、寫鎖(排它鎖):當前寫操作沒有完成前,它會阻斷其他寫鎖和讀鎖。
查看錶是否加鎖
show open tables;
#1表示被鎖了,0表示安全
對emp表加讀鎖,對detp表加寫鎖
lock table emp read,detp write;
解開所有鎖
unlock tables;
4.1表鎖(偏讀)---MyISAM
4.1.1加了讀鎖的情況
管理員1對一個表加了讀鎖之後:
1、管理員1只可讀此表,不可修改此表(修改會報錯);不可讀其它表,不可修改其他表
2、其他管理員可讀,不可修改(此修改會被堵塞,當管理員1解開鎖時,修改才能完成)
4.1.2加了寫鎖的情況
管理員1對一個表加了寫鎖之後:
1、管理員1可讀寫此表,不可讀寫其他表;
2、其他管理員不可讀寫此表(讀寫操作會被堵塞,當管理員1解開鎖時,讀寫操作才能完成)
總而言之,競賽讀鎖會阻塞寫,但不會阻塞讀。而寫鎖則會把讀和寫都阻塞。
4.1.3表鎖分析
show status like 'table%';
Table_locks_immediate:產生表級鎖定的次數,表鎖可以立即獲取鎖的查詢次數,沒立即獲取鎖值加1;
Table_locks_waited:出現表鎖鎖定爭用而發生等待的次數(不能立即獲取鎖的次數,每等待一次鎖值加1),此值高則說明存在着較嚴重的表級鎖爭用情況。
此外,Myisam的讀寫鎖調度是寫優先,這也是myisam不適合做寫爲主表的引擎。因爲寫鎖後,其他線程不能做任何操作,大量的更新會使查詢很難得到鎖,從而造成永遠阻塞。
4.2行鎖(偏寫)---InnoDB
4.2.1行鎖特點
行鎖偏向InnoDB存儲引擎,開銷大,加鎖慢;會出現死鎖;鎖定粒度最小,發送鎖衝突的概率最低,併發度也最高。
InnoDB與MyISAM的最大不同有兩個特點:一是支持事務;二是採用了行級鎖。
索引失效會導致行鎖變表鎖---會導致其他的事務阻塞而使得系統變慢。
4.2.2間隙鎖
當我們用範圍條件而不是相等條件檢索數據,並請求共享或排它鎖時,InnoDB會給符合條件的已有數據記錄的索引項加鎖;對於鍵值再條件範圍但並不存在的記錄,叫做“間隙(GAP)”;
InnoDB也會對這個“間隙”加鎖,這種鎖機制競賽所謂的間隙鎖(Next-Key鎖)。
危害:
因爲Query執行過程中通過範圍查找的話,它會鎖定整個範圍內所有的索引鍵值,即使整個鍵值並不存在。
間隙鎖有一個比較致命的弱點,就是當鎖定一個範圍鍵值之後,即使某些不存在的鍵值也會被無辜的鎖定,而造成再鎖定的時候無法插入鍵值範圍內的任何數據。再某些場景下這可能會對性能造成很大的危害。
4.2.3對一行加鎖
begin;
select * from tablename where a=8 for update;
#鎖定a=8這一行,其它人的對這一行的操作會被阻塞,知道鎖定行的會話提交。
commit;
4.2.4行鎖分析
show status like 'innodb_row_lock%';
從上到下(標紅了是比較重要的三項):
1、當前正在等待鎖定的數量;
2、從系統啓動到現在鎖定總時間長度(等待總時長);
3、每次等待所花平均時間(等待平均時長);
4、從系統啓動到現在等待最長的一次所花的時間;
5、系統啓動後到現在總共等待的次數(等待總次數);
尤其是當等待次數很高,而且每次等待時長也不小的時候,我們就需要分析系統中爲什麼會有如此多的等待,然後分析結果着手指定優化計劃。
4.2.5優化建議⭐
1、儘可能讓所有數據檢索都通過索引來完成,避免無索引行鎖升級爲表鎖。
2、合理設計索引,儘量縮小鎖的範圍。
3、儘可能較少檢索條件,避免間隙鎖。
4、儘量控制事務大小,減少鎖定資源量和時間長度。
5、儘可能低級別事務隔離。
4.3總結⭐
InnoDB存儲引擎由於實現了行級鎖定,雖然在鎖定機制的實現方面所帶來的性能損耗可能比表級鎖定會要高一些,但是再整體併發處理能力方面要遠遠優於MyISAM的表級鎖定的。當系統併發量較高的時候,InnoDB的整體性能和MyISAM相比就會有比較明顯的優勢了。
但是InnoDB的行級鎖定同樣也有其脆弱的一面(間隙鎖),當我們使用不當的時候,可能會讓InnoDB的整體性能表現不僅不能比MyISAM高,甚至可能會更差。
5、主從複製
5.1複製的基本原理
主從複製,是用來建立一個和主數據庫完全一樣的數據庫環境,稱爲從數據庫(slave)。
主數據庫一般是準實時的業務數據庫。您看,像在mysql數據庫中,支持單項、異步賦值。在賦值過程中,一個服務器充當主服務器,而另外一臺服務器充當從服務器。
此時主服務器會將更新信息寫入到一個特定的二進制文件中。並會維護文件的一個索引用來跟蹤日誌循環。這個日誌可以記錄併發送到從服務器的更新中去。當一臺從服務器連接到主服務器時,從服務器會通知主服務器從服務器的日誌文件中讀取最後一次成功更新的位置。然後從服務器會接收從哪個時刻起發生的任何更新,然後鎖住並等到主服務器通知新的更新。
5.2複製的基本原則
每個slave只有一個master。
每個slave只能有一個唯一的服務器ID。
每個master可以有多個slave。
5.3複製的最大問題
延時。
比如一主一從的配置:
既然是兩臺機器,必然會出現網絡,線程。。等原因,造成從機不能及時的同步主機信息。這種問題是不可避免的。部分公司應用主從的時候,往往將主機用來寫,從機用來讀,這樣就很大可能出現讀不到最新數據。
解決方案:將已經寫入主機(master)的數據,放一份緩存放入redis,去從機(slave)讀之前先去redis讀就好了,就可以解決主從的延時問題了。
完結!