性能優化專題(三)MySQL數據庫

目錄

數據庫三大範式是什麼

MySQL邏輯架構

MySQL查詢過程

MySQL存儲引擎

MyISAM與InnoDB區別

InnoDB引擎的4大特性

查詢緩存

性能優化建議

Scheme設計與數據類型優化

創建高性能索引

MySQL索引

索引原理

索引的數據結構

b+樹

b+樹的查找過程

b+樹性質

b樹和b+樹的區別

索引有哪幾種類型?

建索引的幾大原則

特定類型查詢優化

優化COUNT()查詢

優化關聯查詢

優化LIMIT分頁

優化UNION

優化WHERE子句

事務

什麼是數據庫事務?

事物的四大特性(ACID)介紹一下?

什麼是事務的隔離級別?MySQL的默認隔離級別是什麼?

什麼是髒讀?不可重複讀?幻讀?

從鎖的類別上分MySQL都有哪些鎖呢?

隔離級別與鎖的關係

MySQL中InnoDB引擎的行鎖是怎麼實現的?

數據庫的樂觀鎖和悲觀鎖是什麼?怎麼實現的?


數據庫三大範式是什麼

第一範式:每個列都不可以再拆分,1NF是對屬性的原子性

第二範式:非主鍵列完全依賴於主鍵,2NF是對記錄的惟一性

第三範式:非主鍵列不依賴於其他非主鍵列,3NF是對字段的冗餘性,要求任何字段不能由其他字段派生出來。

在設計數據庫結構的時候,要儘量遵守三範式,如果不遵守,必須有足夠的理由。比如性能。事實上我們經常會爲了性能而妥協數據庫的設計。

MySQL邏輯架構

如果能在頭腦中構建一幅MySQL各組件之間如何協同工作的架構圖,有助於深入理解MySQL服務器。下圖展示了MySQL的邏輯架構圖。

MySQL邏輯架構,來自:高性能MySQL



MySQL邏輯架構整體分爲三層,最上層爲客戶端層,並非MySQL所獨有,諸如:連接處理、授權認證、安全等功能均在這一層處理。

MySQL大多數核心服務均在中間這一層,包括查詢解析、分析、優化、緩存、內置函數(比如:時間、數學、加密等函數)。所有的跨存儲引擎的功能也在這一層實現:存儲過程、觸發器、視圖等。

最下層爲存儲引擎,其負責MySQL中的數據存儲和提取。和Linux下的文件系統類似,每種存儲引擎都有其優勢和劣勢。中間的服務層通過API與存儲引擎通信,這些API接口屏蔽了不同存儲引擎間的差異。

MySQL查詢過程

我們總是希望MySQL能夠獲得更高的查詢性能,最好的辦法是弄清楚MySQL是如何優化和執行查詢的。一旦理解了這一點,就會發現:很多的查詢優化工作實際上就是遵循一些原則讓MySQL的優化器能夠按照預想的合理方式運行而已

當向MySQL發送一個請求的時候,MySQL到底做了些什麼呢?

  • 客戶端向MySQL服務器發送一條查詢請求
  • 服務器首先檢查查詢緩存,如果命中緩存,則立刻返回存儲在緩存中的結果。否則進入下一階段
  • 服務器進行SQL解析、預處理、再由優化器生成對應的執行計劃
  • MySQL根據執行計劃,調用存儲引擎的API來執行查詢
  • 將結果返回給客戶端,同時緩存查詢結果

MySQL存儲引擎

常用的存儲引擎有以下:

  • Innodb引擎:Innodb引擎提供了對數據庫ACID事務的支持。並且還提供了行級鎖和外鍵的約束。它的設計的目標就是處理大數據容量的數據庫系統。
  • MyIASM引擎(原本Mysql的默認引擎):不提供事務的支持,也不支持行級鎖和外鍵。

MyISAM與InnoDB區別

  MyISAM Innodb
