概念說明:mysql
原子性 | 原子性是指事務包含的全部操做要麼所有成功,要麼所有失敗回滾 |
一致性 | 一致性是指事務必須使數據庫從一個一致性狀態變換到另外一個一致性狀態,也就是說一個事務執行以前和執行以後都必須處於一致性狀態sql 拿轉帳來講,假設用戶A和用戶B二者的錢加起來一共是5000,那麼無論A和B之間如何轉帳,轉幾回帳,事務結束後兩個用戶的錢相加起來應該還得是5000,這就是事務的一致性。數據庫 |
隔離性 | 隔離性是當多個用戶併發訪問數據庫時,好比操做同一張表時,數據庫爲每個用戶開啓的事務,不能被其餘事務的操做所幹擾,多個併發事務之間要相互隔離。 |
持久性 | 持久性是指一個事務一旦被提交了,那麼對數據庫中的數據的改變就是永久性的,即使是在數據庫系統遇到故障的狀況下也不會丟失提交事務的操做。 |
髒讀 | 事務B讀取到事務A尚爲提交的數據變動 |
不可重複讀 | 事務B先後兩次讀取一條記錄之間,該記錄被事務A修改並提交,致使事務B讀到了不同的結果 |
幻讀 | 事務B按條件匹配到n條記錄並修改,但因爲修改過程當中事務A新插入符合條件的記錄,致使事務B更新完成後仍發現有符合條件的數據未被更新。緩存 幻讀與不可重複讀在理解上差很少,區別是一個是修改數據一個是新增數據安全 |
數據庫不一樣的隔離級別對應不一樣的隔離現象服務器
隔離識別 | 髒讀 | 不可重複讀 | 幻讀 |
未提交讀(Read uncommitted)session |
可能 | 可能 | 可能 |
已提交讀(Read committed)架構 |
不可能 | 可能 | 可能 |
可重複讀(Repeatable read)併發 |
不可能 | 不可能 | 可能 |
可串行化(Serializable)oracle |
不可能 | 不可能 | 不可能 |
特別注意的是:
1.mysql的事務默認是自動提交的,即每條DML語句執行後都會持久化到磁盤
-- 自動提交參數是默認開啓的 -- 該參數可配置全局級別,也可配置session級別 mysql> SHOW GLOBAL VARIABLES LIKE '%autocommit%'; +---------------+-------+ | Variable_name | Value | +---------------+-------+ | autocommit | ON | +---------------+-------+ 1 row in set -- 全局級別 SET GLOBAL SESSION autocommit = 0; SHOW GLOBAL VARIABLES LIKE '%autocommit%'; -- session級別 SET SESSION autocommit = 0; SHOW VARIABLES LIKE '%autocommit%';
2.DLL語句是不能回滾的
-- 1. BEGIN; ALTER TABLE xxx.xxx ADD COLUMN `name` varchar(50) NULL ; ROLLBACK; -- 沒法回滾 -- 2. 值得注意的是: BEGIN; INSERT ...; UPDATE ...; ALTER TABLE xxx.xxx ADD COLUMN `name` varchar(50) NULL ; ROLLBACK;-- 受DLL語句影響,上面的增刪改DML語句一樣沒法回滾
InnoDB實現瞭如下兩種類型的行鎖。
如何加鎖呢?首先通常的select語句是不會加鎖,也不會被其餘事務鎖阻塞的。但執行insert/update/delete前數據庫會自動加鎖,具體以下
共享鎖 | 排他鎖 | |
自動加鎖 | insert前 | update或delete |
手動加鎖 | SELECT * FROM xxx LOCK IN SHARE MODE; | SELECT * FROM xxx FOR UPDATE; |
下面是這兩個鎖的兼容矩陣
當前鎖模式/是否兼容/請求鎖模式 | 排他鎖 | 共享鎖 |
排他鎖(x鎖) | × | × |
共享鎖(s鎖) | × | √ |
解讀:
實踐:
共享鎖例子
事務A | 事務B |
begin; |
|
-- 添加共享鎖 SELECT * FROM admin LOCK IN SHARE MODE; |
begin; |
... | -- 這裏能夠添加共享鎖,且能夠查詢到數據 SELECT * FROM admin LOCK IN SHARE MODE; -- 更新數據會被阻塞 update admin set xx=xxx; |
commit; |
|
|
--等待A提交或回滾後,B才能提交 commit; |
排他鎖例子
事務A | 事務B |
begin; |
|
-- 添加排他鎖 SELECT * FROM admin for UPDATE; |
begin; |
... | -- 這裏加共享或排他鎖都會被阻塞。 SELECT * FROM admin for UPDATE; -- 固然了,普通的select語句是能夠查到數據的,不會被阻塞 |
commit; |
|
|
--等待A提交或回滾後,B才能提交 commit; |
上面例子中,B事務會被A事務的事務鎖阻塞。等待n秒後會拋出異常。這個秒是能夠動態配置的
mysql> SHOW GLOBAL VARIABLES LIKE '%innodb_lock_wait_timeout%'; +--------------------------+-------+ | Variable_name | Value | +--------------------------+-------+ | innodb_lock_wait_timeout | 50 | +--------------------------+-------+ 1 row in set
當兩個連接互相等待對方釋放的鎖,就會形成死鎖,如圖
如何避免死鎖
mysql主要有如下存儲引擎,InnoDB會重點介紹。其餘都是不經常使用的
MyISAM
-- innodb相關參數 SHOW GLOBAL VARIABLES LIKE '%innodb%'
MYISAM儲存文件的結構是堆表,就是說合插入的順序是同樣的,與數據自己的邏輯沒有關係。
InnoDB存儲文件的結構是索引組織表(聚簇表),就是存放數據的順序是根據主鍵對應的邏輯儲存的。如圖
所以,
1.根據主鍵查詢數據的效率會更好
2.自增的主鍵的插入效率較好,而隨機主鍵插入的效率較低
3.InnoDB必須指定主鍵(設計的時候不聲明主鍵,Mysql會自動分配一個字符串類型的主鍵)。建議自增
在InnoDB中全部讀寫操做都必須通過緩存,如圖
用戶讀取某數據,並不是直接讀取磁盤裏面的數據,而是先查詢緩衝池裏面是否有該數據,沒有就去磁盤取,並儲存到緩存池中。寫操做也同樣。而緩存策略是LRU,也就是最近最少使用次數,不被常用的數據會優先替換出緩緩存池
緩存池調優:
-- 查看緩存池容量。假設服務器內存是16G且只開啓了一個mysql實例,那將innodb_buffer_pool_size設置成10G 是比較合理的 SHOW GLOBAL VARIABLES LIKE '%innodb_buffer_pool_size%';
剛剛講到,讀寫操做是必須通過緩存池的,而寫操做其實是對緩存池裏面的數據進行修改。
那mysql是怎麼樣把數據持久化到磁盤呢。
持久化步驟:
1.用戶發起修改數據,mysql首先會修改緩存池裏面的數據,並實時把該操做記錄在redo log文件中
2.而後直接告訴用戶數據修改爲功了,而後Mysql會慢慢的異步把redo log文件中的操做持久化到磁盤
這樣作的好處是把操做記錄 記錄在redo log文件效率是很高的。
可是這樣會有問題就是萬一數據還在redo log文件中,還沒持久化到磁盤的狀況下,mysql宕機。
那數據是否是就丟失了呢,答案是不會。重啓mysql的時候,mysql會自動處理的redo log裏面未處理的記錄
InnoDB持久化相關參數:
SHOW GLOBAL VARIABLES LIKE '%innodb_flush_log_at_trx_commit%'
這個參數有3種值
值 | 說明 | 安全性與性能 |
0 | 每隔1s寫入並持久化一第二天志 | 有可能丟失最多1s的事務修改記錄 |
1 | 每次commit都持久化日誌 | 全部數據都是安全的,一條都不會丟,頻繁寫操做會對影響新能 |
2 | 每次提交都持久化到系統的內容中,且每1s持久化一第二天志 | 這和0最大區別是,當mysql宕機而服務器主機沒有宕機,數據是不會丟的。可是主機宕機,如斷電,那效果和設爲0是同樣的 |