MySQL事務與存儲引擎
3.1-數據庫事務
事務的定義
一系列有序的數據庫操作。
- 要么全部成功
- 要么全部回退到操作前的狀態
- 中間狀態對其他連接不可見
事務的基本操作
基本操作 | 說明 |
---|---|
start transaction | 開始事務 |
commit | 提交(全部完成) |
rollback | 回滾(回到初始狀態) |
-- 開啟一個事務
start transaction;
-- 或者使用(非標準sql)
begin;
insert into t values (1, 1, 1);
-- 事務結束,插入成功
commit;
begin;
insert into t values (2, 1, 1);
insert into t values (3, 1, 1);
insert into t values (4, 1, 1);
-- 事務結束,沒有插入數據
rollback;
begin;
insert into t values (1, 1, 1);
savepoint a1;
insert into t values (2, 1, 1);
-- 回滾到指定的保存點
rollback to a1;
commit;
自動提交
- autocommit可以在session級別設置
- 每個DML操作都自動提交
- DDL永遠都是自動提交,無法通過rollback回滾
事務的四個基本屬性(ACID)
原子性(Atomicity)
一個事務包含多個操作,這些操作要么全部執行,要么全都不執行。實現事務的原子性,要支持回滾操作,在某個操作失敗后(數據庫或應用發生異常),回滾到事務執行之前的狀態。未提交的事務都應該被回滾。一致性(Consistency)
數據的正確性,合理性,完整性
數據一致性要符合應用應該符合的規則:余額不為負/交易對象必須先有賬號
/用戶賬號不重復事務的結果需要滿足數據的一致性約束
隔離性(Isolation)
并發的事務是相互隔離的。數據庫的某一事務在提交完成前,中間的任何數據變化對其他的事務都是不可見的。
即一個事務內部的操作及正在操作的數據必須封鎖起來,不被其它企圖進行修改的事務看到(假如并發交叉執行的多個事務任意操縱相同的共享對象,可能引起異常)持久性(Durability)
發生故障時,確保已提交事務的更新不能丟失。
一旦事務提交完成,對數據庫中數據的影響必須是永久性的(數據成功寫入磁盤 即 持久化成功)。
實現 事務的持久化
- 數據文件持久化
- 隨機同步刷新(慢)
- 事務日志持久化與實例恢復
- 順序同步刷新(快) -> 事務日志
- 隨機異步刷新 -> 磁盤
- 事務日志 -> 磁盤(實例恢復)
數據庫隔離現象
隔離現象 | 描述 |
---|---|
臟讀(Dirty Read) | 事務B讀到事務A尚未提交的數據變更 |
不可重復讀(NonRepeatable Read) | 事務B讀取前后兩次讀取一條記錄之間該記錄被事務A修改并提交,于是事務B讀到了不一樣的結果 |
幻讀(Phantom Read) | 事務B按條件匹配到了若干行記錄并修改。但是由于修改過程中事務A新插入了符合條件記錄,導致B更新完成后發現仍有符合條件卻未被更新的記錄。 |
數據庫隔離等級
隔離等級 | 臟讀 | 不可重復讀 | 幻讀 |
---|---|---|---|
未提交讀(Read Uncommitted) | 可能 | 可能 | 可能 |
已提交讀(Read Committed) | 不可能 | 可能 | 可能 |
可重復讀(Repeated Read) | 不可能 | 不可能 | 可能 |
可串行化讀(Serialization) | 不可能 | 不可能 | 不可能 |
只有在事務提交后,其更新結果才會被其他事務看見。
:在一個事務中,對于同一份數據的讀取結果總是相同的,無論是否有其他事務對這份數據進行操作
MySQL的事務隔離級別
- InnoDB默認標記為可重復讀
- InnoDB并不是標準定義上的課重復讀
- InnoDB默認在可重復讀的基礎上避免幻讀
MySQL事務隔離級別設置
- 可在global/session/下個事務,級別分別進行設置
- 建議使用Read committed(同Oracle)
- 或者建議使用默認的Repeatable read
set tx_isolation = ''
-- 設置隔離級別
事務與并發寫
- 某個正在更新的記錄再提交或回滾前不能被其他事務同時更新
事務回滾的實現
- 回滾段(rollback segment)與數據前像
3.2-存儲引擎概述
MySQL程序層次架構
./sorence/images/01.jpg
MySQL存儲引擎
- 有多種可選方案,可插拔,可修改存儲引擎
- 基于表選擇使用何種存儲引擎
主要存儲引擎
存儲引擎 | 常用度 | 支持事務 |
---|---|---|
InnoDB | 主要,推薦 | 是 |
MyISAM | 古老,偶爾有用,系統表 | 否 |
MEMORY | 偶爾臨時表有用,純內存 | 否 |
BLACKHOLE | 不用來存放數據,個別特殊用處 | 否 |
TokuDB | 新穎,個別特殊場景有奇效 | 是 |
Cluster | 新穎,分布式,內存,線上不要用 | 是 |
InnoDB存儲引擎
- 索引組織表
- 支持事務
- 支持行級鎖
- 數據塊緩存
- 日志持久化
- 穩定可靠,性能好,線上盡量使用InnoDB
MyISAM存儲引擎
- 堆表
- 不支持事務
- 只維護索引緩存池,表數據緩存交給操作系統
- 鎖粒度較大
- 數據文件可以直接拷貝,偶爾可能會用上
- 不建議線上業務數據使用
MWMORY存儲引擎
- 數據全內存存放,無法持久化
- 性能較高
- 不支持事務
- 適合偶爾作為臨時表使用
create temporary table tmp (id int) engine = memory ;
BLACKHOLE存儲引擎
- 數據不作任何存儲
- 利用MySQL Replicate,充當日志服務器
- 在MySQL Replicate環境中充當代理主
TokuDB
- 分形樹存儲結構
- 支持事務
- 行鎖
- 壓縮效率較高
- 適合大批量insert的場景
MySQL Cluster
- 多主分布式集群
- 數據節點間冗余,高可用
- 支持事務
- 設計上易于擴展
- 面向未來,線上慎用
改變表的存儲引擎
alter table m ENGINE=innodb;
3.3-InnoDB存儲引擎
InnoDB存儲引擎體系架構
/sorence/images/02.png
InnoDB相關的磁盤文件
文件 | 名稱 | 數量 | 位置 |
---|---|---|---|
系統表空間 | ibdata1 | 一個實例一個 | innodb_data_home_dir |
日志文件 | ib_logfile0/1 | 一個實例兩個(可配置) | innodb_log_group_home_dir |
表定義文件 | 表名.frm | 每張表一個 | Schema目錄下 |
表數據文件 | 表名.ibd | 如果innodb_file_per_table = 1, 則每張表一個 | Schema目錄下 |
InnoDB系統表空間文件
- ibdata1里存放了什么:
- 回滾段
- 所有InnoDB表元數據信息
- Double Write, Insert buffer dump等等....
- 自動擴展機制
InnoDB與磁盤文件有關的參數
參數 | 樣例值 | 備注 |
---|---|---|
innodb_data_home_dir | /data/mysql/node_1 | 數據主目錄 |
innodb_log_group_home_dir | /data/mysql/node_1 | 一般同上 |
innodb_data_file_path | ibdata1:512M:autoextned | 請開啟autoextned |
innodb_autoextend_increment | 128 | MB,勿太大或太小 |
innodb_file_per_table | 1 | 強烈建議開啟 |
innodb_log_file_size | 100MB | 性能相關 |
innodb_log_files_in_group | 2 | 性能相關 |
InnoDB數據文件存儲結構
- 索引組織表(聚簇表)
- 根據表邏輯主鍵排序
- 數據節點每頁16K
- 根據主鍵尋址速度很快
- 主鍵值遞增的insert插入效率較好
- 主鍵值隨機insert插入效率差
- 因此,InnoDB表必須指定主鍵,建議使用自增數字
InnoDB數據塊緩存池
- 數據的讀寫需要經過緩存
- 數據以整頁(16K)為單位讀取到緩存中
- 緩存中的數據以LRU策略換出
- IO效率高,性能好
InnoDB Buffer Pool相關參數
參數 | 樣例值 | 備注 |
---|---|---|
innodb_buffer_pool_size | 10G | 根據總物理內存設置 |
InnoDB數據持久化與事務日志
- 事務日志實時持久化
- 內存變化數據(臟數據)增量異步刷出到磁盤
- 實例故障靠重放日志恢復
- 性能好,可靠,恢復快
InnoDB日志持久化相關參數
參數 | 樣例值 | 備注 |
---|---|---|
innodb_flush_log_at_trx_commit | 1 | 可選:0:每隔1s寫入并持久化一次日志。1:每次commit都寫入并持久化日志。2:每次提交日志寫到內存,每1s持久化一次 |
InnoDB行級鎖
- 寫不阻塞讀
- 不同行間的寫互相不阻塞
- 并發性能好
InnoDB與事務ACID
- 事務ACID特性完整支持
- 回滾段失敗回滾
- 支持主外鍵約束
- 事務版本+回滾段=MVCC
- 事務日志持久化
- 默認可重復讀隔離級別,可以調整
3.4-InnoDB事務鎖
什么是計算機程序鎖
- 計算機程序鎖
- 控制對共享資源進行并發訪問
- 保護數據的完整性和一致性
數據庫中的鎖
- 分為兩個大類
lock | latch/mutex | |
---|---|---|
對象 | 事務 | 線程 |
保護 | 數據庫邏輯內容 | 內存數據結構 |
持續時間 | 事務過程中 | 臨界資源爭搶 |
- 我們主要關心的是事務鎖
數據庫事務并發
- 對同一行記錄的修改必須串行化
事務鎖粒度
- 行鎖
- InnoDB, Oracle
- 頁鎖
- SQL Server
- 表鎖
- MyISAM, Memory
- 鎖升級
InnoDB存儲引擎中的鎖模式與粒度
- 四種基本鎖模式
- 共享鎖(S) - 讀鎖 - 行鎖
- 排他鎖(X) - 寫鎖 - 行鎖
- 意向共享鎖(IS) - 表級
- 意向排他鎖(IX) - 表級
- 意向鎖
- 意向鎖總是自動先加,并且意向鎖自動加自動釋放
- 意向鎖提示數據庫這個session將要在接下來施加何種鎖
- 意向鎖和X/S鎖級別不同,除了阻塞全表級別的X/S鎖外其他任何鎖
InnoDB鎖模式互斥
/sorence/images/03.png
數據庫加鎖操作
- 一般的select語句不加任何鎖,也不會被任何事物鎖阻塞
- 讀的隔離性由MVCC確保
- S鎖
- 手動:
select * from tb_test lock in share mode;
- 自動:insert前
- 手動:
- X鎖
- 手動:
select * from tb_test lock for update;
- 自動:update,delete前
- 手動:
InnoDB行鎖的實現
- 通過索引項加鎖實現
- 只有條件走索引才能實現行級鎖
- 索引上有重復值,可能鎖住多個記錄
- 查詢有多個索引可以走,可以對不同索引加鎖
- 是否對索引加鎖實際上取決于MySQL執行計劃
- 自增主鍵做條件更新,性能最好
沒有索引的話會對整張表加鎖。
InnoDB的gap lock
- 什么是幻讀
- gap lock消滅幻讀
- InnoDB消滅幻讀僅僅為了確保statement模式replicate的主從一致性
- 小心gap lock
- 自增主鍵做條件更新,性能最好
死鎖
-
什么是死鎖
- A、B兩個事務,A先更新t1,同時B更新t2,A再更新t2,B再更新t1就發生了死鎖。
-
死鎖數據庫自動解決
- 數據庫挑選沖突事務中回滾代價較小的事務回滾
-
死鎖預防
- 單表死鎖可以根據批量更新里的更新條件排序
- 可能沖突的跨表事務盡量避免并發
- 盡量縮短事務長度
業務邏輯加鎖
-
業務流程中的悲觀鎖
- 任何的并發修改都有可能造成我們的業務邏輯最終的錯誤,在事務流程中一開始就加鎖,最后釋放
如何縮短鎖的時間