Mysql 學(xué)習(xí)

1、索引

  • 背景:
    訪問磁盤,機(jī)械硬盤依靠的是機(jī)械運(yùn)動, 尋道時間(磁臂移動到指定磁道)5ms - 旋轉(zhuǎn)延遲(轉(zhuǎn)速 如7200轉(zhuǎn) 表示1min能轉(zhuǎn)7200,120轉(zhuǎn)每秒,1轉(zhuǎn)的延遲就是1/120/2 ~ 4.17ms) - 傳輸時間 相較前兩個可以忽略不計。
    所以IO訪問是代價高昂的,操作系統(tǒng)一般會做頁緩存(把目標(biāo)數(shù)據(jù)附近的數(shù)據(jù)也讀入內(nèi)存緩存)。一般IO讀取的單位是頁,每頁的大小是4K或8K。
    索引的目的就是減少磁盤IO的次數(shù),加快查找讀取。
    依賴數(shù)據(jù)結(jié)構(gòu):
  • B+樹 :
    1、IO次數(shù)取決于樹的高度,h=log(m+1)N , 總數(shù)據(jù)數(shù)量N一定的情況下,每個磁盤塊的數(shù)據(jù)數(shù)量m越大,高度越低。m=磁盤塊的容量大小(4k,8K)/數(shù)據(jù)的單條大小,數(shù)據(jù)頁的大小一定的,所以要讓m越大,則單條數(shù)據(jù)的數(shù)據(jù)量要越小。這就是為啥索引字段要盡量小,B+樹內(nèi)層節(jié)點(diǎn)不存儲數(shù)據(jù),只有葉子結(jié)點(diǎn)存儲數(shù)據(jù)。
    2、B+樹的數(shù)據(jù)是復(fù)合數(shù)據(jù)結(jié)構(gòu)時,他會先比較前面的(name,age,sex)name ,即最左匹配原則
  • Mysql索引建立原則
    1、最左前綴匹配原則 mysql會一直向右匹配直到遇到范圍查詢(>、<、between、like)就停止匹配 這個指的是你建立的索引順序,不是where語句的順序 != isnull isnotnull 無法使用索引
    2、= 和 in 索引中字段可以亂序
    3、盡量使用區(qū)分度高的列作為索引
    4、查詢sql中,不要將索引包在函數(shù)或計算中,可能會使用不到索引
    5、盡量擴(kuò)展索引,不要新建索引 a,b -> a,b,c
  • explain 結(jié)果的含義


    image.png
  1. type:表的連接類型
    system僅有一行的系統(tǒng)表 - 特殊
    const數(shù)據(jù)表只有一行匹配,查詢時會當(dāng)做常量處理 常用于primary key,unique key等
    eq_ref每次與之前的表合并都只有讀取一行,使用primary key,unique key 進(jìn)行join
    ref每次與之前的表合并都只有讀取少量行,多用于各個key的=或<=> 覆蓋索引或非主鍵或非唯一鍵
    range常數(shù)值范圍
    index索引樹掃描
    all全表掃描
    system>const>eq_ref>ref>range>index>all
  2. possible_keys 能用這些索引找到行
  3. key 查詢實際使用的索引
  4. key_len 實際使用索引的長度(字節(jié)),根據(jù)這個可以推算聯(lián)合索引用了多少列
  5. rows 實際掃描行數(shù)
  6. extra 表示查詢時實際使用的詳細(xì)信息
    using temporary 使用了臨時表 很影響性能
    using index 使用了索引覆蓋
    using where 使用了where,但是需要回表查數(shù)據(jù)
    using index condition 使用索引查找,但是需要回表查數(shù)據(jù)
    using index & using where 使用索引查找且不需要回溯數(shù)據(jù)
  • 分類
    聚集索引:按照每張表的主鍵構(gòu)造一顆B+樹,葉子結(jié)點(diǎn)中存放了表的行數(shù)據(jù),故葉子結(jié)點(diǎn)又稱數(shù)據(jù)頁。除了索引能定位頁的地址,相鄰頁之間也能通過雙向鏈表查找,屬于邏輯上連續(xù)。
    非聚簇索引:b+樹的葉子結(jié)點(diǎn)上的數(shù)據(jù)是指向數(shù)據(jù)實際存儲地址的指針,也就是還需要一步查找

InnoDB :主鍵是聚簇索引,輔助索引是非聚簇索引
MyISAM:都是非聚簇索引,數(shù)據(jù)項單獨(dú)存儲

2、主從

3、binlog redolog undolog

