
## 目錄
[TOC]
## 什么是事務?
**事務就是為了保證一組數據庫操作,要么全部成功,要么全部失敗。**
事務是在引擎層實現的,也就是說并不是所有引擎都可以使用事務,MyISAM 就不支持事務,這也是為什么會被 InnoDB 取代的原因。
## 事務的四大特性?
原子性(**A**tomicity,或稱不可分割性)、一致性(**C**onsistency)、隔離性(**I**solation,又稱獨立性)、持久性(**D**urability)。
* **原子性:**一個事務(transaction)中的所有操作,要么全部完成,要么全部不完成,不會結束在中間某個環節。事務在執行過程中發生錯誤,會被回滾(Rollback)到事務開始前的狀態,就像這個事務從來沒有執行過一樣。
* **一致性:**在事務開始之前和事務結束以后,數據庫的完整性沒有被破壞。這表示寫入的資料必須完全符合所有的預設規則,這包含資料的精確度、串聯性以及后續數據庫可以自發性地完成預定的工作。
* **隔離性:**數據庫允許多個并發事務同時對其數據進行讀寫和修改的能力,隔離性可以防止多個事務并發執行時由于交叉執行而導致數據的不一致。事務隔離分為不同級別,包括讀未提交(Read uncommitted)、讀提交(read committed)、可重復讀(repeatable read)和串行化(Serializable)。
* **持久性:**事務處理結束后,對數據的修改就是永久的,即便系統故障也不會丟失。
## 沒有隔離級別的話,多事務并發進行會造成什么問題?
* 臟讀: A事務讀取到了B事務未提交的內容,而B事務后面進行了回滾.
* 不可重復讀: 當設置A事務只能讀取B事務已經提交的部分,會造成在A事務內的兩次查詢,結果竟然不一樣,因為在此期間B事務進行了提交操作.
* 幻讀: A事務讀取了一個范圍的內容,而同時B事務在此期間插入了一條數據.造成"幻覺".
## 事務的隔離級別和各自解決的問題是什么?
隔離性可能會引入**臟讀(dirty read)、不可重復讀(non-repeatable read)、幻讀(phantom read)等問題**,為了解決這些問題就引入了“隔離級別”的概念。
**SQL 標準的事務隔離級別包括:讀未提交(read uncommitted)、讀提交(read committed)、可重復讀(repeatable read)和串行化(serializable)**:
* **讀未提交**:一個事務還沒提交時,它做的變更就能被別的事務看到。
* **讀提交**:一個事務提交之后,它做的變更才會被其他事務看到。
* **可重復讀:**?一個事務執行過程中看到的數據,總是跟這個事務在啟動時看到的數據是一致的。當然在可重復讀隔離級別下,未提交變更對其他事務也是不可見的。
* **串行化:**?顧名思義是對于同一行記錄,“寫”會加“寫鎖”,“讀”會加“讀鎖”。當出現讀寫鎖沖突的時候,后訪問的事務必須等前一個事務執行完成,才能繼續執行。
SQL標準中規定,針對不同的隔離級別,并發事務可以發生不同嚴重程度的問題,具體情況如下:
| 隔離級別 | 臟讀 | 不可重復讀 | 幻讀 |
| --- | --- | --- | --- |
| `讀未提交` | 可能 | 可能 | 可能 |
| `讀提交` | 不可能 | 可能 | 可能 |
| `可重復讀` | 不可能 | 不可能 | 可能 |
| `串行化` | 不可能 | 不可能 | 不可能 |
## InnoDB使用的是哪種隔離級別呢?
InnoDB默認使用的是可重復讀隔離級別. RR
## 數據庫事務的使用的規范有哪些?
1. 控制事務大小,減少鎖定的資源量和鎖定時間長度。
2. 所有的數據檢索都通過索引來完成,從而避免因為無法通過索引加鎖而升級為表鎖。
3. 減少基于范圍的數據檢索過濾條件,避免因為間隙鎖帶來的負面影響而鎖定了不該鎖定的數據。
4. 在業務條件允許下,盡量使用較低隔離級別的事務隔離。減少隔離級別帶來的附加成本。
5. 合理使用索引,讓innodb在索引上面加鎖的時候更加準確。
6. 在應用中盡可能做到訪問的順序執行(串行)。
7. 如果容易死鎖,就可以考慮使用表鎖來減少死鎖的概率。
## InnoDB怎么實現的事務ACID特性?
* redo log重做日志用來保證事務的持久性
* undo log回滾日志保證事務的原子性
* undo log+redo log保證事務的一致性
* 鎖(共享、排他)用來保證事務的隔離性
undo log 實現如下兩個功能:1.實現事務回滾 2.實現MVCC
undo log和redo log記錄物理日志不一樣,它是邏輯日志。可以認為當delete一條記錄時,undo log中會記錄一條對應的insert記錄,反之亦然,當update一條記錄時,它記錄一條對應相反的update記錄。
推薦閱讀,加深理解 : https://www.cnblogs.com/jianzh5/p/11643151.html
## InnoDB的事務為什么是原子性的?
**InnoDB 引擎使用 undo log(歸滾日志)來保證原子性操作**,你對數據庫的每一條數據的改動(INSERT、DELETE、UPDATE)都會被記錄到 undo log 中,比如以下這些操作:
* 你插入一條記錄時,至少要把這條記錄的主鍵值記下來,之后回滾的時候只需要把這個主鍵值對應的記錄刪掉就好了。
* 你刪除了一條記錄,至少要把這條記錄中的內容都記下來,這樣之后回滾時再把由這些內容組成的記錄插入到表中就好了。
* 你修改了一條記錄,至少要把修改這條記錄前的舊值都記錄下來,這樣之后回滾時再把這條記錄更新為舊值就好了。
當事務執行失敗或者調用了 rollback 方法時,就會觸發回滾事件,利用 undo log 中記錄將數據回滾到修改之前的樣子。
## InnoDB是如何保證隔離性的?
**利用鎖和 MVCC 機制**。這里簡單的介紹一下 MVCC 機制,也叫**多版本并發控制**,在使用 READ COMMITTD、REPEATABLE READ 這兩種隔離級別的事務下,每條記錄在更新的時候都會同時記錄一條回滾操作,就會形成一個版本鏈,在執行普通的 SELECT 操作時訪問記錄的版本鏈的過程,這樣子可以使不同事務的讀-寫、寫-讀操作并發執行,從而提升系統性能。
## 事務的持久性如何保證?
持久性的定義:**事務一旦提交,它對數據庫的改變就應該是永久性的。接下來的其他操作或故障不應該對其有任何影響。**
要保證持久性很簡單,就是每次事務提交的時候,都將數據刷磁盤上,這樣一定保證了安全性,但是要知道如果每次事務提交都將數據寫入到磁盤的話,頻繁的 IO 操作,成本太高,數據庫的性能極低,所以這種方式不可取。
InnoDB 引擎是怎么解決的?**InnoDB 引擎引入了一個中間層來解決這個持久性的問題,我們把這個叫做 redo log(歸檔日子)**。
為什么要引入 redo log?redo log 可以保證持久化又可以保證數據庫的性能,相比于直接刷盤,redo log 有以下兩個優勢:
* redo log體積小,畢竟只記錄了哪一頁修改了啥,因此體積小,刷盤快。
* redo log是一直往末尾進行追加,屬于順序IO。效率顯然比隨機IO來的快。
InnoDB 引擎是怎么做的?當有一條記錄需要更新的時候,InnoDB 引擎就會先把記錄寫到 redo log 里面,并更新內存,這個時候更新就算完成了。當數據庫宕機重啟的時候,會將 redo log 中的內容恢復到數據庫中,再根據 undo log和 binlog 內容決定回滾數據還是提交數據。
## 事務的一致性,指的是什么?
**一致性簡單一點說就是數據執行前后都要處于一種合法的狀態**,比如身份證號不能重復,性別只能是男或者女,數據庫應該體現為現實世界的一個映射。
要保證數據庫的數據一致性,要在以下兩個方面做努力:
* **利用數據庫的一些特性來保證部分一致性需求**:比如聲明某個列為`NOT NULL`?來拒絕`NULL`值得插入等。
* **絕大部分還是需要我們程序員在編寫業務代碼得時候來保證**。
## MVCC是什么,如何實現?
多版本并發控制,MVCC是一種并發控制的方法,減少事務中需要鎖定的行數。
## InnoDB的MVCC實現原理是什么?
>簡答
在每一行數據中額外保存兩個隱藏的列:當前行創建時的版本號和刪除時的版本號。數據會保存在某個時間點的快照。`RC`、`RR`?兩種隔離級別的事務在執行普通的讀操作時,通過訪問版本鏈的方法,使得事務間的讀寫操作得以并發執行,從而提升系統性能 。
>加深理解
這兩個列,一個保存了行的創建時間,一個保存了行的過期時間(刪除時間)。當然存儲的并不是實際時間,而是**系統版本號**(sytem version number)。每開始一個新的事務,系統版本號都會自動遞增。事務開始時刻的系統版本號會作為事務的版本號,用來和查詢到的每行記錄的版本號進行比較。
**SELECT**
InnoDB 會根據以下兩個條件檢查每行記錄:
1. InnoDB只查找版本早于當前事務版本的數據行(也就是,行的系統版本號小于或等于事務的系統版本號),這樣可以確保事務讀取的行,要么是在事務開始前已經存在的,要么是事務自身插入或者修改過的。
2. 行的刪除版本要么未定義,要么大于當前事務版本號。這可以確保事務讀取到的行,在事務開始之前未被刪除。
只有符合上述兩個條件的記錄,才能返回作為查詢結果。
**INSERT**
InnoDB為新插入的每一行保存當前系統版本號作為行版本號。
**DELETE**
InnoDB為刪除的每一行保存當前系統版本號作為行刪除標識。
**UPDATE**
InnoDB為插入一行新記錄,保存當前系統版本號作為行版本號,同時保存當前系統版本號到原來的行作為行刪除標識。
保存這兩個額外系統版本號,使大多數讀操作都可以不用加鎖。這樣設計使得讀數據操作很簡單,性能很好,并且也能保證只會讀取到符合標準的行。不足之處是每行記錄都需要額外的存儲空間,需要做更多的行檢查工作,以及一些額外的維護工作。
> MVCC只在REPEATABLE READ和READ COMMITIED兩個隔離級別下工作。其他兩個隔離級別都和 MVCC不兼容 ,因為READ UNCOMMITIED總是讀取最新的數據行,而不是符合當前事務版本的數據行。而SERIALIZABLE則會對所有讀取的行都加鎖。
可參考:
https://www.codercto.com/a/88775.html
https://www.jianshu.com/p/8845ddca3b23
## 可重復讀(repeatable read)級別如何避免幻讀?
* 在快照讀讀情況下,mysql通過mvcc來避免幻讀。
* 在當前讀讀情況下,mysql通過next-key來避免幻讀。
>復習一下
**什么是幻讀**
事務不是獨立執行時發生的一種現象,例如第一個事務對一個表中的數據進行了修改,這種修改涉及到表中的全部數據行。 同時,第二個事務也修改這個表中的數據,這種修改是向表中插入一行新數據。那么,以后就會發生操作第一個事務的用戶發現表中還有沒有修改的數據行,就好象 發生了幻覺一樣。
**什么是next-key鎖**
可以簡單的理解為X鎖+GAP鎖
**臨鍵鎖(Next-key Locks):**是記錄鎖與間隙鎖的組合,它的封鎖范圍,既包含索引記錄,又包含索引區間。
* * 臨鍵鎖主要是為了避免幻讀。如果把事務的隔離級別降級為RC,臨鍵鎖則會失效。
**什么是快照讀和當前讀**
* 快照讀:簡單的select操作,屬于快照讀,不加鎖。(當然,也有例外,下面會分析)
* select \* from table where ?;
* 當前讀:特殊的讀操作,插入/更新/刪除操作,屬于當前讀,需要加鎖。
* select \* from table where ? lock in share mode;
* select \* from table where ? for update;
* insert into table values (…);
* update table set ? where ?;
* delete from table where ?;
## mysql的鎖了解么,如何分類?
可以從四個維度給鎖分類,分別如下:

