數(shù)據(jù)(data)
? ? 數(shù)據(jù)是人類世界非常重要的存在。世界上的一切事物是一部分?jǐn)?shù)據(jù)的集合,那么理論上我們就可以創(chuàng)造出一切事物,前提是獲得相應(yīng)的數(shù)據(jù)并建立起一個(gè)模型(對(duì)事物的描述)。以印刷術(shù)為例,每一個(gè)字的形狀都是一個(gè)數(shù)據(jù),人們以對(duì)這些數(shù)據(jù)的認(rèn)知對(duì)每一個(gè)字都建立了一個(gè)模型,然后可以通過(guò)這些模型就可以印出相應(yīng)的字符。進(jìn)入電子信息時(shí)代,數(shù)據(jù)得到了更廣泛,更深度的使用。例如在醫(yī)學(xué),航天,機(jī)器人(人工智能)。
? ? 越復(fù)雜的事物我們?cè)诫y描述(建立模型),因?yàn)樵綇?fù)雜的事物包含的數(shù)據(jù)元素就越龐大,此時(shí)我們需要將收集到的數(shù)據(jù)存儲(chǔ)起來(lái)進(jìn)行分類管理,需要時(shí)讀取,這時(shí)就引入了數(shù)據(jù)庫(kù)。
數(shù)據(jù)庫(kù)DB(database)
? ? 數(shù)據(jù)庫(kù)從很早就開始存在,例如古時(shí)候用于記賬的賬簿,記錄個(gè)人信息的檔案庫(kù),藏在某處的武林秘籍等等。到了電子信息時(shí)代,已經(jīng)可以實(shí)現(xiàn)將數(shù)據(jù)存儲(chǔ)在計(jì)算機(jī)的磁盤上了。這里定義計(jì)算機(jī)磁盤空間中的長(zhǎng)久存儲(chǔ)的大量數(shù)據(jù)的集合就是一個(gè)數(shù)據(jù)庫(kù)(database),使用磁盤作為數(shù)據(jù)庫(kù)有數(shù)據(jù)永久性保存,查詢管理方便等優(yōu)點(diǎn)。
數(shù)據(jù)庫(kù)系統(tǒng)DBS(database system)
? ? 數(shù)據(jù)庫(kù)系統(tǒng)一般由數(shù)據(jù)庫(kù)、數(shù)據(jù)庫(kù)管理系統(tǒng)(及其開發(fā)工具)、應(yīng)用系統(tǒng)、數(shù)據(jù)庫(kù)管理員構(gòu)成。該系統(tǒng)維護(hù)數(shù)據(jù)庫(kù)的正常生命周期。
數(shù)據(jù)庫(kù)管理系統(tǒng)DBMS(database manager system)
? ? 數(shù)據(jù)庫(kù)管理系統(tǒng)是位于用戶與操作系統(tǒng)之間的一層數(shù)據(jù)管理軟件。這個(gè)系統(tǒng)方便用戶操作,維護(hù)數(shù)據(jù)庫(kù)。常用的DBMS有:MySQL、Oracle、DB2、SQLServer等。本文要討論的是MySQL。
? ?
? ? 當(dāng)我們使用計(jì)算機(jī)磁盤作為數(shù)據(jù)庫(kù)存儲(chǔ)數(shù)據(jù),使用相應(yīng)的DBMS進(jìn)行訪問操作數(shù)據(jù)之后,單用戶訪問操作數(shù)據(jù)庫(kù)是沒有問題的,但是單用戶訪問的效率實(shí)在太低,此時(shí)我們想實(shí)現(xiàn)多用戶并發(fā)訪問。但是此時(shí)就會(huì)出現(xiàn)問題:
? ? 1.用戶1讀取了某數(shù)據(jù),準(zhǔn)備使用。
? ? 2.用戶2在之后修改了該數(shù)據(jù)。
? ? 3.用戶1在使用之前忘記了自己讀的數(shù)據(jù),再次讀取。
那么我們希望能夠解決這些問題來(lái)實(shí)現(xiàn)多用戶并發(fā)訪問數(shù)據(jù)庫(kù)。此時(shí)我們對(duì)DBMS的設(shè)計(jì)引入以下規(guī)則:
? ?
? ? 事務(wù)(Transaction):完成某個(gè)需求需要做的一連串的操作稱為一個(gè)事務(wù),事務(wù)是DBMS的執(zhí)行單元。
? ? 事務(wù)具有以下特性:
? ? ? ? 原子性:事務(wù)是不可分割的,完成事務(wù)的步驟要么全部不做,要么全部做完。(rollback&commit)
? ? ? ? 一致性:事務(wù)執(zhí)行過(guò)程中操作的數(shù)據(jù)必須是一致的。(不同需求下對(duì)于一致性的要求不同)
? ? ? ? 隔離性:多個(gè)事務(wù)并發(fā)執(zhí)行的時(shí)候,多個(gè)事務(wù)之間操作的數(shù)據(jù)互不影響。(隔離性實(shí)現(xiàn)一致性)
? ? ? ? 持久性:事務(wù)一旦提交,數(shù)據(jù)就永久存儲(chǔ)在數(shù)據(jù)庫(kù)中,即使是產(chǎn)生故障也不能影響。(recovery)
? ?
? ? 由于不同需求下對(duì)于并發(fā)訪問的產(chǎn)生的結(jié)果要求不同,提出了以下四大隔離級(jí)別:
? ? ? ? read uncommitted:讀未提交
? ? ? ? ? ? 此隔離級(jí)別下,事務(wù)會(huì)讀到未提交事務(wù)的數(shù)據(jù)。
? ? ? ? read committed:讀已提交
? ? ? ? ? ? 此隔離級(jí)別下,事務(wù)會(huì)讀到已經(jīng)提交事務(wù)的數(shù)據(jù)。
? ? ? ? repeatable read:可重復(fù)讀
? ? ? ? ? ? 此隔離級(jí)別下,其他事務(wù)對(duì)于數(shù)據(jù)的修改不會(huì)影響到本事務(wù)讀數(shù)據(jù)。
? ? ? ? serializable:可串行化
? ? ? ? ? ? 此隔離級(jí)別下,所有事務(wù)串行化(排隊(duì))執(zhí)行。
? ? ? ?
? 并發(fā)訪問問題匯總:
? ? 1.修改丟失:
? ? ? ? 1)事務(wù)1讀取了數(shù)據(jù)對(duì)象A=3,保存下來(lái)賦值為B1。
? ? ? ? 2)事務(wù)2讀取了數(shù)據(jù)對(duì)象A=3,保存下來(lái)賦值為B2。
? ? ? ? 3)事務(wù)1修改了數(shù)據(jù)對(duì)象A=B1+1=4。
? ? ? ? 4)事務(wù)2修改了數(shù)據(jù)對(duì)象A=B2+1=4。
? ? ? ? 場(chǎng)景:多窗口賣票。
? ? ? ? 窗口1,2同時(shí)知道已經(jīng)賣了3張票,然后在同一時(shí)間窗口1賣出了一張票,窗口2也賣出了一張票,本應(yīng)該賣出了5張,結(jié)果只賣了4張。
? ? 2.臟讀:事務(wù)2修改數(shù)據(jù)未提交,事務(wù)1讀取了數(shù)據(jù)。
? ? ? ? 1)事務(wù)2修改了數(shù)據(jù)A=3,操作未完成...
? ? ? ? 2)事務(wù)1讀取到了數(shù)據(jù)對(duì)象A=3。
? ? ? ? 3)事務(wù)2修改了數(shù)據(jù)A=2,操作完成。
? ? ? ? 場(chǎng)景:銀行轉(zhuǎn)賬
? ? ? ? X向Y轉(zhuǎn)賬,銀行中X賬戶減錢,Y還沒加錢,X此時(shí)查看了兩個(gè)賬戶。
? ? 3.不可重復(fù)讀:事務(wù)兩次讀取的數(shù)據(jù)不同。
? ? ? ? ①事務(wù)1讀取數(shù)據(jù)A之后,事務(wù)2修改了A并提交,事務(wù)1再讀。
? ? ? ? ? ? 1)事務(wù)1讀取數(shù)據(jù)A。
? ? ? ? ? ? 2)事務(wù)2修改A并提交。
? ? ? ? ? ? 3)事務(wù)1讀A。
? ? ? ? ? ? 場(chǎng)景:查看銀行余額,查詢完成之前,之后再有人轉(zhuǎn)賬余額都不應(yīng)該變化。
? ? ? ? ②幻讀:事務(wù)1讀取了數(shù)據(jù)之后,事務(wù)2插入或者刪除了某些數(shù)據(jù)并提交,事務(wù)1再讀。
? ? ? ? ? ? 1)事務(wù)1讀取了一個(gè)范圍內(nèi)的數(shù)據(jù){A,B,C......}。
? ? ? ? ? ? 2)事務(wù)2插入了數(shù)據(jù)D到此范圍并提交。
? ? ? ? ? ? 3)事務(wù)1讀此范圍發(fā)現(xiàn)新增了D,不一致。
? ? ? ? ? ? 場(chǎng)景:網(wǎng)站實(shí)時(shí)統(tǒng)計(jì)某一時(shí)間在線用戶人數(shù),在統(tǒng)計(jì)完成之前后面再有新增的用戶記錄都不應(yīng)該沒查詢到。
? ? ? ? ? ?
? ? 不同的隔離級(jí)別與并發(fā)問題對(duì)應(yīng)關(guān)系:
? ? ? ? 1.修改丟失:
? ? ? ? ? ? 這種問題屬于邏輯上的問題,數(shù)據(jù)庫(kù)是處理不了的。以賣票為例,當(dāng)有窗口在售票的時(shí)候,其他窗口就應(yīng)該等該窗口售完票之后修改了售票數(shù)額再進(jìn)行讀取余額進(jìn)行修改。所以應(yīng)該由程序進(jìn)行控制。
? ? ? ? 2.臟讀:read uncommitted隔離下會(huì)產(chǎn)生臟讀,read committed以上的級(jí)別都處理了臟讀。
? ? ? ? 3.不可重復(fù)讀:read committed以下隔離級(jí)別有不可重復(fù)讀,repeatable以上級(jí)別處理了該問題(有一些DBMS沒有處理幻讀的問題)。
下面介紹MySQL處理這些問題并實(shí)現(xiàn)事務(wù)四大特性的原理:
首先我們先引進(jìn)鎖的概念:
? ? ? ? 目前在計(jì)算機(jī)中對(duì)于共享數(shù)據(jù)的并發(fā)訪問大多只能通過(guò)“鎖”來(lái)進(jìn)行處理。在數(shù)據(jù)庫(kù)中也引進(jìn)了兩種鎖的概念:悲觀鎖與樂觀鎖。
? ?
?
悲觀鎖:
? ? ? ? 顧名思義:悲觀鎖及在事務(wù)對(duì)數(shù)據(jù)進(jìn)行訪問的時(shí)候,悲觀地認(rèn)為之后會(huì)有其他事務(wù)會(huì)訪問該數(shù)據(jù)并進(jìn)行修改而在數(shù)據(jù)上加上一把鎖,而阻塞其他事務(wù)。其中悲觀鎖分為共享鎖和排他鎖。
? ? ? ? ? ? 共享鎖(Share Lock):簡(jiǎn)稱S鎖
? ? ? ? ? ? 共享鎖又稱為讀鎖。若事務(wù)T對(duì)數(shù)據(jù)對(duì)象A加上S鎖,則只事務(wù)T可以讀A但是不能修改A,其他任何事務(wù)只能再對(duì)A加S鎖,而不能加X鎖 ,直到T釋放A上的鎖。? ? ? ? ? ?
? ? ? ? ? ? 排他鎖(eXclusive Lock):簡(jiǎn)稱X鎖
? ? ? ? ? ? 排他鎖又稱為寫鎖。若事務(wù)T對(duì)數(shù)據(jù)對(duì)象A加上X鎖,則只允許T讀取和修改A,其他任何事務(wù)都不能對(duì)A加任何類型的鎖 ,直到T釋放A上的鎖。
注意這里的兩個(gè)命題:
1.事務(wù)對(duì)A加上了X鎖(條件)能讀寫A(結(jié)果)
2.事務(wù)對(duì)A加上了S鎖(條件)能讀A(結(jié)果)
是充分條件,不是必要條件。
即:
1.事務(wù)對(duì)A能讀寫A(條件)就加上了X鎖(結(jié)果)
2.事務(wù)對(duì)A能讀A(條件)就加上了S鎖(結(jié)果)
這是不成立的。
上述粗體字段就是對(duì)前半命題的補(bǔ)充,拿其他任何事務(wù)都不能對(duì)A加任何類型的鎖舉例:
這里是定義了,加了X鎖之后,不能加其他的鎖,而不是不允許其他事務(wù)讀取A了。這里只是定義了加鎖操作之間的互斥關(guān)系。
樂觀鎖:
? ? ? ? 顧名思義:這是一個(gè)樂觀的“鎖”,其實(shí)稱其為樂觀控制(驗(yàn)證)法更為準(zhǔn)確。即事務(wù)在訪問數(shù)據(jù)的時(shí)候,樂觀地認(rèn)為不會(huì)有其他事務(wù)對(duì)數(shù)據(jù)進(jìn)行修改,不阻塞任何事務(wù),在之后再對(duì)該數(shù)據(jù)進(jìn)行訪問的時(shí)候會(huì)驗(yàn)證其是否和之前訪問的數(shù)據(jù)是一致的。
下面討論使用這兩種鎖如何實(shí)現(xiàn)事務(wù)四大特性?
? ? 1.理論上,只使用悲觀鎖是可以實(shí)現(xiàn)的:
? ? ? ? 1)在讀之前要加共享鎖。
? ? ? ? 2)在寫之前要加排他鎖。
? ? 在這種情況下,確實(shí)實(shí)現(xiàn)了事務(wù)的四大特性,但是因?yàn)殒i的特性會(huì)導(dǎo)致,寫操作會(huì)阻塞大量的讀操作(讀操作占了數(shù)據(jù)庫(kù)訪問中很大的比重),嚴(yán)重影響了并發(fā)訪問程度,而且會(huì)很容易導(dǎo)致死鎖的發(fā)生(事務(wù)之間互相等待釋放鎖,解決死鎖有多種方式,這里不具體討論)。
? ? 2.排他鎖加樂觀鎖,即多版本并發(fā)控制。
? ? ? ? 1)讀不加鎖。
? ? ? ? 2)寫之前加排他鎖。
? ? ? ? 3)維護(hù)多個(gè)版本的數(shù)據(jù)供不同的事務(wù)讀取。(不同隔離級(jí)別情況不同,下面有介紹)。? ? ? ?
? ? 多版本并發(fā)控制(MutiVersion Concurrency Control),簡(jiǎn)稱MVCC。它使得大部分支持行鎖的事務(wù)引擎,不再單純的使用行鎖來(lái)進(jìn)行數(shù)據(jù)庫(kù)的并發(fā)控制,取而代之的是把數(shù)據(jù)庫(kù)的行鎖與行的多個(gè)版本結(jié)合起來(lái),只需要很小的開銷,就可以實(shí)現(xiàn)非鎖定讀,從而大大提高數(shù)據(jù)庫(kù)系統(tǒng)的并發(fā)性能。
? ? 這里引入新概念:行鎖。行鎖指悲觀鎖的粒度(即加鎖的對(duì)象單位),其中有行鎖,表鎖,頁(yè)鎖,數(shù)據(jù)庫(kù)鎖等等。
MySQL(InnoDB)實(shí)現(xiàn)事務(wù)四大特性的原理:? ?
概念簡(jiǎn)述:
1.undo log(撤銷)
? ? undo log記錄數(shù)據(jù)的多版本快照,事務(wù)在修改數(shù)據(jù)之前會(huì)將舊版本的數(shù)據(jù)復(fù)制到undo log,讀操作不寫undo log。
? ? 存在形式:會(huì)分配到一定的內(nèi)存(cache),也會(huì)寫入磁盤(ibdata1文件)。
? ? 寫入方式:按修改操作順序?qū)懭耄环质聞?wù)。
? ? ? ? 1)用于事務(wù)的回滾而實(shí)現(xiàn)原子性。
? ? ? ? 2)用于事務(wù)讀操作匹配版本實(shí)現(xiàn)一致性,隔離性。
? ? ? ?
2.redo log (重做)
? ? 事務(wù)在不會(huì)直接覆蓋修改數(shù)據(jù)庫(kù)磁盤中的數(shù)據(jù)(以下稱為data file),會(huì)先復(fù)制一份數(shù)據(jù)到redo cache中進(jìn)行修改,然后寫到redo file之后,再修改data file。
? ? 存在形式:會(huì)分配到一定的內(nèi)存(cache),也會(huì)寫入磁盤(ib_logfile0、ib_logfile1文件)。
? ? 寫入方式:按修改操作順序?qū)懭?,不分事?wù)。
? ? redo log主要用于維護(hù)數(shù)據(jù)的持久性。
3.DATA_TRX_ID、DATA_ROLL_PTR、DELETE BIT
? ? 在MySQL表中有三個(gè)隱藏字段:
? ? DATA_TRX_ID:事務(wù)修改當(dāng)前行數(shù)據(jù)的時(shí)候會(huì)將這個(gè)字段記錄為自己的事務(wù)ID
? ? DATA_ROLL_PTR:回滾指針,事務(wù)修改當(dāng)前行數(shù)據(jù)的時(shí)候?qū)⑦@個(gè)字段記錄指向undolog中上一次修改該行的記錄。
? ? DELETE BIT:事務(wù)修改當(dāng)前行數(shù)據(jù)的時(shí)候記錄標(biāo)記為“被刪除”。
? ?
? ? 事務(wù)修改數(shù)據(jù)的流程分析:
? ? 事務(wù)將數(shù)據(jù)對(duì)象A從數(shù)據(jù)庫(kù)(磁盤)中復(fù)制到redo cache buffer中進(jìn)行修改,在修改之前再?gòu)?fù)制一份填寫刪除標(biāo)識(shí)字段,將其回滾指針指向undo中上一條記錄,放到undo cache buffer中,然后將redo cache中的數(shù)據(jù)副本中的trxid設(shè)為自己的事務(wù)id,undo&redo cache每隔一段時(shí)間(1秒)刷到undo&redo file,redo file寫入之后將寫入redo file的數(shù)據(jù)覆蓋寫入data file數(shù)據(jù)。
? ? 事務(wù)修改數(shù)據(jù)的流程圖解:
?

