[TOC]
## 數據庫鎖
SQLite的數據庫連接有5種狀態:
| 狀態 | 對應的鎖 |
| --- | --- |
| 未加鎖 | — |
| 共享(shared) | 共享鎖? |
| 預留(reserved) | 預留鎖 |
| 未決(pending) | 未決鎖 |
| 排它(exclusive) | 排它鎖 |
SQL使用鎖逐步提升機制,上面的表格從上到下,對應鎖的等級逐步提升,等級越高權限就越大。
**未加鎖:**
未和數據庫建立連接、已建立連接但是還沒訪問數據庫、已用BEGIN開始了一個事務但未開始讀寫數據庫,處于這些情形時是未加鎖狀態。
**共享:**
連接需要從數據庫中讀取數據時,需要申請獲得一個共享鎖,如果獲得成功,則進入共享狀態。
**預留:**
連接需要寫數據庫時,首先申請一個預留鎖,一個數據庫同時只能有一個預留鎖,預留鎖可以與共享鎖共存。獲得預留鎖后進入預留狀態,這時會先在緩沖區中進行需要的修改、更新操作,操作后的結果依然保存在緩沖區中,未真正寫入數據庫。
**未決:**
連接從預留升為排它前,需要先升為未決,這時其它連接就不能獲得共享鎖了,但已經擁有共享鎖的連接仍然可以繼續正常讀數據庫,此時,**擁有未決鎖的連接等待其它擁有共享鎖的連接完成工作并釋放其共享鎖后,提成到排它鎖。**
**排它:**
連接需要提交修改時,需要將預留鎖升為排它鎖,這時其它連接都無法獲得任何鎖,直到當前連接的排它狀態結束。
## 讀寫鎖
當執行[select](https://so.csdn.net/so/search?q=select&spm=1001.2101.3001.7020)即讀操作時,需要獲取到SHARED鎖(共享鎖),
當執行[insert](https://so.csdn.net/so/search?q=insert&spm=1001.2101.3001.7020)/update/delete操作(即內存寫操作時),需要進一步獲取到RESERVERD鎖(保留鎖),
當進行commit操作(即磁盤寫操作時),需要進一步獲取到EXCLUSIVE鎖(排它鎖)。
* 對于RESERVERD鎖,sqlite3保證同一時間只有一個連接可以獲取到保留鎖,也就是同一時間只有一個連接可以寫數據庫(內存),但**是其它連接仍然可以獲取SHARED鎖**,也就是其它連接仍然可以進行讀操作(這里可以認為寫操作只是對磁盤數據的一份內存拷貝進行修改,并不影響讀操作)。
*
* 對于EXCLUSIVE鎖,是比保留鎖更為嚴格的一種鎖,在需要把修改寫入磁盤即commit時需要在保留鎖/未決鎖的基礎上進一步獲取到排他鎖,顧名思義,排他鎖排斥任何其它類型的鎖,即使是SHARED鎖也不行,所以,在一個連接進行commit時,[其它連接是不能做任何操作的]()(包括讀)。
*
* PENDING鎖(即未決鎖),則是比較特殊的一種鎖,它可以允許已獲取到SHARED鎖的事務繼續進行,但不允許其它連接再獲取SHARED鎖,當已存在的SHARED鎖都被釋放后(事務執行完成),持有未決鎖的事務就可以獲得commit的機會了。sqlite3使用這種鎖來防止writer starvation(寫餓死)。
## 事務死鎖

死鎖的情況
死鎖的情況:當兩個連接使用begin transaction開始事務時,
第一個連接執行了一次select操作(已經獲取到SHARED鎖),
第二個連接執行了一次insert操作(已經獲取到了RESERVERD鎖),
此時第一個連接需要進行一次insert/[update](https://so.csdn.net/so/search?q=update&spm=1001.2101.3001.7020)/delete(需要獲取到RESERVERD鎖),
第二個連接則希望執行commit(需要獲取到EXCLUSIVE鎖),由于第二個連接已經獲取到了RESERVERD鎖,根據RESERVERD鎖同一時間只有一個連接可以獲取的特性,第一個連接獲取RESERVERD鎖的操作必定失敗,
而由于第一個連接已經獲取到SHARED鎖,第二個連接希望進一步獲取到EXCLUSIVE鎖的操作也必定失敗。就導致了事務死鎖。
事務類型的使用原則
在用”begin transaction”顯式開啟一個事務時,默認的事務類型為DEFERRED,鎖的狀態為UNLOCKED,即不獲取任何鎖,如果在使用的數據庫沒有其它的連接,用begin就可以了。如果有多個連接都需要對數據庫進行寫操作,那就得使用BEGIN IMMEDIATE/EXCLUSIVE開始事務了。
使用事務的好處是:
1.一個事務的所有操作相當于一次原子操作,如果其中某一步失敗,可以通過回滾來撤銷之前所有的操作,只有當所有操作都成功時,才進行commit,保證了操作的原子特性;
2.對于多次的數據庫操作,如果我們希望提高數據查詢或更新的速度,可以在開始操作前顯式開啟一個事務,在執行完所有操作后,再通過一次commit來提交所有的修改或結束事務。
# 參考資料
[Sqlite3并發讀寫注意事項](https://blog.csdn.net/hqfok/article/details/75020563)
[SQLite 鎖機制與事務簡介](https://www.jianshu.com/p/8b0d70191b87)
- Android
- 四大組件
- Activity
- Fragment
- Service
- 序列化
- Handler
- Hander介紹
- MessageQueue詳細
- 啟動流程
- 系統啟動流程
- 應用啟動流程
- Activity啟動流程
- View
- view繪制
- view事件傳遞
- choreographer
- LayoutInflater
- UI渲染概念
- Binder
- Binder原理
- Binder最大數據
- Binder小結
- Android組件
- ListView原理
- RecyclerView原理
- SharePreferences
- AsyncTask
- Sqlite
- SQLCipher加密
- 遷移與修復
- Sqlite內核
- Sqlite優化v2
- sqlite索引
- sqlite之wal
- sqlite之鎖機制
- 網絡
- 基礎
- TCP
- HTTP
- HTTP1.1
- HTTP2.0
- HTTPS
- HTTP3.0
- HTTP進化圖
- HTTP小結
- 實踐
- 網絡優化
- Json
- ProtoBuffer
- 斷點續傳
- 性能
- 卡頓
- 卡頓監控
- ANR
- ANR監控
- 內存
- 內存問題與優化
- 圖片內存優化
- 線下內存監控
- 線上內存監控
- 啟動優化
- 死鎖監控
- 崩潰監控
- 包體積優化
- UI渲染優化
- UI常規優化
- I/O監控
- 電量監控
- 第三方框架
- 網絡框架
- Volley
- Okhttp
- 網絡框架n問
- OkHttp原理N問
- 設計模式
- EventBus
- Rxjava
- 圖片
- ImageWoker
- Gilde的優化
- APT
- 依賴注入
- APT
- ARouter
- ButterKnife
- MMKV
- Jetpack
- 協程
- MVI
- Startup
- DataBinder
- 黑科技
- hook
- 運行期Java-hook技術
- 編譯期hook
- ASM
- Transform增量編譯
- 運行期Native-hook技術
- 熱修復
- 插件化
- AAB
- Shadow
- 虛擬機
- 其他
- UI自動化
- JavaParser
- Android Line
- 編譯
- 疑難雜癥
- Android11滑動異常
- 方案
- 工業化
- 模塊化
- 隱私合規
- 動態化
- 項目管理
- 業務啟動優化
- 業務架構設計
- 性能優化case
- 性能優化-排查思路
- 性能優化-現有方案
- 登錄
- 搜索
- C++
- NDK入門
- 跨平臺
- H5
- Flutter
- Flutter 性能優化
- 數據跨平臺