存儲結構 每張表被存放在三個文件:frm-表格定義、MYD(MYData)-數據文件、MYI(MYIndex)-索引文件 每張表被存放在兩個文件:frm-表格定義,ibd-數據和索引文件,都保存在同一個數據文件中,InnoDB表的大小隻受限於操作系統文件的大小,一般爲2GB
存儲空間 MyISAM可被壓縮,存儲空間較小 InnoDB的表需要更多的內存和存儲,它會在主內存中建立其專用的緩衝池用於高速緩衝數據和索引
可移植性、備份及恢復 由於MyISAM的數據是以文件的形式存儲,所以在跨平臺的數據轉移中會很方便。在備份和恢復時可單獨針對某個表進行操作 免費的方案可以是拷貝數據文件、備份 binlog,或者用 mysqldump,在數據量達到幾十G的時候就相對痛苦了
文件格式 數據和索引是分別存儲的,數據.MYD,索引.MYI 數據和索引是集中存儲的,.ibd
記錄存儲順序 按記錄插入順序保存 按主鍵大小有序插入
外鍵 不支持 支持
事務 不支持 支持
鎖支持 表鎖 行鎖、表鎖,鎖定力度小併發能力高
SELECT MyISAM更優  
INSERT、UPDATE、DELETE   InnoDB更優
select count(*) myisam更快,因爲myisam內部維護了一個計數器,可以直接調取。  
索引的實現方式 B+樹索引,myisam 是堆表 B+樹索引,Innodb 是索引組織表
哈希索引 不支持 支持
全文索引 支持 不支持
聚簇索引 MyISAM索引是非聚簇索引 InnoDB索引是聚簇索引
葉子節點 MyISAM索引的葉子節點存儲的是行數據地址,需要再尋址一次才能得到數據 InnoDB主鍵索引的葉子節點存儲着行數據,因此主鍵索引非常高效;InnoDB非主鍵索引的葉子節點存儲的是主鍵和其他帶索引的列數據,因此查詢時做到覆蓋索引會非常高效

InnoDB引擎的4大特性

  • 插入緩衝(insert buffer)

  • 二次寫(double write)

  • 自適應哈希索引(ahi)

  • 預讀(read ahead)

查詢緩存

在解析一個查詢語句前,如果查詢緩存是打開的,那麼MySQL會檢查這個查詢語句是否命中查詢緩存中的數據。如果當前查詢恰好命中查詢緩存,在檢查一次用戶權限後直接返回緩存中的結果。這種情況下,查詢不會被解析,也不會生成執行計劃,更不會執行。

MySQL將緩存存放在一個引用表(不要理解成table,可以認爲是類似於HashMap的數據結構),通過一個哈希值索引,這個哈希值通過查詢本身、當前要查詢的數據庫、客戶端協議版本號等一些可能影響結果的信息計算得來。所以兩個查詢在任何字符上的不同(例如:空格、註釋),都會導致緩存不會命中。

如果查詢中包含任何用戶自定義函數、存儲函數、用戶變量、臨時表、mysql庫中的系統表,其查詢結果
都不會被緩存。比如函數NOW()或者CURRENT_DATE()會因爲不同的查詢時間,返回不同的查詢結果,再比如包含CURRENT_USER或者CONNECION_ID()的查詢語句會因爲不同的用戶而返回不同的結果,將這樣的查詢結果緩存起來沒有任何的意義。

既然是緩存,就會失效,那查詢緩存何時失效呢?MySQL的查詢緩存系統會跟蹤查詢中涉及的每個表,如果這些表(數據或結構)發生變化,那麼和這張表相關的所有緩存數據都將失效。正因爲如此,在任何的寫操作時,MySQL必須將對應表的所有緩存都設置爲失效。

性能優化建議

Scheme設計與數據類型優化

選擇數據類型只要遵循小而簡單的原則就好,越小的數據類型通常會更快,佔用更少的磁盤、內存,處理時需要的CPU週期也更少。越簡單的數據類型在計算時需要更少的CPU週期,比如,整型就比字符操作代價低,因而會使用整型來存儲ip地址,使用DATETIME來存儲時間,而不是使用字符串。