innoDB 在一條記錄要更新時,會先寫日志redolog,再更新到內(nèi)存,到這算更新結(jié)束。持久化到磁盤的時間根據(jù)策略來。redolog和binlog都是順序?qū)懀俣瓤?/p>

  • redo log 保證事務(wù)的持久性
    物理日志,記錄物理頁的修改,而不是記錄修改了某行數(shù)據(jù)。用來恢復(fù)提交后的物理頁
    用來保證意外crash后,恢復(fù)還未持久化的數(shù)據(jù)
    redo是順序?qū)懳募欤慌K頁是隨機(jī)IO,慢
    redo只用寫需要修改的部分內(nèi)容,少;臟頁刷新是整頁寫入,大
  • undo log 保證事務(wù)的一致性
    用來做回滾和mvcc
  • bin log :
    記錄的是表結(jié)構(gòu)的創(chuàng)建或更新,表記錄的創(chuàng)建或更新 的二進(jìn)制文件 記錄的sql語句

redo log 是innoDb特有的,bin log是server層的,所有引擎都有
redo log 是記錄的物理日志,bin log是sql日志
redo log 是循環(huán)記錄到固定空間,如果write_pos趕上了checkpoint,空間會用完,bin log 是追加寫(會分多個文件),理論可以無限寫

4、非常規(guī)sql

#如果不存在記錄,直接插入;如果存在,刪除后再插入
replace into t(id, update_time) values(1, now());
#mysql 局部變量實現(xiàn)rank,無并列
select id,maxScore,(@rowNum:=@rowNum+1) as rowNo
from t_user,
(select (@rowNum :=0) ) b
order by t_user.maxScore desc 
#mysql 局部變量實現(xiàn)rank,實現(xiàn)并列名次問題
select fee_rate,
       @`rank`:=@`rank`+1 as n,
       @rank1:=(case
           when @fee = fee_rate then @rank1
           when @fee:=fee_rate then @`rank`
           end ) as r from fund_zbank.fee_rate_config a, (select @rank:=0, @rank1:=0, @fee:=null) b order by fee_rate desc;
# having是對分組后的結(jié)果進(jìn)行where
# 篩選各個部門的部門人數(shù)大余2的最大和最小工資
SELECT dept ,MAX(salary) AS MAXIMUM ,MIN(salary) AS MINIMUM
FROM STAFF
GROUP BY dept
HAVING COUNT(*) > 2
ORDER BY dept
# 獲取分組前3
select * from (
select fee_rate,
       if(@ol_merchant=merchant_id, @rr:=@rr+1, @rr:=1) as rr,
       @ol_merchant:=merchant_id
from fund_zbank.fee_rate_config a,
     (select @rr:=1, @ol_merchant:=null) b
order by merchant_id, fee_rate ) c
where c.rr <=3;
# 獲取分組前3,含并列
select c.merchant_id, c.fee_rate, c.rp from (
                  select fee_rate,
                         merchant_id,
                         case
                             when @ol_merchant = merchant_id and @ol_fee = fee_rate then @rr
                             when @ol_merchant = merchant_id then @rr_1 + 1
                             else 1
                             end
                             as rp,
                         @rr_1 := if(@ol_merchant = merchant_id, @rr_1 + 1, 1),
                         @rr := if(@ol_merchant = merchant_id and @ol_fee = fee_rate, @rr, @rr_1),
                         @ol_merchant := merchant_id,
                         @ol_fee := fee_rate
                  from fund_zbank.fee_rate_config a,
                       (select @rr := 1, @ol_merchant := null, @rr_1 := 1, @ol_fee := null) b
                  order by merchant_id, fee_rate
              ) c
where c.rp <= 3;

5、鎖

按粒度分:

  • 表鎖 開銷小,鎖的快,不會出現(xiàn)死鎖;粒度大,鎖沖突概率高,并發(fā)度低 -- 查詢?yōu)橹鳎倭扛?/li>
  • 行鎖 開銷大,加鎖慢,會出現(xiàn)死鎖;粒度小,鎖沖突概率低,并發(fā)度最高 -- 更新頻繁,且需要查詢
  • 頁鎖 介于表鎖和行鎖之間

按引擎:

  • MyISAM
    show status like 'table%' 查到的table_locks_waited值表示等待加表鎖的數(shù)目,如果值很大,說明當(dāng)前鎖爭用激烈