? ?
? ?
? ? ①原子性:事務(wù)沒完成,可以通過(guò)回滾指針一條一條記錄往前反向復(fù)原,直到事務(wù)修改之前的記錄版本。
? ?
? ? ②一致性&隔離性(因?yàn)橐恢滦砸蕾囉诟綦x性,這里一起討論):
? ? ? ? read uncommitted:
? ? ? ? 讀不加鎖,寫加X鎖。
? ?
? ? ? ? read committed&repeatable read:
? ? ? ? 事務(wù)讀操作每次都是結(jié)合read view在data+undo file中匹配適合的數(shù)據(jù)。(MVCC)
? ? ? ?
? ? ? ? 新概念:
? ? ? ? read view:
? ? ? ? 每個(gè)事務(wù)在創(chuàng)建的時(shí)候InnoDB(MySQL數(shù)據(jù)庫(kù)引擎)都會(huì)記錄當(dāng)前系統(tǒng)中的活動(dòng)事務(wù)id(不包含committed以及本事務(wù)的id)到一個(gè)副本-->read view。這個(gè)副本的功能是記錄了對(duì)與本事務(wù)來(lái)說(shuō)應(yīng)該隔離的事務(wù)(不可見)。
? ? ? ? 注:事務(wù)id是按照創(chuàng)建順序遞增分配的。? ? ? ? ? ? .
? ? ? ?
? ? ? ? 1.將data+undo file中的符合條件的數(shù)據(jù)全部找出(多版本)。將排在第一的數(shù)據(jù)的transaction_id賦值給trx_id,read view中的最小值id為trx_min,最大值為trx_max。
? ? ? ? 2.如果trx_id<trx_min,說(shuō)明創(chuàng)建該版本數(shù)據(jù)的事務(wù)是在read view創(chuàng)建之前(也就是在當(dāng)前事務(wù)創(chuàng)建之前)就已經(jīng)committed了。跳到步驟6。
? ? ? ? 3.如果trx_id>trx_max,說(shuō)明創(chuàng)建該版本數(shù)據(jù)的事務(wù)是在read view創(chuàng)建之后(也就是當(dāng)前事務(wù)創(chuàng)建之后)創(chuàng)建的,此版本數(shù)據(jù)不可見。跳到步驟5
? ? ? ? 4.如果trx_min <= trx_id<= trx_max,說(shuō)明創(chuàng)建該版本數(shù)據(jù)的事務(wù)在當(dāng)前事務(wù)創(chuàng)建之前創(chuàng)建的,(但是不確定是否是在當(dāng)前事務(wù)創(chuàng)建之前提交的)。拿trx_id遍歷匹配read view:
? ? ? ? ? ? 1)如果有相等的事務(wù)id,說(shuō)明創(chuàng)建該版本的事務(wù)在當(dāng)前事務(wù)創(chuàng)建的時(shí)候還是活躍的。此版本數(shù)據(jù)也不可見,跳到步驟5;
? ? ? ? ? ? 2)如果沒有匹配到相等的事務(wù)id,說(shuō)明創(chuàng)建該版本的事務(wù)在當(dāng)前事務(wù)創(chuàng)建的之前就提交了,只不過(guò)它比一些在它之前創(chuàng)建的事務(wù)提交得早。調(diào)到步驟6。
? ? ? ? 5.用回滾指針找到上一版本數(shù)據(jù),將其trasaction_id賦值給trx_id,跳到步驟2。
? ? ? ? 6.該版本數(shù)據(jù)是可見的,返回該數(shù)據(jù)到讀取操作。
? ? ? ? 在rc隔離級(jí)別下:在事務(wù)創(chuàng)建之初以及每次read的時(shí)候都會(huì)創(chuàng)建一份read view。
? ? ? ? 在rr隔離級(jí)別下:只在事務(wù)創(chuàng)建的時(shí)候創(chuàng)建一份相對(duì)與該事務(wù)全局的read view。
? ? ? ? serializable:
? ? ? ? 讀加S鎖,寫加X鎖。
? ? ? ?
? ? ③持久性:因?yàn)樗袛?shù)據(jù)修改都是在提交完成之前已經(jīng)通過(guò)redo log寫到了服務(wù)器數(shù)據(jù)庫(kù)磁盤中去了。即使之后數(shù)據(jù)庫(kù)故障都可以通過(guò)redo中的記錄進(jìn)行恢復(fù)。(根據(jù)undo進(jìn)行的rollback操作也會(huì)被寫入redo,也就是說(shuō)恢復(fù)數(shù)據(jù)庫(kù)的時(shí)候,rollback操作也會(huì)重新執(zhí)行)。
? ? ? ?
? ? ? ?
? ?
?