前言:#
本次將向大家科普一些資料庫事務相關知識。為什麼選擇資料庫作為本次介紹的主角?
資料庫是現代網絡的重要組成部分。在信息爆炸的大數據時代,各行各業都因為大幅爆發的數據而正變得蒸蒸日上;海量,多樣且在高速產生的數據正迸發著前所未有的價值。可以說,數據改變了我們的生活,也在創造我們的未來。因此,我們越來越需要更加靈活,功能強大的資料庫來幫助我們存儲數據,處理數據。隨著時代的發展,掌握資料庫操作相關技術已經是從事 ICT 行業的相關人員的必備選擇。
基礎概念#
在正式介紹事務之前,讓我們先充充電,大致了解一些基本概念,這些在之後的學習將會用到。
- 什麼是資料庫
"資料庫" 是以一定方式儲存在一起、能予多個用戶共享、具有盡可能小的冗余度、與應用程序彼此獨立的數據集合。
- 什麼是資料庫管理系統
資料庫管理系統(Data Base Management System,簡稱 DBMS)是位於用戶應用程序與操作系統之間的一層數據管理軟件,是資料庫系統的核心組成部分。為用戶或應用程序提供訪問資料庫的方法:包括資料庫的建立、查詢、更新以及各種數據控制。
資料庫管理系統不僅允許單個用戶查詢和修改數據,也可以支持多人操作。多人操作就會出現導致操作並發,出現擁塞。
- 什麼是鎖
鎖是資料庫伺服器用來控制數據資源被並行使用的一種機制。被鎖的對象只允許持有用戶操作,只有等鎖釋放後,其他用戶才能有擁有鎖的機會。
大部分資料庫使用下面兩種鎖策略:
- 寫操作需要申請寫鎖,讀操作申請讀鎖,一個表一次只能分配一個寫鎖,並且拒絕讀請求直到寫鎖釋放
- 寫操作需要寫鎖,讀操作不需要鎖
其中常見的資料庫如 MySQL 可以根據不同的儲存引擎選擇不同的鎖策略
- 鎖的粒度
即鎖的範圍,在資料庫中,伺服器可以在 3 個不同級別應用鎖
表鎖
阻止多用戶同時修改同一張表的數據
頁鎖
阻止多用戶同時修改某表中的一頁
行鎖
阻止多用戶同時修改某表中的某一行
- 什麼是儲存引擎
資料庫儲存引擎是資料庫底層軟件組織,資料庫管理系統 (DBMS) 使用數據引擎進行創建、查詢、更新和刪除數據,不同的儲存引擎提供不同的儲存機制、鎖定水平等功能。
鎖的行為是由儲存引擎決定的,儲存引擎使用不當會引發死鎖現象。
例如 MySQL 有三種儲存引擎:InnoDB、MyISAM、MEMORY
什麼是事務#
我們從一個場景開始說起。假設一個人在銀行辦理轉賬業務,現在他要將 1 萬元轉賬到另一個人,按一般情況,轉賬的過程幾乎是秒級完成,但是,當我們將過程拆解,轉賬步驟可分為 3 步:1. 查詢賬戶余額是否大於 1 萬元,2. 從原賬戶上減去 1 萬元,3. 在對方賬戶上增加 1 萬元。我們可以發現,其實只要其中任意一環節出現偏差,都有可能造成無法挽回的重大損失。因此,為了確保銀行裡金額不會損失,我們可以這麼做:將這一萬元暫時緩存下來,只有確認對方賬戶已完成扣除款項的操作,才將這一萬元增加到對方賬戶;否則,所有操作都失效,原賬戶取消扣除款項,對方賬戶也不會無故增加數量,我們把這種操作稱之為事務回滾,轉賬的操作就是一個事務。
事務的特性#
世界上總是充滿各種各樣的意外,很多都是我們無法左右的,例如伺服器的損壞,突然的斷電,系統的崩潰等等,如果沒有事務的存在,資料庫的操作可靠性將無法得到保證。但這些的前提是事務本身應當有完備的標準來確保事務本身的可靠性。因此,那些前輩們給事務指定了 ACID 四大特性,只有嚴格通過了 ACID 測試,事務才能發揮其作用。
-
原子性
一個事務必須被視為不可分割的最小工作單元,整個事務種的所有操作要麼全部提交成功,要麼全部失敗回滾,對於一個事務來說,不可能執行其中一部分操作。
-
一致性
資料庫總是從一個一致性的狀態轉移到另一個一致性的狀態,事務不能破壞資料庫的完整性以及業務邏輯的一致性。比如無論轉賬成功或失敗,都不可能會多出或減少 1 萬元,金額總數是不變的。
-
隔離性
一個事務在提交前的修改對其他事務通常是不可見的。一個事務不應影響其他事務的運行效果。
-
持久性
一旦事務提交,其修改是不可逆的。
事務的 ACID 特性保證了資料庫操作的安全性和可靠性,但實際操作總沒有想象中那麼簡單。同時,添加事務也需要資料庫系統進行更多額外的工作,這對資料庫系統的性能提出了一定的要求。
隔離級別#
實際上,要讓事務保證完全隔離依然是一件十分困難的事情 ,完全的隔離要求資料庫同一時間只能執行一個事務,這樣會嚴重影響性能。現實中,往往是多個事務並發執行。
一旦隔離性無法得到保證,資料庫的讀寫就會面臨如下情況
- 脏讀:事務讀取了未提交的數據。例如事務 A 讀取了事務 B 的更新的數據,但是事務 B 回滾了,導致 A 讀取的為脏數據
- 不可重複讀:事務 A 讀取同一數據兩次,但是在兩次之間事務 B 對該數據進行了修改並提交,導致事務 A 讀取兩次讀取不一致
- 幻讀:事務 A 修改全表的數據,在未提交時,事務 B 向表中插入或刪除數據,導致事務 A 讀取的數據與需要修改的數據不一致
對此,SQL 標準中指定了四種隔離級別:
-
未提交讀(READ UNCOMMITTED)
事務可以讀取為提交的數據,不做隔離控制
-
提交讀(READ COMMITTED)
不允許未提交讀,一個事務開始前,只能 “看見” 已提交的事務修改,是大部分資料庫的默認隔離級別(MySQL 除外)
-
可重複讀(REPEATED READ)
保證同一事務中多次讀取同一記錄的結果是一致的,一般方法是事務中對符合條件的記錄上排他鎖,這樣其他事務不能對該事務操作的數據進行修改,是 MySQL 的默認隔離級別
-
可串行化 (SERIALIZABLE)
是最高隔離級別,通過強制事務串行執行,在讀取的每一行數據上加鎖,導致其他事務不能對數據進行操作(包括增加、刪除和修改),但是此級別也要注意大量鎖的超時會極大地影響性能。
隔離級別 | 脏讀 | 不可重複讀 | 幻讀 |
---|---|---|---|
未提交讀(READ UNCOMMITTED) | 是 | 是 | 是 |
提交讀(READ COMMITTED) | 否 | 是 | 是 |
可重複讀(REPEATED READ) | 否 | 否 | 是 |
可串行化 (SERIALIZABLE) | 否 | 否 | 否 |
阻塞和死鎖#
談到並發就大致談一下阻塞的概念
當多個事務對某一資源進行鎖定時,其他沒有分配到鎖的事務勢必等待鎖的釋放,這就造成了阻塞。
當阻塞時間達到永久,就形成了死鎖。
在事務中,一旦兩個及以上的事務在同一資源上相互佔用,並請求鎖定對方佔用的資源,就會引發死鎖現象。
解決思路
- 查詢時間達到鎖的超時時間後放棄請求
- 使用較低的隔離級別,讓持有鎖的時間減短,減少鎖競爭
- 避免事務中用戶交互,同時盡量順序訪問對象
- …….
不同的儲存引擎對死鎖實現了不同的死鎖檢測和死鎖超時機制,因此大家在考慮死鎖的解決方案的時候一定要結合資料庫儲存引擎的實現方案,並且做好事務日誌,以便在問題發生時進行有效排查和高效解決。
事務的阻塞和死鎖的出現實際上是多個進程並發的必然結果,大家如果想了解更多關於有關並發和鎖,可以自行了解相關知識。
總結#
本篇筆者帶大家初識資料庫事務,初步了解了:
- 什麼是資料庫、什麼是鎖和事務;
- 事務的四個特性:原子性、隔離性、一致性、持久性;
- 資料庫讀寫出現的情況:脏讀、幻讀和不可重複讀;
- 事務的隔離級別;
- 死鎖的出現和解決思路
因為篇幅有限,這裡只能淺嘗輒止。資料庫是一門龐大複雜又極其重要的學科,如果大家有興趣,鼓勵大家自己鑿渠引水,因筆者知識有限,文章內容所述難免存在謬誤,懇請廣大讀者斧正。
知識鏈接#
資料庫學習《SQL 學習指南》,《高性能 MYSQL》