事務(wù)特性
事務(wù)特性分為:
原子性 每一個事務(wù)都是不可分割的整體,要么全部成功要么全部失敗;
一致性 事務(wù)執(zhí)行后的結(jié)果是和預(yù)設(shè)的規(guī)則完全符合的,不會因為出現(xiàn)系統(tǒng)意外等原因和預(yù)測的結(jié)果不一致;
隔離性 事務(wù)與事務(wù)之間是相互獨立的,互不影響;也是在事務(wù)并發(fā)時實現(xiàn)一致性的一個前提,可以設(shè)置4種隔離級別。級別越高一致性越強,但并發(fā)性越低;
1.讀未提交 會讀到其他事務(wù)未提交的數(shù)據(jù),產(chǎn)生臟讀
2.讀已提交 解決臟讀,但在同一事務(wù)中多次讀取單行數(shù)據(jù)會得到不同結(jié)果,即不可重復(fù)讀的問題
3.可重復(fù)讀 解決不可重復(fù)讀,但會在多次范圍查詢時,得到數(shù)量不同的結(jié)果,即幻讀。在innodb引擎中,不會存在此問題,實現(xiàn)原理是臨鍵鎖。
4.可串行化 解決幻讀
持久性 事務(wù)一旦提交就會永久保存,不會因為系統(tǒng)意外原因而丟失
innodb事務(wù)的這些特性如何實現(xiàn)?鎖、MVCC
鎖
innodb一共有5種鎖:共享鎖(行)、排它鎖(行)、意向共享鎖(表)、意向排它鎖(表)、自增鎖。行鎖實現(xiàn)有三種算法,又稱為:臨鍵鎖next-key、間隙所gap和記錄鎖record。默認(rèn)隔離級別不可重復(fù)讀使用臨鍵鎖作為行鎖的實現(xiàn)算法。
InnoDB的行鎖是通過給索引上的索引項加鎖來實現(xiàn)的,只有通過索引條件進行數(shù)據(jù)檢索,InnoDB才使用行級鎖,否則InnoDB 將使用表鎖(鎖住全部索引)。
鎖的類型
1.共享鎖:也稱為讀鎖,可以讓多個事務(wù)同時對數(shù)據(jù)進行操作時可以訪問到數(shù)據(jù),但不能修改。使用方式:
select語句+LOCK IN SHARE MOD
2.排它鎖:又稱為寫鎖。一個事務(wù)持有了一行數(shù)據(jù)的排它鎖時,其他事務(wù)不能再訪問和修改這行數(shù)據(jù)。innodb默認(rèn)update delete insert上會加排它鎖,select使用方式:
select語句+for update
3.意向共享鎖:在事務(wù)對某一行加共享鎖時,要先給該表加上意向共享鎖。
4.意向排它鎖:在事務(wù)對某一行數(shù)據(jù)加排它鎖時,必須要先給該表加上意向排它鎖。
作用是,當(dāng)某一個事務(wù)需要去鎖表時,不用判斷每一行上是否有不兼容的鎖,只需要判斷有沒有意向鎖。如寫操作鎖表,判斷某行數(shù)據(jù)是否存在共享鎖,如果拿不到意向鎖就可直接阻塞操作。
5.自增鎖:針對自增列自增長的一個特殊的表級別鎖。
鎖的算法
1.臨鍵鎖next-key:當(dāng)sql語句按照范圍查詢,并且有數(shù)據(jù)命中的時候會給索引加上臨鍵鎖,鎖住命中索引項的前一個索引到命中索引項的后一個索引之間的一個左開右閉區(qū)間。因為鎖住區(qū)間,所以避免了幻讀。
2.間隙鎖gap:當(dāng)sql語句查找未命中時,會鎖住查詢條件值附近的間隙。
3.記錄鎖record:使用唯一索引查詢并精準(zhǔn)匹配到數(shù)據(jù),則只會鎖住該索引項。
這三種算法鎖的原理基于b+tree索引。
死鎖
事務(wù)并發(fā)時,每個事務(wù)都持有鎖(或者是已經(jīng)在等待鎖),每個事務(wù)都需要再繼續(xù)持有鎖,然后事務(wù)之間產(chǎn)生加鎖的循環(huán)等待,形成死鎖。避免死鎖:
1)類似的業(yè)務(wù)邏輯以固定的順序訪問表和行。
2)大事務(wù)拆小。大事務(wù)更傾向于死鎖,如果業(yè)務(wù)允許,將大事務(wù)拆小。
3)在同一個事務(wù)中,盡可能做到一次鎖定所需要的所有資源,減少死鎖概 率。
4)降低隔離級別,如果業(yè)務(wù)允許,將隔離級別調(diào)低也是較好的選擇 。
5)為表添加合理的索引。可以看到如果不走索引將會為表的每一行記錄添 加上鎖(或者說是表鎖)
MVCC多版本并發(fā)控制
并發(fā)訪問(讀或?qū)?數(shù)據(jù)庫時,對正在事務(wù)內(nèi)處理的數(shù)據(jù)做 多版本的管理。以達(dá)到用來避免寫操作的堵塞,從而引發(fā)讀操 作的并發(fā)問題。
新增:會給行數(shù)據(jù)添加兩個隱藏列,數(shù)據(jù)版本號和刪除版本號。數(shù)據(jù)版本號值為插入時的事務(wù)id,刪除版本號默認(rèn)為null。
刪除:會給行數(shù)據(jù)的刪除版本號設(shè)一個當(dāng)前事務(wù)id值。
修改:會先拷貝一份行數(shù)據(jù),再把原先行數(shù)據(jù)的刪除版本號設(shè)值,再修改拷貝的數(shù)據(jù),并改變數(shù)據(jù)版本號值。
查詢:必須保證當(dāng)前事務(wù)ID大于等于該行數(shù)據(jù)的數(shù)據(jù)版本號,并且刪除版本號必須為null或者大于當(dāng)前事務(wù)ID值。
undo log
undo log作用是保證了事務(wù)的原子性和普通select的快照讀。當(dāng)事務(wù)開啟的時候會拷貝當(dāng)前數(shù)據(jù)到undo log中,此時有其他事務(wù)中的select讀取數(shù)據(jù)直接從undo log中獲取,若事務(wù)回滾可根據(jù)undo log恢復(fù)原始數(shù)據(jù)。
redo log
redo log保證了事務(wù)的持久性。事務(wù)開啟后,只要開始改變數(shù)據(jù)信息就會持續(xù)寫入redo buffer中,具體落盤可以指定不同的策略。在數(shù)據(jù)庫發(fā)生意外故障時,尚有修改的數(shù)據(jù)未寫入磁盤,在重啟mysql服務(wù)的時候,根據(jù)redo log恢復(fù)事務(wù)修改后的新數(shù)據(jù)。
Redo buffer 持久化到Redo log的策略有三種:
取值 0 每秒一次進行提交持久化[可能丟失一秒內(nèi) 的事務(wù)數(shù)據(jù)]
取值 1 默認(rèn)值,每次事務(wù)提交執(zhí)行Redo buffer --> Redo log OS cache -->flush cache to disk [最安全,性能最差的方式]
取值 2 每次事務(wù)提交到系統(tǒng)緩存OS cache,再每一秒從系統(tǒng)緩存中執(zhí)行持久化 操作
mysql配置優(yōu)化
最大連接數(shù)配置 max_connections
connection內(nèi)存參數(shù)配置:
sort_buffer_size connection排序緩沖區(qū)大小 建議256K(默認(rèn)值)-> 2M之內(nèi) 當(dāng)查詢語句中有需要文件排序功能時,馬上為connection分配配置的內(nèi)存大小。
join_buffer_size connection關(guān)聯(lián)查詢緩沖區(qū)大小 建議256K(默認(rèn)值)-> 1M之內(nèi) 當(dāng)查詢語句中有關(guān)聯(lián)查詢時,馬上分配配置大小的內(nèi)存用這個關(guān)聯(lián)查 詢,所以有可能在一個查詢語句中會分配很多個關(guān)聯(lián)查詢緩沖區(qū)。
上述配置4000連接占用內(nèi)存: 4000*(0.256M+0.256M) = 2G
Innodb_buffer_pool_size緩沖池大小配置
innodb buffer/cache的大小(默認(rèn)128M)
Innodb_buffer_pool緩沖池包含:數(shù)據(jù)緩存 索引緩存 緩沖數(shù)據(jù) 內(nèi)部結(jié)構(gòu)
大的緩沖池可以減小多次磁盤I/O訪問相同的表數(shù)據(jù)以提高性能
參考計算公式: Innodb_buffer_pool_size = (總物理內(nèi)存 - 系統(tǒng)運行所用 - connection 所用)* 90%
其他參數(shù)配置
wait_timeout 服務(wù)器關(guān)閉非交互連接之前等待活動的秒數(shù)
innodb_open_files 限制Innodb能打開的表的個數(shù)
innodb_write_io_threads innodb_read_io_threads innodb:使用后臺線程處理innodb緩沖區(qū)數(shù)據(jù)頁上的讀寫 I/O(輸入輸出)請求的數(shù)量
innodb_lock_wait_timeout InnoDB事務(wù)在被回滾之前可以等待一個鎖定的超時秒數(shù)