MYSQL 45講學習筆記
一、一條sql是怎麼執行的
-
mysql的架構:
客戶端
連接器
查詢緩存
分析器
優化器
執行器
存儲引擎
二、一條SQL更新是如何執行的
重要的日誌模塊redolog、binlog
-
redolog
這裏舉了一個孔乙己老闆賒賬記賬的故事。
一條記錄需要更新的時候,innoDB引擎會先把記錄寫到redo日誌中,然後更新內存,這時候算是完成了,等到系統比較空閒的時候,才把redo日誌更新到磁盤中。
relog的特點:只有在innodb中才有,是幾個文件,循環寫入(也就是如果滿了,那麼就把記錄更新一下,然後擦除,然後補上去)
-
binlog
(1)binglog是Server層的日誌,其實也就是執行器記錄的日誌。
(2)這裏先說一下更新一行記錄做的過程:重點🚨
- 執行器找引擎要id=2的那一行記錄,拿到以後,給他+1,
- 之後調用引擎的接口,引擎更新把這條 記錄更新到內存中,並記錄把這操作記錄到redolog中,redolog處於prepare狀態,告訴執行器我執行完了,隨時可以提交事務。
- 執行器生成這個操作的binlog日誌,然後調用引擎的提交事務接口,引擎也把redolog改爲提交commit狀態,更新完成。
(3)根據log的寫入順序我們可以知道1個事情:
- 如果沒有redolog,在引擎執行命令的時候發生異常,是沒法恢復的,因爲binlog還沒有記 錄擦操作。cash-save
(4)那麼爲什麼redolog有兩個狀態?
- 結合兩個日誌的寫入順序,假設在引擎處redolog已經寫了,然後系統掛掉了,那麼binglog還沒記錄,這個時候如果系統重啓時會讀redolog的東西的,但是你binlog你沒有這個記錄呀,當你加入要創建一個從庫,使用binlog創建,就會少一條操作記錄,主從數據庫內容就不一樣了。
- 上面一點講到的從庫,備庫是經常做的事情,他們一般使用全量備份+binlog歸檔來實現,這個時候binglog如果少了一條操作記錄,主從就會不一致的。
(5)說一說爲什麼DBA能夠將數據庫恢復到任何時間
- 其實就是第一個有一個備份庫,第二個就是依靠binlog
三、隔離問題
讀未提交 讀已提交 可重複度 串行化(表鎖)
那麼其實本質上是數據庫創建了個視圖,這樣就能做到可重複度和讀已提交
在MYSQL中實際上,每條記錄在更新的時候都會記錄一條回滾操作。因此也會存在一個問題,這記錄回滾操作一定不能太多,當你是一個長事務的時候就會讓這些回滾記錄非常大,要避免這種情況,其實就是當你開啓事務的時候,最後一定要提交或者回滾。重點🚨
四、深入淺出索引(一)
- 分爲主鍵索引和普通索引,在innodb中,表都是根據主鍵順序存放的。假設一個表中有主鍵,有普通索引,如果用普通索引去查數據,實際過程中,他是先用普通索引找到對應的主鍵,然後根據主鍵的索引去找到應對的記錄。其實原因就是主鍵索引存放的是整行數據,普通索引存放的是對應主鍵的信息。這個過程也叫做重點:回表。**因此爲了提高效率,我們應該用主鍵來查詢數據。**也正是因爲有回表,所以innodb一定要有主鍵,這是他的B+樹結構決定的,而MYisam就不需要了,也是因爲他的B+樹存的是地址導致的,這個後面我們會講到。🚨
- 看了上面,我們知道普通索引存的其實是主鍵信息,那麼其實在某種情況下,主鍵的確定也很重要,比如一個表要記錄身份證,你會把身份中作爲主鍵嗎?應該不可以的,這樣普通索引每個佔用的空間就很大,應該使用一般的數字自增就行了。
- mysql的innodb使用B+樹,其實也就是N叉樹,爲什麼呢?其實就是二叉樹的存放數據太分散了,太分散的數據尋址一定是耗時的,使用N叉樹就能避免多次的分區尋址。
- B+樹的頁分裂。頁分裂就是你的新id在索引二叉樹中超過了N,這個時候就要分裂,經常的分裂會的導致效率貶低,所以建議使用自增長的主鍵作爲索引,反例就是身份證做主鍵,每次添加的身份證id太隨機了,就經常需要頁分裂。在使用InnoDB存儲引擎時,如果沒有特別的需要,請永遠使用一個與業務無關的自增字段作爲主鍵。
- 頁合併:如果刪除了某個索引的一些值,如果不處理的話,我們要經過的節點會多一點,如果把二叉樹重新整頓一下的話,經過的會更少。一般我們會設置,如果節點的數量小於N/2 就和附近的節點合併一下。
五、深入淺出索引(二)
-
覆蓋索引:我們知道了有回表這一現象的存在,本質是普通索引存的是主鍵信息,如果我們要的東西就是主鍵呢?,這樣就不用回表了,這樣就提高速度了。
-
最左前綴原則,假如我有了a,b兩者的聯合主鍵,請問我是否要單獨創建一個a的主鍵呢?答案是不用,因爲B+樹的索引機構,可以利用他的最左前綴來定位。比如你要找到張三的信息,(老王,1),(老豆,2)(張三,10),(張三,20),根據聯合主鍵他會迅速找到(張三,10),然後依次向後找符合張三條件的。**就是隻要滿足最左前綴,那麼就可以使用索引,重要🚨。**所以我們在創建聯合索引的時候,前後順序要考慮一下,原則就是可以少維護一個索引,就是不錯的。
-
索引下推:比如我們要搜索姓氏是張,年齡爲10的男孩,索引是姓名和年齡的聯合索引。在mysql5.6之前它會先找到張開頭的,然後回表,去對比年齡爲10的,5.6以後,能夠做到,找到張開頭以後,直接對比年齡是否符合要求,然後再回表獲取其他信息。這樣極大減少了回表次數。
-
我們再仔細研究一下索引的使用情況:也就是在有聯合索引的時候,不同語句情況下,到底所有有生效嗎?基本基於最左前綴原則
我們假設有個表:employees.titles表,他的聯合主鍵索引是<emp_no, title, from_date> 我們分別看一下不同sql使用索引的情況:
- select * from employees.titles where emp_no=‘1’ and titile=‘1’ and from_date=‘2020’;
- 這個是全列匹配,聯合索引的字段都用上了
- select * from employees.titles where emp_no=‘1’
- 最左前綴匹配,這樣用到了emp_no一個索引
- SELECT * FROM employees.titles WHERE emp_no=‘10001’ AND from_date=‘1986-06-26’;
- 這裏和上面一樣,他缺失了title這個字段,最後只用到了emp_no一個索引。
- SELECT * FROM employees.titles WHERE from_date=‘1986-06-26’;
- 這中就不會用到索引 — 索引失效🚨
- SELECT * FROM employees.titles WHERE emp_no=‘10001’ AND title LIKE ‘Senior%’;
- 這個會用到索引,但是有要求,%不能在開頭,否則失效,只用到一個索引。
- SELECT * FROM employees.titles WHERE emp_no < ‘10010’ and title=‘Senior Engineer’;
- 只有第一列的範圍才能用到索引,而且只是第一個索引,後面的title都不會走走索引。
- SELECT * FROM employees.titles WHERE emp_no=‘10001’ AND left(title, 6)=‘Senior’;
- 對於有函數的是不會用索引的,也就是這一句只用到了emp_no這個索引。
- select * from employees.titles where emp_no=‘1’ and titile=‘1’ and from_date=‘2020’;
六、B樹、B+樹的知識
這個博客講的特別好:http://blog.codinglabs.org/articles/theory-of-mysql-index.html 2011年的,太厲害了,主要的圖片也是來自於這篇博客,總之寫得很精彩。
-
索引是磁盤上的東西,一般來說很大(我們講那種索引大的情況),讀索引的過程是把索引文件讀到內存中,對於索引很大的文件,就會需要分部分來讀取。
-
對於平衡二叉樹或者是紅黑樹,物理實現上是數組,相關聯的數據有時候是距離很遠的,這就會導致你讀的索引部分會很多,這樣效率會很低的。
-
爲了索引而出現的結構就是B樹了。
-
有個概念叫做局部性原理,就是磁盤預讀,讀取磁盤的時候,會把這個地址後面的東西也讀取到內存中,所以我們應該把相近的數據放到一起,這樣能夠一起讀取出來,減少io次數。這個原理是屬於計算機原理的知識。
-
那麼對於B樹,我們知道他是N叉書,也就是一個節點存了N個數據,這樣就能充分利用這個局部性原理和磁盤預讀。
-
B樹的結構圖:他的特點是:
- 每個非葉子節點(非?)由key和指針組成,key數量是指針數量+1。
- key從左到右升序
- 如圖我們可以看到比15大,比56小的,依靠他們中間的指針指向下一個節點,這個節點的key一定是小於56,大於15
// 他的搜索僞代碼 BTree_Search(node, key) { if(node == null) return null; foreach(node.key) { if(node.key[i] == key) return node.data[i]; if(node.key[i] > key) return BTree_Search(point[i]->node); } return BTree_Search(point[i+1]->node); } data = BTree_Search(root, my_key);
-
B+樹的結構是:一些特點:
- 他的內節點不存data數據,只有key
- 他的葉子節點沒有指針
-
B+樹真正使用會進一步改進,如下圖:改進點:
- 帶有順序訪問指針,提高了區間訪問能力,當我們查詢15到30的數的時候,定位到15以後,直接完後找就完事了。這也是爲什麼說B樹找範圍能力沒有B+樹厲害
總結一下:兩個問題:
- 平衡二叉樹/紅黑樹 與 B樹的區別是什麼?爲什麼索引要用B樹而不用那兩個呢?
- 平衡二叉樹是二叉樹,每個節點只有兩個葉子節點,關聯的數據可能在不同的位置,讀取次數多,效率低下。
- B樹就是爲了解決這問題而誕生的,他的多叉樹特性保證了他讀取io的次數會降低,而卻他的節點數據是有順序的,利用了磁盤預讀的特點。
- B樹和B+樹的區別
- B+樹葉子節點不存放指針,只存放着數據,這樣只需要遍歷葉子節點以及下一個葉子節點即可。
- B+樹的結構是有序數組鏈表+平衡多叉樹,B樹是有序數組+平衡多叉樹。也就是B+樹的葉子節點是個鏈表,每個數據指向下一個相鄰的數據。
- B+樹的特點決定了他很擅長範圍查找,而b樹沒法和他比較。
-
MyISAM和innoDB的索引結構區別
-
myisam索引最後一個葉子節點存放的是地址,普通索引和主鍵索引是一樣的,也是存放地址,地址指向對應的數據庫記錄。也叫做非聚集索引,索引和數據是分離的。
-
innoDB
對於主鍵,他存的不是地址,而是整個記錄,就是innoDB的數據文件本身就是索引文件,myisam的索引和數據是分離的。普通索引的節點存的內容是主鍵。
-
-
InnoDB和MyISAM分別是怎麼存儲數據的,優缺點是什麼— 後面總結一下,不單是結構問題,還有事務,鎖等問題考慮對比。
MyIsam不支持事務。
七、全局鎖和表鎖
鎖分爲全局鎖、表鎖、行鎖
-
全局鎖:
鎖住全表,只能進行select查詢操作,增刪改,ddl都不行,一般用於我們需要備份主庫的時候。語法是Flush table with read lock,開啓時候就會鎖住全部的表,但是如果有用戶在我們備份的時候操作怎麼辦了?只能等我們結束嗎?其實不然,結合之前我們講隔離級別的時候,我們有提到數據庫會有回滾記錄,或者說是MVCC版本控制,他給你一個視圖,你可以對視圖操作,實際操作上就是你在鎖住全表之前,開啓一個事務,就行了,官方自帶的邏輯備份工具mysqldump。當 mysqldump 使用參數–single-transaction 的時候,導數據之前就會啓動一個事務。
-
表鎖:
表鎖分爲兩種:表鎖,元數據鎖MDL
-
表鎖:語法是lock tables … read/write,來鎖中某個表。假設我們有兩個會話AB,如果會話A 執行lock t1 read,那麼會話A就只能查詢t1表,查詢其他表會報read lock沒有釋放的錯誤。而會話B能查t1表,也可以查其他表,但是insert這個表會一直被阻塞,直到會話A執行unlock tables。
也就是在某個會話中lock 了一些表特定的read或者write,那麼首先他無法訪問其他表了,其次對於他lock的那些表只能進行lock的操作。🚨
明顯這種限制太大了,Myisam還會用這樣的方式,但是innoDB有顆粒度更細緻的行鎖,幾乎是不會使用表鎖的。
-
元數據鎖metadata lock:是表層面的鎖
這個是數據庫自動給我們加的鎖,當你有增刪改查的時候自動加上讀鎖。當你有DDL語句的時候,就會自動加上寫鎖。寫鎖之間是互斥的,兩個DDL寫的操作要按照順序執行。讀寫鎖之間也是互斥的,另外加上MDL不會主動釋放鎖,只有在事務結束以後纔會釋放鎖,當我們給一個表加字段的時候,就容易導致數據庫掛掉。
解決辦法就是,先去看看select * from information_schema.innodb_trx;這表裏面的事務,考慮把它停掉,或者是我們的DDL語句設置等待時間。
這一部分呢,1、只要就是要知道MDL鎖對我們新增一個字段的影響,已經我們該怎麼做去避免影響。2、就是全局鎖主要應用在備份中,一般我們開啓事務後,在進行備份比較合適。🚨
-
八、行鎖的功過
行鎖就是鎖住每一行記錄的,是在存儲引擎中實現,比如MyISAM就沒有行鎖,只能用表鎖,也就是這個表同 一時刻只能有一個更新操作。這也是他被取代的原因。
-
一個事務掌握一個行鎖之後,其他事務要對這行記錄操作,會被阻塞,只能等待那個行鎖釋放。
-
在 InnoDB 事務中,行鎖是在需要的時候才加上的,但並不是不需要了就立刻釋放,而是要等到事務結束時才釋放。這個就是兩階段鎖協議。知道了這個設定,對我們使用事務有什麼幫助呢?那就是,如果你的事務中需要鎖多個行,要把最可能造成鎖衝突、最可能影響併發度的鎖儘量往後放。
假設你負責實現一個電影票在線交易業務,顧客 A 要在影院 B 購買電影票。我們簡化一點,這
個業務需要涉及到以下操作:-
從顧客 A 賬戶餘額中扣除電影票價;
-
給影院 B 的賬戶餘額增加這張電影票價;
-
記錄一條交易日誌。
也就是說,要完成這個交易,我們需要 update 兩條記錄,並 insert 一條記錄。當然,爲了保
證交易的原子性,我們要把這三個操作放在一個事務中。那麼,你會怎樣安排這三個語句在事務
中的順序呢?
試想如果同時有另外一個顧客 C 要在影院 B 買票,那麼這兩個事務衝突的部分就是語句 2 了。
因爲它們要更新同一個影院賬戶的餘額,需要修改同一行數據。
根據兩階段鎖協議,不論你怎樣安排語句順序,所有的操作需要的行鎖都是在事務提交的時候才
釋放的。所以,如果你把語句 2 安排在最後,比如按照 3、1、2 這樣的順序,那麼影院賬戶餘
額這一行的鎖時間就最少。這就最大程度地減少了事務之間的鎖等待,提升了併發度。 -
-
死鎖問題
innodb是會自動檢測死鎖的,也就是如果發生了鎖等待,引擎會掃一遍所有鎖,看看是不是因爲自己的加入而導致的死鎖,如果是就會回滾事務,這樣別人就能走下去了。
但是每次掃描太費CPU了,解決方向是減少併發度,可以在中間件裏控制範圍數據庫的併發量,或者是mysql修改源碼設置等待隊列。
總結一下鎖:🚨
- 全局鎖用於數據庫的備份,建議開始事務以後鎖庫然後備份,業務會友好點。
- 表鎖主要是MYISAM在用,lock xx read / write,就只能對這個表進行相應操作。幾乎沒啥意義。
- 表鎖的MDL是表層面的,所以寫指的是DDL,讀指的是增刪改查。讀寫鎖之間互斥,寫鎖之間也互斥。MDL在事務提交後釋放鎖。
- 行鎖也是事務提交以後纔會釋放,讀操作不會有互斥,更新操作就會有互斥情況。
九、事務到底是隔離的還是不隔離的
首先清楚innodb默認是可重複度PR,我們知道這可能會導致幻讀。下面我們探討的就是這麼做到的可重複度和讀已提交。
前面我們說到了每行數據的多版本控制,MVCC,本質是創建了一個視圖,這個視圖不是數據庫的那個視圖,其實是一個快照,或者說是一個up_limit_id,它的意義是:表示我只讀取比這個up_limit_id小於或者等於的數據版本。
那麼up_limit_id這是個什麼東西呢?是個版本號,每一次的事務操作都會生成一個row trx_id,那麼每次事務啓動的時候會獲取最大的那個row trx_id,也就是up_limit_id
還有一種情況會再一次獲取up_limit_id版本,就是更新操作,更新操作的執行順序是:先讀取,再更新。(因此我們看pdf裏面的Q1的最後結果是3。注意了他的事務C是自動提交的,但是AB都是begin,最後才提交,還有就是你測試的時候,要開多個窗口,纔是模擬多個事務)這個比較重要,再寫一遍:更新操作的執行順序是:先讀取,再更新。🚨
所以你能知道爲什麼他可以重複讀了吧。開啓事務記錄up_limit_id------>update就重新獲取一次,select就還是原來的id--------->獲取正確的版本 。 在可重複讀隔離級別下,只需要在事務開始的時候找到那個 up_limit_id,之後事務裏的其他查詢都共用這個 up_limit_id;
讀已提交隔離級別的時候就是:每一次的查詢都會更新一下 up_limit_id。
重點再說一遍:更新操作的執行順序是:先讀取,再更新🚨
所以你能知道爲什麼他可以重複讀了吧。開啓事務記錄up_limit_id------>update就重新獲取一次,select就還是原來的id--------->獲取正確的版本 。 在可重複讀隔離級別下,只需要在事務開始的時候找到那個 up_limit_id,之後事務裏的其他查詢都共用這個 up_limit_id;
讀已提交隔離級別的時候就是:每一次的查詢都會更新一下 up_limit_id。
十、MYSQL是怎麼保證主備一致的呢?
假設A是主庫,B是備庫。AB之間會有一個連接的,A內部有個專門的線程專門來通訊備庫。A庫接受到B庫從緩過來的位置,從本地讀取binlog,發給B,B拿到以後寫道自己本地叫做relay log,B中有專門的線程讀取中轉日誌,來執行。
實際情況AB兩個庫會設置成互爲主庫或者互爲輔庫。但B拿到A的binlog後執行,也會記錄binglog,A又拿到B的binlog,這不就循環複製了嗎?那麼binglog裏面有一個server_id,他會看是不是自己這臺機生成的binlog,如果是,那麼就不會執行。
十一、MYSQL怎麼保證高可用
-
上面我們說的是一致性,這裏講的高可用值的是,主從之間的延遲情況。
-
延遲指的是同一個事務,在A庫的執行完成的時間和在B庫執行完成的時間的差值。那麼真正耗時的不是binlog的傳遞,而是B庫讀取relaylog執行的快慢。可能導致這個速度的原因有:
- 有些人會因爲備庫很少用,而把備庫的機器性能配置的很低。當然現在這種情況很少了,因爲主備之間經常切換。
- 備庫壓力太大了。因爲有些人很故意生產的數據庫,所以儘可能的優先使用備庫去查數據,反而導致備庫壓力很大。解決辦法:
- 一主多從
- 把binlog給外部系統,比如hadoop,讓他們來做查詢的工作
- 大事務。比如一次刪除很多數據
- 輔庫的並行能力
-
那麼MYSQL主備切換是個什麼過程呢?
- 確認B庫的延時時間是否低於規定(防止高延遲,否則後面同步的時候很慢)
- 把A庫改爲readonly
- 同步A庫剩下的binlog,直到B庫的延遲時間爲0.
- 把B庫改爲可讀寫
- 把業務請求切換到B庫來
這個叫做可靠性優先策略。
十二、爲什麼備庫延遲幾個小時
對於主庫壓力,使用頻率非常高的情況,從庫如果延遲了,也許再也追不上主庫了。下面我們學習從庫的並行複製能力。
其實是什麼呢?就是從庫的讀取relay log是單線程的,併發度不夠,在MYSQL5.6以後就改爲多線程讀取了。
十三、主庫出現問題了,從庫怎麼辦
首先看一下一主多從的結構圖:主庫主要做寫操作以及一般小部分讀操作。從庫主要分到讀的操作。AA`互爲主備。
當主庫A掛掉以後,BCD會指向A`,他會成爲寫庫。
那麼有個問題,但A掛掉了,B去連接A`,但是不能保證A 和 A\在那個時刻是一樣的呀?當然你可以等到A主從庫之間完成同步,但是也可以打開GTID這個東西,在這個模式下一主多從切換就會方便很多。
十四、主從讀寫分離
一主多從架構的應用場景:讀寫分離。
讀寫分離在哪實現分開查詢不同數據庫。可能是在客戶端直連,也就是目前我這個項目的情況,直接選擇特定的數據源。第二個就是在客戶端和數據庫之間搞個代理,代理根據上下文來判斷應該去那個數據庫。
但是都會導致一個問題,如果我剛剛更新的主庫數據,我馬上要讀取,這個時候從庫還沒有同步呢,怎麼辦?這個問題不想主備庫之間延遲問題可以避免。這個不能100%避免。有以下幾種辦法:
- 強制走主庫:區分我們的查詢,如果對於比如我買完東西,返回以後我一定要看到我的訂單,那麼就強制他到主庫去查。對於比如上新一個產品,晚一點看到也沒關係,那麼就放在從庫去查。看起來這個辦法是逃避現實的辦法,但是實際用的比較多。但是如果你的需求都是要求實時性的話,這個辦法就不太好了,因爲相當於沒有從庫了。
- sleep方案,就是我在從庫查詢的時候,select sleep(1) 一下,這樣很大概率主從完成了同步,但是明顯者不太精確,可能本來只要0.5s的同步,硬生生被你拖到了1s。
- 判斷主從是否無延遲。直接看延遲時間是否爲0,或者是GTID主從庫日誌的記錄
- 更多的不做展開學習…
這幾種方案中,有的方案看上去是做了妥協,有的方案看上去不那麼靠譜兒,但都是有實際應用場景的,你需要根據業務需求選擇。
即使是最後等待位點和等待 GTID 這兩個方案,雖然看上去比較靠譜兒,但仍然存在需要權衡的情況。如果所有的從庫都延遲,那麼請求就會全部落到主庫上,這時候會不會由於壓力突然增大,把主庫打掛了呢?
其實,在實際應用中,這幾個方案是可以混合使用的。比如,先在客戶端對請求做分類,區分哪些請求可以接受過期讀,而哪些請求完全不能接受過期讀;然後,對於不能接受過期讀的語句,再使用等 GTID 或等位點的方案。
總結一下:
上面我們討論了
- 主備/從之間是怎麼保持一致的,binlog,連接,relaylog,循環複製。
- 怎麼實現高可用,就是主從之間的延遲情況。一主多從,主從對稱配置,輔庫少一點大事務,輔庫並行複製。
- 主庫怎麼切換到備庫的,延遲時間限制----->主庫改爲只讀----->延遲時間爲0----->備庫改爲寫。
- 一主多從模式下,主庫掛了,從機怎麼連接到備機。GTID 全局事務id。具體沒深入研究。
- 讀寫分離導致的“過期讀”解決辦法。
十五、到底能不能用join
select * from t1 straight_join t2 on t1.a = t2.a;這個過程是這樣的:
- 對驅動表t1拿取一行記錄。全部搞下來就是全表掃描
- 每一行記錄獲取a,然後跟據a去t2表中查,這個查詢是樹搜索。
- 找到t2中符合條件的行,和T1取出的數據組成一行
- 循環第二第三步,直到走到t1表末尾
明顯的驅動表走全表掃描,被驅動表是走的樹查找,所有儘可能的驅動表要小一點(更仔細的說是,用各自的條件去看涉及的數據量多還是少)。前提是能夠使用到被驅動表的索引。
十六、MYISAM和InnoDB
My:沒有行鎖,沒有MMVC多版本控制,查詢會相對快一點
innoDB:有行鎖,索引是記錄有一定的緩存,有MVCC。
十七、索引失效的情況
- or 讓本來有索引的反而不走索引了,除非你每個條件都有索引
- 聯合索引如果不是第一個就不會使用到
- 立刻以%開頭的,不會用到索引 N%表示N開頭,這個會用到索引
- 如果列是字符串的,條件中一定要用引號標註
- 如果mysql優化器判斷全表查找比用索引更加快,那麼就用全表查找
- <> \ not \ in \ exists \
- 語句包含函數
十八、幻讀學習
什麼是幻讀:第一次讀取數據庫,根據返回的結果後面的insert語句是沒有問題的,但是執行insert以後報錯主鍵衝突,這就是幻讀。
解決辦法:在一開始select的時候就加上一把行鎖。而serialize這個級別就是自動查詢的時候自動加行鎖,他也是唯一的不會幻讀的隔離級別。
這個深入學習要看一下原文了,next-key lock,間隙鎖+行鎖配合可以解決幻讀問題。
十九、鎖的分類
- 樂觀鎖:首先先查出數據(連帶着版本),然後進行update的時候再查一次,如果這個時候的version和一開始的version一樣,那麼就update;java代碼應該是一個自旋。
update table set a='a',version = version + 1 where version = #{version} and id = #{id}
- 悲觀鎖:
- 共享鎖:讀鎖。A事務
lock in share mode
鎖住結果的每一行,A事務可以更新,但是其他事務不能再加讀鎖,更不能更新。其他會話可以讀取。也就是我讀取數據的時候,別的事務不允許改變數據。 - 排他鎖:寫鎖,
for update
一個會話中使用這個這個語句,那麼其他會話只能查不能改,只有前面那個會話可以改。目的就是我更新的時候別的會話別動。 - 對於
insert
、update
、delete
等操作。會自動給涉及的數據加排他鎖;
- 共享鎖:讀鎖。A事務