這裏總結幾個可能容易理解錯誤的技巧:

  • 通常來說把可爲NULL的列改爲NOT NULL不會對性能提升有多少幫助,只是如果計劃在列上創建索引,就應該將該列設置爲NOT NULL。
  • 對整數類型指定寬度,比如INT(11),沒有任何卵用。INT使用16爲存儲空間,那麼它的表示範圍已經確定,所以INT(1)和INT(20)對於存儲和計算是相同的。
  • UNSIGNED表示不允許負值,大致可以使正數的上限提高一倍。比如TINYINT存儲範圍是通常來講,沒有太大的必要使用DECIMAL數據類型。即使是在需要存儲財務數據時,仍然可以使用BIGINT。比如需要精確到萬分之一,那麼可以將數據乘以一百萬然後使用TIMESTAMP使用4個字節存儲空間,DATETIME使用8個字節存儲空間。因而,TIMESTAMP只能表示1970 - 2038年,比DATETIME表示的範圍小得多,而且TIMESTAMP的值因時區不同而不同。
  • schema的列不要太多。原因是存儲引擎的API工作時需要在服務器層和存儲引擎層之間通過行緩衝格式拷貝數據,然後在服務器層將緩衝內容解碼成各個列,這個轉換過程的代價是非常高的。如果列太多而實際使用的列又很少的話,有可能會導致CPU佔用過高。

創建高性能索引

索引是提高MySQL查詢性能的一個重要途徑,但過多的索引可能會導致過高的磁盤使用率以及過高的內存佔用,從而影響應用程序的整體性能。應當儘量避免事後纔想起添加索引,因爲事後可能需要監控大量的SQL才能定位到問題所在,而且添加索引的時間肯定是遠大於初始添加索引所需要的時間,可見索引的添加也是非常有技術含量的。

MySQL索引

索引原理

除了詞典,生活中隨處可見索引的例子,如火車站的車次表、圖書的目錄等。它們的原理都是一樣的,通過不斷的縮小想要獲得數據的範圍來篩選出最終想要的結果,同時把隨機的事件變成順序的事件,也就是我們總是通過同一種查找方式來鎖定數據。

數據庫也是一樣,但顯然要複雜許多,因爲不僅面臨着等值查詢,還有範圍查詢(>、<、between、in)、模糊查詢(like)、並集查詢(or)等等。數據庫應該選擇怎麼樣的方式來應對所有的問題呢?我們回想字典的例子,能不能把數據分成段,然後分段查詢呢?最簡單的如果1000條數據,1到100分成第一段,101到200分成第二段,201到300分成第三段……這樣查第250條數據,只要找第三段就可以了,一下子去除了90%的無效數據。但如果是1千萬的記錄呢,分成幾段比較好?稍有算法基礎的同學會想到搜索樹,其平均複雜度是lgN,具有不錯的查詢性能。但這裏我們忽略了一個關鍵的問題,複雜度模型是基於每次相同的操作成本來考慮的,數據庫實現比較複雜,數據保存在磁盤上,而爲了提高性能,每次又可以把部分數據讀入內存來計算,因爲我們知道訪問磁盤的成本大概是訪問內存的十萬倍左右,所以簡單的搜索樹難以滿足複雜的應用場景。

索引的數據結構

前面講了生活中索引的例子,索引的基本原理,數據庫的複雜性,又講了操作系統的相關知識,目的就是讓大家瞭解,任何一種數據結構都不是憑空產生的,一定會有它的背景和使用場景,我們現在總結一下,我們需要這種數據結構能夠做些什麼,其實很簡單,那就是:每次查找數據時把磁盤IO次數控制在一個很小的數量級,最好是常數數量級。那麼我們就想到如果一個高度可控的多路搜索樹是否能滿足需求呢?就這樣,b+樹應運而生。

b+樹

b+樹