表級鎖有:

  1. 讀共享鎖
  2. 寫?yīng)氄兼i
    MyISAM 寫鎖比讀鎖優(yōu)先級更高,如果更新頻繁,可能導(dǎo)致讀永遠(yuǎn)排隊,無法查詢
  • innoDB
    show status like 'innodb_row_lock%' 查找InnoDB_row_lock_waits和InnoDB_row_lock_time_avg的值,如果很大,說明當(dāng)前鎖爭用激烈

行鎖有:

  1. 共享鎖S 允許事務(wù)讀,不允許排他鎖獲取相同數(shù)據(jù)集
  2. 排他鎖X 允許事務(wù)寫,不允許其他共享讀鎖或排他寫鎖獲取相同數(shù)據(jù)集

為了行鎖表鎖共存的鎖粒度機(jī)制,使用表鎖 意向鎖:

  1. 意向共享鎖IS
  2. 意向排他鎖IX
  • 意向鎖是innoDB自己加的,用戶無需干預(yù)
  • 對于update,insert,delete,innoDB會自動給相關(guān)數(shù)據(jù)集加排他鎖X
  • 普通select不會加鎖

事務(wù)中,自己手工加鎖

  • SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE 加共享鎖,其他事務(wù)任然可以查詢該數(shù)據(jù)集,而且也能加共享鎖。如果當(dāng)前事務(wù)要對記錄更新,可能會死鎖
  • SELECT * FROM table_name WHERE ... FOR UPDATE 加排他鎖,其他session任然可以查詢該記錄,但是不能獲得鎖了,會發(fā)生加鎖等待

事務(wù)中,隱式加鎖
在事務(wù)中,會根據(jù)隔離級別,自動上鎖,在commit或rollback后,自動釋放

行鎖是加在索引上的,所以只有命中索引才會用到行鎖,否則使用表鎖

間隙鎖 gap lock
當(dāng)使用范圍查詢時,會給范圍內(nèi)的索引且存在記錄加鎖,但是在范圍內(nèi)不存在的記錄也會加鎖,叫做間隙鎖。這樣會造成不允許間隙內(nèi)的記錄插入。 主要是為了防止幻讀,滿足恢復(fù)和復(fù)制的目的。

死鎖:
多個事務(wù)同時請求鎖定對方占用的資源
innoDB能檢測死鎖的依賴,并返回錯誤,回滾返回錯誤的那個事務(wù)。無法檢測到的外部鎖,可以通過設(shè)置獲取鎖的超時時間 innodb_lock_wait_timeout 。高并發(fā)場景下,多個線程等待同一個鎖時,死鎖檢測會導(dǎo)致速度變慢。

避免死鎖:

  • 并發(fā)寫入事務(wù)開始時,select ... for update 先獲取要修改的行的鎖,再執(zhí)行修改
  • 事務(wù)中,如果先查后改,需要先獲取排他鎖,直接先獲取排它鎖
  • 多個表訪問時,盡量互相保證獲取各個表中鎖的順序性

6、事務(wù)

  • A automicity 原子性 一個事務(wù)要么成功,要么全部回滾 使用undolog實現(xiàn)回滾
  • C consistency 一致性 事務(wù)的目的就是一致性,AID都是為了保證一致性
  • I isolation 隔離性 事務(wù)內(nèi)部的操作和其他事務(wù)是隔離的,各個并發(fā)事務(wù)互不干擾

寫操作對寫操作的影響:使用鎖機(jī)制保證
寫操作對讀操作的影響:使用mvcc機(jī)制保證
臟讀:A事務(wù)讀取到B事務(wù)未完成提交的數(shù)據(jù)
不可重復(fù)讀:A事務(wù)前后兩次讀取到B事務(wù)提交前后的數(shù)據(jù),數(shù)據(jù)被更改了 B update
幻讀:A事務(wù)前后兩次讀取到B事務(wù)提交前后的數(shù)據(jù)數(shù)量,數(shù)據(jù)數(shù)量變了 B insert delete

SQL事務(wù)隔離級別


image.png

一般數(shù)據(jù)庫默認(rèn)值都是RC或者RR
RR按SQL要求無法避免幻讀,但是innoDB避免了,使用了mvcc機(jī)制

mvcc multi-version concurrency control 多版本并發(fā)控制協(xié)議


image.png

讀不加鎖,讀寫不沖突,性能好。多個版本數(shù)據(jù)可以共存
原理:通過隱藏列和版本鏈實現(xiàn)的ReadView,打快照

  • D duration 持久性 事務(wù)一旦提交成功,對數(shù)據(jù)庫的改變就是永久的 使用redolog實現(xiàn)持久化
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容