## 樂觀鎖和悲觀鎖是什么,如何實現?
1、樂觀鎖:先修改,保存時判斷是夠被更新過,應用級別,說白了就是自己寫代碼實現,是一種思想,可以基于版本號、更新時間戳可以實現,
2、悲觀鎖:先獲取鎖,再操作修改,數據庫級別,比如sql后綴寫 for update,是基于MySQL自帶的功能。
## 鎖的粒度有哪幾種?
表級鎖:開銷小,加鎖快,粒度大,鎖沖突概率大,并發度低,適用于讀多寫少的情況。
頁級鎖:BDB存儲引擎
行級鎖:Innodb存儲引擎,默認選項
注意,innoDB中行級鎖是加在索引上的,因此只有命中索引的情況才會是行級鎖,不然是表級鎖,
## 鎖的兼容性對比
* S鎖:也叫做讀鎖、共享鎖,對應于 `select * from users where id =1 lock in share mode`
* X鎖:也叫做寫鎖、排它鎖、獨占鎖、互斥鎖,對應于 `select * from users where id =1 for update`
可參考:
https://zhuanlan.zhihu.com/p/31875702
https://blog.csdn.net/qq_38238296/article/details/88362999
- 導讀
- 簡歷和信心
- 一面(技術基礎)
- PHP
- MySQL-基礎
- MySQL-基礎2
- Nginx
- Redis
- 網絡
- 二面(技術進階)
- PHP
- MySQL
- Nginx
- Redis
- Linux
- 網絡
- 算法
- 操作系統
- 數據結構
- 網絡安全
- 分布式和微服務
- 線上故障處理經驗
- 架構
- 通用型業務的解決方案
- 高并發下如何保持一致性?
- 軟件測試的階段和方法有哪些
- 雪崩效應解決方案
- 兩個海量數據的同構表,如何查詢數據差異?
- 怎么設計一套通信接口的標準?
- 工作中有用到ES么
- 如何設計SKU表結構
- 如何設計RBAC表結構
- 如何設計防超賣的架構
- 如何設計高并發的架構
- 怎么理解SaaS,如何設計SaaS項目的架構
- 如何設計新浪關注feed流的架構
- 怎么設計短url服務
- 如何實現接口冪等性?
- 如何設計高可用的訂單業務架構
- 如何設計單點登錄的架構
- 如何應對大流量高并發?
- 團隊開發中,git分支管理怎么設定策略?
- 項目設計
- 如何設計秒殺架構?
- 有哪些方式生成唯一ID?
- 三面(技術終面)
- 工作素養
- 四面(hr & hrbp)
- 離職前要先找好下家嗎?
- 離職原因
- 談薪定級
- 個人道德、職業素養和服從性測試
- 反問環節
- 注意事項
- 擴展學習