如上圖,是一顆b+樹,這裏只說一些重點,淺藍色的塊我們稱之爲一個磁盤塊,可以看到每個磁盤塊包含幾個數據項(深藍色所示)和指針(黃色所示),如磁盤塊1包含數據項17和35,包含指針P1、P2、P3,P1表示小於17的磁盤塊,P2表示在17和35之間的磁盤塊,P3表示大於35的磁盤塊。真實的數據存在於葉子節點即3、5、9、10、13、15、28、29、36、60、75、79、90、99。非葉子節點只不存儲真實的數據,只存儲指引搜索方向的數據項,如17、35並不真實存在於數據表中。

b+樹的查找過程

如圖所示,如果要查找數據項29,那麼首先會把磁盤塊1由磁盤加載到內存,此時發生一次IO,在內存中用二分查找確定29在17和35之間,鎖定磁盤塊1的P2指針,內存時間因爲非常短(相比磁盤的IO)可以忽略不計,通過磁盤塊1的P2指針的磁盤地址把磁盤塊3由磁盤加載到內存,發生第二次IO,29在26和30之間,鎖定磁盤塊3的P2指針,通過指針加載磁盤塊8到內存,發生第三次IO,同時內存中做二分查找找到29,結束查詢,總計三次IO。真實的情況是,3層的b+樹可以表示上百萬的數據,如果上百萬的數據查找只需要三次IO,性能提高將是巨大的,如果沒有索引,每個數據項都要發生一次IO,那麼總共需要百萬次的IO,顯然成本非常非常高。

b+樹性質

1. 通過上面的分析,我們知道IO次數取決於b+數的高度h,假設當前數據表的數據爲N,每個磁盤塊的數據項的數量是m,則有h=㏒(m+1)N,當數據量N一定的情況下,m越大,h越小;而m = 磁盤塊的大小 / 數據項的大小,磁盤塊的大小也就是一個數據頁的大小,是固定的,如果數據項佔的空間越小,數據項的數量越多,樹的高度越低。這就是爲什麼每個數據項,即索引字段要儘量的小,比如int佔4字節,要比bigint8字節少一半。這也是爲什麼b+樹要求把真實的數據放到葉子節點而不是內層節點,一旦放到內層節點,磁盤塊的數據項會大幅度下降,導致樹增高。當數據項等於1時將會退化成線性表。

2. 當b+樹的數據項是複合的數據結構,比如(name,age,sex)的時候,b+數是按照從左到右的順序來建立搜索樹的,比如當(張三,20,F)這樣的數據來檢索的時候,b+樹會優先比較name來確定下一步的所搜方向,如果name相同再依次比較age和sex,最後得到檢索的數據;但當(20,F)這樣的沒有name的數據來的時候,b+樹就不知道下一步該查哪個節點,因爲建立搜索樹的時候name就是第一個比較因子,必須要先根據name來搜索才能知道下一步去哪裏查詢。比如當(張三,F)這樣的數據來檢索時,b+樹可以用name來指定搜索方向,但下一個字段age的缺失,所以只能把名字等於張三的數據都找到,然後再匹配性別是F的數據了, 這個是非常重要的性質,即索引的最左匹配特性。

b樹和b+樹的區別

  • 在b樹中,你可以將鍵和值存放在內部節點和葉子節點;但在b+樹中,內部節點都是鍵,沒有值,葉子節點同時存放鍵和值。

  • b+樹的葉子節點有一條鏈相連,而b樹的葉子節點各自獨立。

    img

索引有哪幾種類型?

主鍵索引:數據列不允許重複,不允許爲NULL,一個表只能有一個主鍵。

唯一索引:數據列不允許重複,允許爲NULL值,一個表允許多個列創建唯一索引。

普通索引:基本的索引類型,沒有唯一性的限制,允許爲NULL值。

全文索引:目前搜索引擎使用的一種關鍵技術。

建索引的幾大原則

1. 最左前綴匹配原則,非常重要的原則,mysql會一直向右匹配直到遇到範圍查詢(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)順序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引則都可以用到,a,b,d的順序可以任意調整。

2. =和in可以亂序,比如a = 1 and b = 2 and c = 3 建立(a,b,c)索引可以任意順序,mysql的查詢優化器會幫你優化成索引可以識別的形式。

3. 儘量選擇區分度高的列作爲索引,若是不能有效區分數據的列不適合做索引列(如性別,男女未知,最多也就三種,區分度實在太低)。

4. 索引列不能參與計算,保持列“乾淨”,比如from_unixtime(create_time) = ’2014-05-29’就不能使用到索引,原因很簡單,b+樹中存的都是數據表中的字段值,但進行檢索時,需要把所有元素都應用函數才能比較,顯然成本太大。所以語句應該寫成create_time = unix_timestamp(’2014-05-29’)。

5. 儘量的擴展索引,不要新建索引。比如表中已經有a的索引,現在要加(a,b)的索引,那麼只需要修改原來的索引即可。

6. 較頻繁作爲查詢條件的字段纔去創建索引,更新頻繁字段不適合創建索引。

特定類型查詢優化

優化COUNT()查詢

最簡單的就是當使用COUNT(*)時,並不是我們所想象的那樣擴展成所有的列,實際上,它會忽略所有的列而直接統計所有的行數。

我們最常見的誤解也就在這兒,在括號內指定了一列卻希望統計結果是行數,而且還常常誤以爲前者的性能會更好。但實際並非這樣,如果要統計行數,直接使用COUNT(*),意義清晰,且性能更好。

優化關聯查詢

在大數據場景下,表與表之間通過一個冗餘字段來關聯,要比直接使用JOIN有更好的性能。如果確實需要使用關聯查詢的情況下,需要特別注意的是:

  • 確保ON和USING字句中的列上有索引。在創建索引的時候就要考慮到關聯的順序。當表A和表B用列c關聯的時候,如果優化器關聯的順序是A、B,那麼就不需要在A表的對應列上創建索引。沒有用到的索引會帶來額外的負擔,一般來說,除非有其他理由,只需要在關聯順序中的第二張表的相應列上創建索引(具體原因下文分析)。
  • 確保任何的GROUP BY和ORDER BY中的表達式只涉及到一個表中的列,這樣MySQL纔有可能使用索引來優化。

要理解優化關聯查詢的第一個技巧,就需要理解MySQL是如何執行關聯查詢的。當前MySQL關聯執行的策略非常簡單,它對任何的關聯都執行嵌套循環關聯操作,即先在一個表中循環取出單條數據,然後在嵌套循環到下一個表中尋找匹配的行,依次下去,直到找到所有表中匹配的行爲爲止。然後根據各個表匹配的行,返回查詢中需要的各個列。

太抽象了?以上面的示例來說明,比如有這樣的一個查詢:

SELECT A.xx,B.yy   
FROM A INNER JOIN B USING(c)  
WHERE A.xx IN (5,6)  

假設MySQL按照查詢中的關聯順序A、B來進行關聯操作,那麼可以用下面的僞代碼表示MySQL如何完成這個查詢:

outer_iterator = SELECT A.xx,A.c FROM A WHERE A.xx IN (5,6);  
outer_row = outer_iterator.next;  
while(outer_row) {  
    inner_iterator = SELECT B.yy FROM B WHERE B.c = outer_row.c;  
    inner_row = inner_iterator.next;  
    while(inner_row) {  
        output[inner_row.yy,outer_row.xx];  
        inner_row = inner_iterator.next;  
    }  
    outer_row = outer_iterator.next;  
}  

可以看到,最外層的查詢是根據A.xx列來查詢的,A.c上如果有索引的話,整個關聯查詢也不會使用。再看內層的查詢,很明顯B.c上如果有索引的話,能夠加速查詢,因此只需要在關聯順序中的第二張表的相應列上創建索引即可。

優化LIMIT分頁

當需要分頁操作時,通常會使用LIMIT加上偏移量的辦法實現,如果有對應的索引(覆蓋索引),效率很高,因爲索引默認在B+樹的葉子節點(從左到右)從小到大的使用單向鏈表排序好了;否則,MySQL需要做大量的文件排序操作。

優化UNION

除非確實需要服務器去重,否則就一定要使用UNION ALL,如果沒有ALL關鍵字,MySQL會給臨時表加上DISTINCT選項,這會導致整個臨時表的數據做唯一性檢查,這樣做的代價非常高。當然即使使用ALL關鍵字,MySQL總是將結果放入臨時表,然後再讀出,再返回給客戶端。

優化WHERE子句

1. 對查詢進行優化,應儘量避免全表掃描,首先應考慮在 where 及 order by 涉及的列上建立索引。
2. 應儘量避免在 where 子句中對字段進行 null 值判斷,否則將導致引擎放棄使用索引而進行全表掃描,如:

select id from t where num is null
-- 可以在num上設置默認值0,確保表中num列沒有null值,然後這樣查詢:
select id from t where num=0

3. 應儘量避免在 where 子句中使用 != 或 <> 操作符,否則引擎將放棄使用索引而進行全表掃描。
4. 應儘量避免在 where 子句中使用 or 來連接條件,否則將導致引擎放棄使用索引而進行全表掃描,如:

select id from t where num=10 or num=20
-- 可以這樣查詢:
select id from t where num=10 union all select id from t where num=20

5. in 和 not in 也要慎用,否則會導致全表掃描,如:

select id from t where num in(1,2,3) 
-- 對於連續的數值,能用 between 就不要用 in 了:
select id from t where num between 1 and 3

6. 下面的查詢也將導致全表掃描:select id from t where name like ‘%李%’若要提高效率,可以考慮全文檢索。
7. 如果在 where 子句中使用參數,也會導致全表掃描。因爲SQL只有在運行時纔會解析局部變量,但優化程序不能將訪問計劃的選擇推遲到運行時;它必須在編譯時進行選擇。然 而,如果在編譯時建立訪問計劃,變量的值還是未知的,因而無法作爲索引選擇的輸入項。如下面語句將進行全表掃描:

select id from t where num=@num
-- 可以改爲強制查詢使用索引:
select id from t with(index(索引名)) where num=@num

8.不要在 where 子句中的“=”左邊進行函數、算術運算或其他表達式運算,否則系統將無法使用索引。如:

-- 使用函數
select id from t where substring(name,1,3)=’abc’
-- 應改爲:
select id from t where name like ‘abc%’

-- 使用算術運算
select id from t where num/2=100
-- 應改爲:
select id from t where num=100*2

事務

什麼是數據庫事務?

事務是一個不可分割的數據庫操作序列,也是數據庫併發控制的基本單位,其執行的結果必須使數據庫從一種一致性狀態變到另一種一致性狀態。事務是邏輯上的一組操作,要麼都執行,要麼都不執行。

事務最經典也經常被拿出來說例子就是轉賬了。

假如小明要給小紅轉賬1000元,這個轉賬會涉及到兩個關鍵操作就是:將小明的餘額減少1000元,將小紅的餘額增加1000元。萬一在這兩個操作之間突然出現錯誤比如銀行系統崩潰,導致小明餘額減少而小紅的餘額沒有增加,這樣就不對了。事務就是保證這兩個關鍵操作要麼都成功,要麼都要失敗。

事物的四大特性(ACID)介紹一下?

關係性數據庫需要遵循ACID規則,具體內容如下:

  • 原子性: 事務是最小的執行單位,不允許分割。事務的原子性確保動作要麼全部完成,要麼完全不起作用;
  • 一致性: 執行事務前後,數據保持一致,多個事務對同一個數據讀取的結果是相同的;
  • 隔離性: 併發訪問數據庫時,一個用戶的事務不被其他事務所幹擾,各併發事務之間數據庫是獨立的;
  • 持久性: 一個事務被提交之後。它對數據庫中數據的改變是持久的,即使數據庫發生故障也不應該對其有任何影響。

什麼是事務的隔離級別?MySQL的默認隔離級別是什麼?

爲了達到事務的四大特性,數據庫定義了4種不同的事務隔離級別,由低到高依次爲Read uncommitted、Read committed、Repeatable read、Serializable,這四個級別可以逐個解決髒讀、不可重複讀、幻讀這幾類問題。

隔離級別 髒讀 不可重複讀 幻讀
READ-UNCOMMITTED
READ-COMMITTED ×
REPEATABLE-READ × ×
SERIALIZABLE × × ×

這裏需要注意的是:MySQL 默認採用的 REPEATABLE_READ 隔離級別 Oracle 默認採用的 READ_COMMITTED隔離級別

什麼是髒讀?不可重複讀?幻讀?

  • 髒讀:針對未提交數據】如果一個事務中對數據進行了更新,但事務還沒有提交,另一個事務可以“看到”該事務沒有提交的更新結果
  • 不可重複讀:【針對其他提交前後,讀取數據本身的對比】不可重複讀取是指同一個事務在整個事務過程中對同一筆數據進行讀取,每次讀取結果都不同
  • 幻讀:【針對其他提交前後,讀取數據條數的對比幻讀是指同樣一筆查詢在整個事務過程中多次執行後,查詢所得的結果集是不一樣的

從鎖的類別上分MySQL都有哪些鎖呢?

  • 共享鎖:又叫做讀鎖。 當用戶要進行數據的讀取時,對數據加上共享鎖。共享鎖可以同時加上多個。
  • 排他鎖:又叫做寫鎖。 當用戶要進行數據的寫入時,對數據加上排他鎖。排他鎖只可以加一個,他和其他的排他鎖,共享鎖都相斥。

隔離級別與鎖的關係

  • Read Uncommitted:讀取數據不需要加共享鎖,這樣就不會跟被修改的數據上的排他鎖衝突
  • Read Committed:讀操作需要加共享鎖,但是在語句執行完以後釋放共享鎖;
  • Repeatable Read:讀操作需要加共享鎖,但是在事務提交之前並不釋放共享鎖,也就是必須等待事務執行完畢以後才釋放共享鎖。
  • SERIALIZABLE:限制性最強的隔離級別,因爲該級別鎖定整個範圍的鍵,並一直持有鎖,直到事務完成。

MySQL中InnoDB引擎的行鎖是怎麼實現的?

答:InnoDB是基於索引來完成行鎖

  1. 對於UPDATE、DELETE和INSERT語句,InnoDB會自動給涉及數據集加排他鎖;
  2. 對於普通SELECT語句,InnoDB不會加任何鎖,當然我們也可以顯示的加鎖;
    共享鎖:select * from tableName where id = 1 lock in share more
    ​排他鎖:select * from tableName where id = 1 for update
    並且 id 是有索引鍵的列,如果 id 不是索引鍵那麼InnoDB將完成表鎖,併發將無從談起

數據庫的樂觀鎖和悲觀鎖是什麼?怎麼實現的?

悲觀鎖:假定會發生併發衝突,屏蔽一切可能違反數據完整性的操作。在查詢完數據的時候就把事務鎖起來,直到提交事務。實現方式:使用數據庫中的鎖機制【適用場景:寫多讀少】

樂觀鎖:假設不會發生併發衝突,只在提交操作時檢查是否違反數據完整性。在修改數據的時候把事務鎖起來,通過version的方式來進行鎖定。實現方式:一般會使用版本號機制或CAS算法實現【適用場景:寫少讀多】

// 樂觀鎖Version版本實現
// 1.查詢出商品信息
select status,version from t_goods where id=#{id}
// 2.根據商品信息生成訂單
// 3.修改商品status爲2
update t_goods 
set status=2,version=version+1
where id=#{id} and version=#{version}

參考文章:
https://tech.meituan.com/2014/06/30/mysql-index.html
https://www.iteye.com/news/32381

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