1. 計算機組成原理(三)存儲系統
輔存中的數據要調入主存後才能被CPU訪問
按存儲介質,存儲器可分為磁表面存儲器(磁碟、磁帶)、磁心存儲器半導體存儲器(MOS型存儲器、雙極型存儲器)和光存儲器(光碟)。
隨機存取存儲器(RAM):讀寫任何一個存儲單元所需時間都相同,與存儲單元所在的物理位置無關,如內存條等
順序存取存儲器(SAM):讀寫一個存儲單元所需時間取決於存儲單元所在的物理位置,如磁碟等
直接存取存儲器(DAM):既有隨機存取特性,也有順序存取特性。先直接選取信息所在區域,然後按順序方式存取。如硬碟等
相聯存儲器,即可以按內容訪問的存儲器(CAM)可以按照內容檢索到存儲位置進行讀寫,「快表」就是一種相聯存儲器
讀寫存儲器—即可讀、也可寫(如:磁碟、內存、Cache)
只讀存儲器—只能讀,不能寫(如:實體音樂專輯通常採用CD-ROM,實體電影採用藍光光碟,BIOS通常寫在ROM中)
斷電後,存儲信息消失的存儲器——易失性存儲器(主存、Cache)
斷電後,存儲信息依然保持的存儲器——非易失性存儲器(磁碟、光碟)
信息讀出後,原存儲信息被破壞——破壞性讀出(如DRAM晶元,讀出數據後要進行重寫)
信息讀出後,原存儲信息不被破壞——非破壞性讀出(如SRAM晶元、磁碟、光碟)
存儲器晶元的基本電路如下
封裝後如下圖所示
圖中的每條線都會對應一個金屬引腳,另外還有供電引腳、接地引腳,故可以由此求引腳數目
n位地址對應2 n 個存儲單元
假如有8k×8位的存儲晶元,即
現代計算機通常按位元組編址,即每個位元組對應一個地址
但也支持按位元組定址、按字定址、按半字定址、按雙字定址
(Dynamic Random Access Memory,DRAM)即動態RAM,使用柵極電容存儲信息
(Static Random Access Memory,SRAM)即靜態RAM,使用雙穩態觸發器存儲信息
DRAM用於主存、SRAM用於Cache,兩者都屬於易失性存儲器
簡單模型下需要有 根選通線,而行列地址下僅需 根選通線
ROM晶元具有非易失性,斷電後數據不會丟失
主板上的BIOS晶元(ROM),存儲了「自舉裝入程序」,負責引導裝入操作系統(開機)。邏輯上,主存由 輔存RAM+ROM組成,且二者常統一編址
位擴展的連接方式是將多個存儲晶元的地址端、片選端和讀寫控制端相應並聯,數據端分別引出。
字擴展是指增加存儲器中字的數量,而位數不變。字擴展將晶元的地址線、數據線、讀寫控制線相應並聯,而由片選信號來區分各晶元的地址范圍。
實際上,存儲器往往需要同時擴充字和位。字位同時擴展是指既增加存儲字的數量,又增加存儲字長。
兩個埠對同一主存操作有以下4種情況:
當出現(3)(4)時,置「忙」信號為0,由判斷邏輯決定暫時關閉一個埠(即被延時),未被關閉的埠正常訪問,被關閉的埠延長一個很短的時間段後再訪問。
多體並行存儲器由多體模塊組成。每個模塊都有相同的容量和存取速度,各模塊都有獨立的讀寫控制電路、地址寄存器和數據寄存器。它們既能並行工作,又能交義工作。多體並行存儲器分為高位交叉編址(順序方式)和低位交叉編址(交叉方式)兩種.
①高位交叉編址
②低位交叉編址
採用「流水線」的方式並行存取(宏觀上並行,微觀上串列),連續取n個存儲字耗時可縮短為
宏觀上,一個存儲周期內,m體交叉存儲器可以提供的數據量為單個模塊的m倍。存取周期為T,存取時間/匯流排傳輸周期為r,為了使流水線不間斷,應保證模塊數
單體多字系統的特點是存儲器中只有一個存儲體,每個存儲單元存儲m個字,匯流排寬度也為m個字。一次並行讀出m個字,地址必須順序排列並處於同一存儲單元。
缺點:每次只能同時取m個字,不能單獨取其中某個字;指令和數據在主存內必須是連續存放的
為便於Cache 和主存之間交換信息,Cache 和主存都被劃分為相等的塊,Cache 塊又稱Cache 行,每塊由若干位元組組成。塊的長度稱為塊長(Cache 行長)。由於Cache 的容量遠小於主存的容盤,所以Cache中的塊數要遠少於主存中的塊數,它僅保存主存中最活躍的若干塊的副本。因此 Cache 按照某種策略,預測CPU在未來一段時間內欲訪存的數據,將其裝入Cache.
將某些主存塊復制到Cache中,緩和CPU與主存之間的速度矛盾
CPU欲訪問的信息已在Cache中的比率稱為命中率H。先訪問Cache,若Cache未命中再訪問主存,系統的平均訪問時間t 為
同時訪問Cache和主存,若Cache命中則立即停止訪問主存系統的平均訪問時間t 為
空間局部性:在最近的未來要用到的信息(指令和數據),很可能與現在正在使用的信息在存儲空間上是鄰近的
時間局部性:在最近的未來要用到的信息,很可能是現在正在使用的信息
基於局部性原理,不難想到,可以把CPU目前訪問的地址「周圍」的部分數據放到Cache中
直接映射方式不需要考慮替換演算法,僅全相聯映射和組相聯映射需要考慮
①隨機演算法(RAND):若Cache已滿,則隨機選擇一塊替換。實現簡單,但完全沒考慮局部性原理,命中率低,實際效果很不穩定
②先進先出演算法(FIFO):若Cache已滿,則替換最先被調入Cache的塊。實現簡單,依然沒考慮局部性原理
③近期最少使用演算法(LRU):為每一個Cache塊設置一個「計數器」,用於記錄每個Cache塊已經有多久沒被訪問了。當Cache滿後替換「計數器」最大的.基於「局部性原理」,LRU演算法的實際運行效果優秀,Cache命中率高。
④最不經常使用演算法(LFU):為每一個Cache塊設置一個「計數器」,用於記錄每個Cache塊被訪問過幾次。當Cache滿後替換「計數器」最小的.並沒有很好地遵循局部性原理,因此實際運行效果不如LRU
現代計算機常採用多級Cache,各級Cache之間常採用「全寫法+非寫分配法」;Cache-主存之間常採用「寫回法+寫分配法」
寫回法(write-back):當CPU對Cache寫命中時,只修改Cache的內容,而不立即寫入主存,只有當此塊被換出時才寫回主存。減少了訪存次數,但存在數據不一致的隱患。
全寫法(寫直通法,write-through):當CPU對Cache寫命中時,必須把數據同時寫入Cache和主存,一般使用寫緩沖(write buffer)。使用寫緩沖,CPU寫的速度很快,若寫操作不頻繁,則效果很好。若寫操作很頻繁,可能會因為寫緩沖飽和而發生阻塞訪存次數增加,速度變慢,但更能保證數據一致性
寫分配法(write-allocate):當CPU對Cache寫不命中時,把主存中的塊調入Cache,在Cache中修改。通常搭配寫回法使用。
非寫分配法(not-write-allocate):當CPU對Cache寫不命中時只寫入主存,不調入Cache。搭配全寫法使用。
頁式存儲系統:一個程序(進程)在邏輯上被分為若干個大小相等的「頁面」, 「頁面」大小與「塊」的大小相同 。每個頁面可以離散地放入不同的主存塊中。CPU執行的機器指令中,使用的是「邏輯地址」,因此需要通「頁表」將邏輯地址轉為物理地址。頁表的作用:記錄了每個邏輯頁面存放在哪個主存塊中
邏輯地址(虛地址):程序員視角看到的地址
物理地址(實地址):實際在主存中的地址
快表是一種「相聯存儲器」,可以按內容尋訪,表中存儲的是頁表項的副本;Cache中存儲的是主存塊的副本
地址映射表中每一行都有對應的標記項
主存-輔存:實現虛擬存儲系統,解決了主存容量不夠的問題
Cache-主存:解決了主存與CPU速度不匹配的問題
2. 分布式基礎-存儲引擎
題目和文章內容有點不太符合,這里存儲引擎是指單機存儲引擎。對於分布式存儲系統來說,存儲引擎是必須的。存儲引擎決定了數據在內存和磁碟中具體如何存儲的,如何方便地拿出來的問題。可以說直接決定了存儲系統的性能和可以干什麼,不可以干什麼的問題;本文參考《數據密集型應用系統的設計》 和《大規模分布式存儲系統原理解析和架構實戰》。
存儲系統的功能做機制的簡化就是存儲和查詢,如果從一般功能出發就是基礎的增刪改查。從最簡單的開始想起,最簡單的存儲系統,無非就是把數據直接寫入到文件中(可以按照K,V一行方式存儲),需要的時候就順序讀取文件,找到可以需要查詢的行。這在少量的數據的時候並沒有問題,但是如果是大批量數據,幾百MB或者幾GB,甚至TB,PB的時候,順序讀取大量文件那速度慢的嚇人。
順序讀取文件做遍歷查找,速度很慢,我們第一想到的思路是建索引,索引最常用的就是哈希表了,如果我們對文件中的數據建個索引,Key 保存著我們下次要查詢的值,Value對應這哪個文件的哪個位置。在內存中保存這個索引,下次查詢的時候,我們通過哈希錶快速定位到文件和位置,就可以迅速取到需要的值了。Bitcask折中日誌型小型文件系統就採用這種存儲方法,它可以提供高性能的讀寫,只需要經過一次磁碟的定址就可以獲取到所需要的數據。
作為日誌型的存儲系統,Bitcask的刪除和修改是通過順序記錄到文件中,並不是對原來的文件進行修改,這減少了隨機磁碟的讀寫操作。數據寫入到文件中,如果一直寫,顯然文件越來越大,不便於操作,所以限制文件的大小,當大小達到一定規模後,重新寫入一個文件。 對於更新和刪除的數據,如果不處理,會產生大量的垃圾數據,佔用了空間,所以後台會定時進行文件合並,合並的時候刪除標記刪除的具體數據。
Bitcask
哈希存儲引擎的數據分為兩份,一份是內存中的數據,一個是磁碟的文件,系統崩潰後,磁碟中的哈希表就沒有了。如果恢復的時候通過讀取文件的方式也是可以重建的,但是如果文件很多,很大,恢復的時間就會很長,Bitcask對每個段的文件的哈希錶快照存儲在文件中,下次恢復的時候可以快速恢復。
Bitcask只有一個寫入線程追加,可以採用多個讀取的線程並發讀取,性能上還是很不錯。
哈希存儲引擎 因為採用哈希表,查找的性能不錯,但是同樣因為採用哈希存儲引擎,會導致范圍查詢,只能通過遍歷的方式去查詢數據,范圍查詢慢。
剛才結構也說了,索引必須可以保存在內存中,才可以性能夠好,但是如果數據量超大,內存中無法保存,保存到磁碟中,會產生大量的隨機訪問。另外哈希還存在著哈希沖突的問題。
剛才的哈希存儲引擎的兩個缺點,一是范圍查詢性能很差,我們要做范圍查詢,最好數據是有序的,有序的就可以不用遍歷全部數據去做范圍查詢了。所以我們內存的數據不就不適合哈希索引,我們可以考慮改造成一個支持排序的數據結構。 另外剛才的哈希存儲引擎,數據是按照順序寫入到數據文件中的,如果同一個key的多次更新,只保留最後一個數據的時候,是不是挺麻煩。
我們可以將文件中和內存中的數據都排序,這種格式稱為排序字元串,在Level DB中叫SSTable。文件中的K-V結構排序後,好處是我們在做多文件合並的時候,可以按照多路歸並的演算法,快速排序,用多個指針依次比較和後移就可以辦到。多個文件含有同一個值的時候,我們可以保留最新的欄位值。
內存中的數據排序後,我們不一定對所有的數據的key都保存,可以只保存部分,根據key的排序特性,也可以很容易找到要找的值。 由於要對內存中的數據排隊,而且數據要經常插入和刪除,所以紅黑樹和AVL樹是比較適合這種場合。對於存儲在磁碟上的文件,也是有序的,用普通的AVL樹或紅黑樹,保存到磁碟上後,數據多的話,樹的層次會很高,這樣通過多個指針需要多次隨機讀取,所以一般採用專門為大數據存儲磁碟而設計的B+樹,B+樹的每個節點的分叉很多,一個節點可能有上千個分支。這樣很少的層次就可以支持大量的數據了。
這種引擎如何寫入數據:
如何讀取數據:
這個存儲引擎就是LSM 存儲引擎的本質了,Level DB 就是採用這個存儲引擎的。
類似的存儲引擎還用於HBASE,以前還記得學習HBase的時候minor compaction(少量的HFile合適小文件合並,為提升性能同時減少IO壓力)和major compaction(一個Node節點的所有文件合並),還比較迷茫。 從上圖的Level DB存儲引擎圖可以看出,數據處理過程:
說明清單文件保存的是元數據信息,記錄了每個SSTable文件所屬的Level,文件中的key的最大值和最小值。同時由於SSTable文件經常變動的,所以增加個當前文件指向當前的清單文件這樣操作起來就不用加鎖了。
相對於以上兩種引擎,B樹存儲引擎應用的最廣泛,在關系型資料庫中運用的很多。B樹存儲引擎不光支持隨機查詢,還很好地支持范圍查詢。像SSTable一樣,B樹引擎同樣保持了對key的排序。在文件存儲上,還是有很大的差異。LSM存儲引擎的段文件大小不一,是順序寫入到磁碟的。B-Tree不像LSM樹那樣有內存表和SSTable,而只有一個B樹,當然一些頂層塊常在內存中。
B樹是按照塊存儲資料庫的數據的,它一般是一個多叉樹,比如InnoDB引擎採用B+樹存儲,每個節點大概有1200個子分支。B樹分為葉子節點和非葉子節點,葉子節點存儲的是key和具體的數據,而非葉子節點存的是key和磁碟地址。
B樹存儲結構
以B+樹為例說明查詢和插入的基本流程
讀取一個節點,如果對應的節點所在的數據頁不在內存中,需要按照下面的過程從磁碟中讀取,然後緩存在內存中。
插入和更新按照InnoDB引擎為例的話,還是比較復雜。
實際中還涉及到bin log日誌。可以看到實際工程中,B-樹引擎還是通過redo log這種WAL日誌,用順序磁碟讀寫替換了隨機讀寫;change buffer 減少了隨機讀數據的過程,可以合並多條修改記錄,一次性寫,增加了性能。
B樹和LSM樹相比有以下特點: B-樹引擎特點:
3. 數據 寫入硬碟的順序
簡單來說:硬碟寫入數據是從外向內寫的。最外圈是0磁軌。目前絕大多數硬碟是用溫徹斯特(Winchester)技術製造的硬碟,所以也被稱為溫盤。所以個個廠家都是一樣的。
具體的硬碟讀寫原理:
系統將文件存儲到磁碟上時,按柱面、磁頭、扇區的方式進行,即最先是第1磁軌的第一磁頭下(也就是第1盤面的第一磁軌)的所有扇區,然後,是同一柱面的下一磁頭(也就是第2盤面的第一磁軌),……,一個柱面存儲滿後就推進到下一個柱面,直到把文件內容全部寫入磁碟。如果中間有其他文件已經使用了一部分扇區,那就跳過。
系統也以相同的順序讀出數據。讀出數據時通過告訴磁碟控制器要讀出扇區所在的柱面號、磁頭號和扇區號(物理地址的三個組成部分)進行。磁碟控制器則 直接使磁頭部件步進到相應的柱面,選通相應的磁頭,等待要求的扇區移動到磁頭下。在扇區到來時,磁碟控制器讀出每個扇區的頭標,把這些頭標中的地址信息與 期待檢出的磁頭和柱面號做比較(即尋道),然後,尋找要求的扇區號。待磁碟控制器找到該扇區頭標時,根據其任務是寫扇區還是讀扇區,來決定是轉換寫電路, 還是讀出數據和尾部記錄。找到扇區後,磁碟控制器必須在繼續尋找下一個扇區之前對該扇區的信息進行後處理。如果是讀數據,控制器計算此數據的ECC碼,然 後,把ECC碼與已記錄的ECC碼相比較。如果是寫數據,控制器計算出此數據的ECC碼,與數據一起存儲。在控制器對此扇區中的數據進行必要處理期間,磁 盤繼續旋轉。
溫徹斯特(Winchester)技術:
硬碟停轉時,磁頭停留在啟停區。當硬碟開始旋轉後,旋轉速度達到額定的高速時,磁頭就會因碟片旋轉產生的氣流而抬起, 這時磁頭才向碟片存放數據的區域移動。
碟片旋轉產生的氣流相當強,足以使磁頭托起,並與盤面保持一個微小的距離。這個距離越小,磁頭讀寫數據的靈敏度就越高,當然對硬碟各部件的要求也越 高。早期設計的磁碟驅動器使磁頭保持在盤面上方幾微米處飛行。稍後一些設計使磁頭在盤面上的飛行高度降到約0.1μm~0.5μm,現在的水平已經達到 0.005μm~0.01μm,這只是人類頭發直徑的千分之一。
氣流既能使磁頭脫離開盤面,又能使它保持在離盤面足夠近的地方,非常緊密地跟隨著磁碟表面呈起伏運動,使磁頭飛行處於嚴格受控狀態。磁頭必須飛行在盤面上方,而不是接觸盤面,這種位置可避免擦傷磁性塗層,而更重要的是不讓磁性塗層損傷磁頭。
但是,磁頭也不能離盤面太遠,否則,就不能使盤面達到足夠強的磁化,難以讀出盤上的磁化翻轉(磁極轉換形式,是磁碟上實際記錄數據的方式)。
4. QT存儲日誌用資料庫還是txt文本
QT存儲日誌用資料庫還是txt文本是需要具體問題具體分析的,因為如果小量的寫資料庫沒事。如果是大量的,肯定寫文件好。匯總後寫程序導入資料庫。還有一種方法是寫redis等內存資料庫,並累積數量後觸發合並寫入資料庫操作。
並且如果這個日誌是需要定期分析的,寫在資料庫里更方便處理;反之只是留檔,就存文件里 但2種方式都要注意寫操作的頻率。
絕對不能產生一行寫一行,中間加一個內存隊列來過渡,比如memcache,有新日誌就加入隊列,然後做個定時器去批量寫入文件並清空隊列,同時也規避文件沖突了。
QT存儲中大端模式和小端模式是:
對於long long a 和 struct{ char a;short b;int c;}二者同樣占據了8個位元組的空間,在存儲上,後者則是先存儲一個char,空一個位元組,然後按照大端/小端模式存儲short,最後按照大端/小端模式存儲int。
在我們日常使用的x86架構的計算機中(其他類別的可能會採用大端模式或可配置模式,可以通過查閱資料或者用下文的代碼進行測試),都是使用的小端模式,而網路位元組序是大端模式的。
這就使得在網路通信時進行位元組序的轉換變得極為重要。比方說,通信雙方規定了了通信頭為一個4位元組的魔數(Magic Number),而一方按著大端序的模式發送。
一方按著小端序的模式解讀,那麼兩方的通信就會失敗。如果沒有這個魔數,而在內部的數據中出現這樣的問題則會更加的麻煩。
5. sql Server日誌作用以及為什麼先寫日誌後寫數據
今天在看Oracle的BackupGroundProcess,里邊有一段是寫到為什麼先寫日誌後寫數據的:LGWR, on the other hand, does lots of sequential writes to the redo log. This is an important distinction and one of the reasons that Oracle has a redo log and the LGWR process as well as the DBWn process. Scattered writes are significantly slower than sequential writes. By having the SGA buffer dirty blocks and the LGWR process do large sequential writes that can re-create these dirty buffers, we achieve an increase in performance. 其實SQL Server也是一樣,每一個SQL Server的資料庫都會按照其修改數據(insert,update,delete)的順序將對應的日誌記錄到日誌文件.SQL Server使用了Write-Ahead logging技術來保證了事務日誌的原子性和持久性.而這項技術不僅僅保證了ACID中的原子性(A)和持久性(D),還大大減少了IO操作,把對數據的修改提交到磁碟的工作交給lazy-writer和checkpoint.預寫式日誌(Write-Ahead Logging (WAL))SQL Server使用了WAL來確保了事務的原子性和持久性.實際上,不光是SQL Server,基本上主流的關系資料庫包括oracle,mysql,db2都使用了WAL技術.WAL的核心思想是:在數據寫入到資料庫之前,先寫入到日誌.因為對於數據的每筆修改都記錄在日誌中,所以將對於數據的修改實時寫入到磁碟並沒有太大意義,即使當SQL Server發生意外崩潰時,在恢復(recovery)過程中那些不該寫入已經寫入到磁碟的數據會被回滾(RollBack),而那些應該寫入磁碟卻沒有寫入的數據會被重做(Redo)。從而保證了持久性(Durability)但WAL不僅僅是保證了原子性和持久性。還會提高性能.硬碟是通過旋轉來讀取數據,通過WAL技術,每次提交的修改數據的事務並不會馬上反映到資料庫中,而是先記錄到日誌.在隨後的CheckPoint和lazy Writer中一並提交,如果沒有WAL技術則需要每次提交數據時寫入資料庫:而使用WAL合並寫入,會大大減少磁碟IO:也許你會有疑問,那每次對於修改的數據還是會寫入日誌文件.同樣消耗磁碟IO。上篇文章講過,每一筆寫入日誌的記錄都是按照先後順序,給定順序編號的LSN進行寫入的,日誌只會寫入到日誌文件的邏輯末端。而不像數據那樣,可能會寫到磁碟的各個地方.所以,寫入日誌的開銷會比寫入數據的開銷小很多。SQL Server修改數據的步驟SQL Server對於數據的修改,會分為以下幾個步驟順序執行:1.在SQL Server的緩沖區的日誌中寫入」Begin Tran」記錄2.在SQL Server的緩沖區的日誌頁寫入要修改的信息3.在SQL Server的緩沖區將要修改的數據寫入數據頁4.在SQL Server的緩沖區的日誌中寫入」Commit」記錄5.將緩沖區的日誌寫入日誌文件6.發送確認信息(ACK)到客戶端(SMSS,ODBC等)可以看到,事務日誌並不是一步步寫入磁碟.而是首先寫入緩沖區後,一次性寫入日誌到磁碟.這樣既能在日誌寫入磁碟這塊減少IO,還能保證日誌LSN的順序.上面的步驟可以看出,即使事務已經到了Commit階段,也僅僅只是把緩沖區的日誌頁寫入日誌,並沒有把數據寫入資料庫.那將要修改的數據頁寫入資料庫是在何時發生的呢?Lazy Writer和CheckPoint上面提到,SQL Server修改數據的步驟中並沒有包含將數據實際寫入到磁碟的過程.實際上,將緩沖區內的頁寫入到磁碟是通過兩個過程中的一個實現:這兩個過程分別為:1.CheckPoint2.Lazy Writer任何在緩沖區被修改的頁都會被標記為「臟」頁。將這個臟頁寫入到數據磁碟就是CheckPoint或者Lazy Writer的工作.當事務遇到Commit時,僅僅是將緩沖區的所有日誌頁寫入磁碟中的日誌文件:而直到Lazy Writer或CheckPoint時,才真正將緩沖區的數據頁寫入磁碟文件:前面說過,日誌文件中的LSN號是可以比較的,如果LSN2>LSN1,則說明LSN2的發生時間晚於LSN1的發生時間。CheckPoint或Lazy Writer通過將日誌文件末尾的LSN號和緩沖區中數據文件的LSN進行對比,只有緩沖區內LSN號小於日誌文件末尾的LSN號的數據才會被寫入到磁碟中的資料庫。因此確保了WAL(在數據寫入到資料庫之前,先寫入日誌)。Lazy Writer和CheckPoint的區別Lazy Writer和CheckPoint往往容易混淆。因為Lazy Writer和CheckPoint都是將緩沖區內的「臟」頁寫入到磁碟文件當中。但這也僅僅是他們唯一的相同點了。Lazy Writer存在的目的是對緩沖區進行管理。當緩沖區達到某一臨界值時,Lazy Writer會將緩沖區內的臟頁存入磁碟文件中,而將未修改的頁釋放並回收資源。而CheckPoint存在的意義是減少伺服器的恢復時間(Recovery Time).CheckPoint就像他的名字指示的那樣,是一個存檔點.CheckPoint會定期發生.來將緩沖區內的「臟」頁寫入磁碟。但不像Lazy Writer,Checkpoint對SQL Server的內存管理毫無興趣。所以CheckPoint也就意味著在這個點之前的所有修改都已經保存到了磁碟.這里要注意的是:CheckPoint會將所有緩沖區的臟頁寫入磁碟,不管臟頁中的數據是否已經Commit。這意味著有可能已經寫入磁碟的「臟頁」會在之後回滾(RollBack).不過不用擔心,如果數據回滾,SQL Server會將緩沖區內的頁再次修改,並寫入磁碟。通過CheckPoint的運作機制可以看出,CheckPoint的間歇(Recovery Interval)長短有可能會對性能產生影響。這個CheckPoint的間歇是一個伺服器級別的參數。可以通過sp_config進行配置,也可以在SSMS中進行配置:恢復間歇的默認參數是0,意味著由SQL Server來管理這個回復間隔。而自己設置恢復間隔也是需要根據具體情況來進行界定。
6. 郵件伺服器郵件存儲和日誌的介紹
本文以資料庫的基本原理為基礎,分析了EXCHANGE SERVER的存儲系統,並說明了各部分的作用。
一、IS服務和ESE的層次關系
IS服務是EXCHANGE伺服器中重要的服務之一,它控制著對郵箱和PF的存儲操作請求,EXCHANGE伺服器的存儲實際上是由ESE的資料庫引擎來管理的。這個ESE引擎是微軟專門為保存非關系型數據而開發的,目前在微軟的很多產品中都有廣泛的應用,如:AD資料庫、DHCP、WINS、SRS等等。
EXCHANGE的資料庫是由EDB文件、STM文件和LOG文件組成。在這些文件里,微軟使用了「B+樹」的內部數據結構。ESE的引擎的任務之一,就是當IS服務請求訪問資料庫的時候,把這些請求轉化為對內部數據結構的讀寫訪問。B+樹的特點是能夠對存儲在硬碟上的數據提供快速訪問能力。微軟利用「B+樹」作為ESE的後台結構的主要原因,就是盡可能的提高訪問數據時I/O性能。當然,這些結構對於EXCHANGE STORE來說是透明的。
另外,作為一個資料庫系統,ESE有責任提供事務級別的操作的支持,並維護資料庫的完整性和一致性。對資料庫系統而言,我們提到事務時,一般用ACID來描述事務的特點。
A--Atomic(原子的):事務必須是全或全無的操作,要麼全部成功更新,要麼全部不被更新
C--Consistent(一致的):一個成功提交的事務必須使資料庫處於一個一致的狀態。
I--Isolated(孤立的):所有未提交的更改都必須能夠和其他事務孤立。
D--Durable(持久的):當事務一旦提交,所做的更改必須存儲到穩定的介質上,防止系統失敗導致的資料庫不一致。(此點非常重要!!)
二、EXCHANGE 2000/2003存儲系統的新特點
在EX5.5中,ESE的版本為ESE97,而在EX2000/2003里,ESE版本已經升級ESE98了。ESE引起在以下方面得到了改進:
* I/O性能進一步提高和優化
* 對日誌文件增加了計算校驗操作
* 提高了ESEUTIL等工具的維護速度
而IS也在以下方面有了更新:
* 在每個SERVER上提供多個SG支持
* 資料庫STM文件格式的引入,提高了INTERNET郵件的性能
* WSS的引入,用戶可以使用多種協議訪問資料庫
三、EDB和STM的關系
常有人問,EDB文件是資料庫,那STM文件是做什麼用的?可以刪除嗎?
在EX5.5里,只有EDB文件,因為在EX5.5發布時,微軟主推的是內部郵件系統,因此其主要協議為MAPI,這是微軟的私有郵件西醫,EDB文件是專門為此協議優化過的。因此在EX5.5中,為了支持INTERNET郵件,必須在每次處理INTERNET郵件時,做一個格式轉換。這顯然帶來了性能的損失。
在EX2000里,微軟加大了對INTERNET郵件的支持,這就是STM文件的來源。MAPI格式是RPC和二進制標準的,而STM是純文本加上一些MIME編碼格式,這樣的區別使得它們不可能存儲在同一資料庫里。因此EX2000中,微軟開始使用EDB和STM兩個文件來分別保存兩種格式的郵件。並且在兩個文件之間建立了引用和關聯。對於用戶來說,它的郵箱實際上是跨越了EDB和STM文件共同組成的。另外,需要注意的是,EDB文件中還保留著用戶的郵箱結構。所以EDB文件更加重要。那麼EDB和STM是怎麼協同工作的呢?我們以幾個情景來分析之。
情景一:用戶使用OUTLOOK(MAPI)發送接收郵件
在該情景下,用戶將郵件通過MAPI協議提交給資料庫,直接被保存EDB文件中。當用戶通過MAPI訪問郵箱里的郵件時,如果被訪問的郵件在EDB里,直接返回,如果在STM里(如外來郵件),則執行轉換,將STM轉換為EDB文件格式,再返回用戶。
情景二:用戶使用標准SMTP/POP3/IMAP4等協議訪問
用戶使用非MAPI協議提交的郵件,內容保存在STM文件里,但是由於EDB里有郵箱結構,STM沒有,因此系統會把郵件的重要信息提取出來,放在EDB里。當用戶用MAPI提取郵件時,過程同上,當用戶通過標准協議訪問時,同樣需要進行格式轉換,轉換為STM文件格式返回。 這些轉換是在後台發生的。對用戶來說是透明的。通過上面的描述,你會看到,這兩個文件是緊密聯系的缺一不可。所以,在任何時間我們都不要單獨操作這兩個文件,它們是一個整體。同時也要注意的是,無論用戶使用何方式訪問郵箱,都需要向EDB文件請求郵箱結構信息,這是需要注意的。
四、LOG文件的重大作用
在論壇里經常會看到有人說我的硬碟怎麼很快就沒了,一看原來是日誌文件搞的鬼,於是就有人刪除日誌文件,甚至使用循環日誌來強制減少日誌,甚至有人提出這樣的疑問,日誌到底有什麼用?是不是多餘的'?那我們來看看日誌的重大作用。
對於一個SG來說,系統會產生一系列的日誌,這些日誌的擴展名為LOG,前綴一般是E00、E01……除了這些連續的日誌文件外,還有一些特殊的日誌文件(res1.log,res2.log,e0x.chk))),它們又有什麼用呢?我們的管理員通常不喜歡備份這一操作,因此對這些日誌是痛恨不已啊。那麼微軟在EXCHANGE資料庫系統中引入日誌的作用難道真的是多此一舉嗎?我們從以下幾個方面來考察一下日誌的作用:
1、作為一個企業級的郵件系統,必須要保證數據安全和完整。必須能夠面對隨時可能發生的意外災難,把數據損失降低到最小。
2、必須提供高性能的郵件處理能力,對資料庫中的郵件的事務操作在完成後必須馬上(或是說立即)被記錄在存儲介質上(見前面的事務持久性說明)
3、災難發生後,使用資料庫備份恢復必須要返回到災難發生前一刻的資料庫狀態(這是至關重要的!!)
現在我們來更進一步的看一下,當用戶要修改郵箱中的內容時,被修改的內容首先被提取出來放到內存中,實際的修改是發生在內存里的,這是眾所周知的,當修改完成後,這些內容必須被盡快寫回存儲介質,這樣才表示一個事務成功完成了。
從事務的描述中我們可以看到,事務是具有原子特性的,為了保證資料庫的一致和完整,事務必須全部成功或全部失敗,如果事務失敗,則必須回滾到事務開始的狀態。而當郵件在內存中修改完成後,此時事務並沒有完成(為什麼呢?)因為一旦系統崩潰,這些修改就丟失了。所以要確保事務修改完成,必須盡快將修改寫回到資料庫里去(也就是硬碟上)。這也是事務的持久性要求。注意,我們這里說的第一時間或是盡快,是一個什麼樣的概念。如果我們直接修改EDB文件,由於EDB
文件比較大,那麼在硬碟上修改一個大文件,就 需要花費大量的時間在等待和尋找數據存儲塊上(見操作系統原理),當系統出現高負載的繁忙狀態時,這將是一個非常大的瓶頸。也就無法做到「盡快」了。那怎麼辦呢?所以資料庫系統使用了日誌,而日誌通常很小(EXCHANGE的日誌只有5MB),向這些文件寫入修改結果是很快速的,因此當內存的修改完成後,這些結果就會立即寫入日誌中,以保證了事務的持久性。當成功寫入日誌後,該事務就成功完成了(現在在硬碟上了,不會因為當機丟失了)接下來,ESE引擎會在後台慢慢將這些日誌里的修改記錄寫回真正的資料庫里去(這對用戶來說已經不是那麼重要了),這就是日誌的第一個作用:確保事務在第一時間(盡可能快的)保存到非易失存儲器上(提供了事務持久性支持)。
根據上面的藐視,我們看到運行中的EXCHANGE資料庫,是由三個部分組成的:
* 內存中已經完成處理還沒有寫會到日誌里的內容(Dirt page)
* 還沒有寫到資料庫文件里的日誌內容
* EDB和STM資料庫文件
對於第一個部分,一旦掉電就回丟失的,是最不安全的。而對於第二部分的內容,系統通過檢查點文件(CHK)來標記哪些日誌已經被寫入資料庫了,而哪些還沒有。CHK文件類似一個指針。我們可以用「ESEUTIL /MK」來檢查CHK文件里的內容,在該命令的輸出中的checkpoint:<0x8,26d1,29>這樣的東西就是檢查點位置,它表示E0x00008的日誌的頁面序號已經被成功寫入資料庫了。大家可以自己看看。。:)
前面提到過,EXCHANGE系統在出現災難時,應能恢復到災難發生前的時刻的狀態。這是非常重要的。但即使是最勤快的管理員,也只能在指定的預定時間內做系統備份,而不可能時時刻刻的都在備份。那麼在備份完成後到災難發生之前的這段數據該如何保護呢?是不是就任由它丟失呢?顯然是不可能的。那答案是什麼呢?就是日誌文件。前面我們知道,任何對資料庫的更改都先寫入日誌里,再由日誌寫入資料庫,這樣我們只要找到日誌文件,就可以重新進行模擬的操作來完成備份後的資料庫文件的更改了,我們舉個例子來看看:
假設我們在凌晨3點完成了一次FULLBACKUP,備份完成後,系統正常運行,到下午4點的時候,系統突然崩潰。管理員用凌晨3點的數據恢復了資料庫,那麼從凌晨3點到下午4點這段時間的數據變更,就只能依賴於日誌了。當完成資料庫恢復後,系統會自動的跟蹤到關聯的日誌文件,如果發現有比當前資料庫還新的日誌存在,系統就會自動的按照日誌的順序將更改寫回到資料庫中去。因此這樣一來,從凌晨3點到下午4點的數據變更就被完整的恢復了。這就是日誌的第二個作用:保證系統備份和恢復的完整性。當然前提是沒有使用循環日誌!!(看到了吧,使用循環日誌的危害是相當大的,比起你的數據來說,多做幾次備份不是沒有意義的吧?
說到這里,有人可能要問,如果資料庫和日誌同時損壞,如何辦?答案是:盡量避免這樣的情況發生。首先資料庫損壞的幾率要大於日誌,另外,微軟建議將資料庫和日誌分別存儲在不同的磁碟上,要是這樣還會同時壞,那就沒有辦法了,呵呵。。對於管理員對日誌文件的抱怨,合理的解決方法是定期做備份。啟用循環日誌是不正確的做法,當啟用循環日誌後,一旦系統發生災難恢復,將有可能不能將系統恢復到災難發生時的狀態,磁碟和數據誰更重要,管理員自己要考慮考慮了。
五、ESE與IS服務的啟動和關閉
ESE引擎在載入資料庫文件時,會去檢查資料庫文件的標志。這個標志保留了上次關閉資料庫的狀態,當狀態為正常關閉說,系統將直接載入該資料庫,當資料庫標志為非正常關閉時,系統將先進行一個軟恢復過程(你可以在事件里看到它),然後再載入。
那麼,正常關閉和非正常關閉有什麼區別呢?一個正常關閉的資料庫,表示所有的日誌信息都已經正確的寫入資料庫了。反之一個非正常關閉的資料庫,則表示至少有一部分數據未能正確的從日誌寫入資料庫。要注意的是,非正常關閉的資料庫並不等於已經被破壞的資料庫。只表示有數據沒有提交到資料庫文件。
使用ESEUTIL/MH命令可以看到資料庫的該狀態,其中的STATE欄位標記的就是這個狀態,「CLEANSHUTDOWN」表示資料庫正常關閉。當系統載入處於非正常關閉的資料庫時,就會根據檢查點文件確定日誌文件的位置,並做重放操作。當檢查點文件丟失或損壞時,系統將從最早的日誌文件開始處理。有的時候,系統不能自動的修復資料庫,這時我們也可以用「ESEUTIL /R」命令手工的恢復處於非正常關閉狀態的資料庫。強烈推薦在系統異常關閉後執行此命令。在執行前最好前確定資料庫文件的狀態確實為非正常關閉,不要對正常關閉的資料庫執行該恢復命令!
由此可見,EXCHANGE系統對資料庫有自我修復能力,能確保系統在發生意外後恢復正確的狀態。但這並不是說我們可以隨意的關閉系統,仍要UPS等必要的保護措施。
六、關於M盤
在EX2000里,有一個M盤的映射。這個映射只是提供開發人員通過API訪問郵箱和郵件用的。因此對M盤的手工操作都可能帶來資料庫的破壞,請注意,另外,有一種觀點認為備份了M盤就備份了郵件,這是絕對錯誤的。M盤雖然是資料庫的映射,但已經去掉了很多的關聯和內在聯系。因此備份M盤是不能恢復資料庫的。所有的EXCHANGE管理員必須按規定認真的備份系統狀態和SG。切不可偷懶哦。
7. mysql 核心內容-上
1、SQL語句執行流程
MySQL大體上可分為Server層和存儲引擎層兩部分。
Server層:
連接器:TCP握手後伺服器來驗證登陸用戶身份,A用戶創建連接後,管理員對A用戶許可權修改了也不會影響到已經創建的鏈接許可權,必須重新登陸。
查詢緩存:查詢後的結果存儲位置,MySQL8.0版本以後已經取消,因為查詢緩存失效太頻繁,得不償失。
分析器:根據語法規則,判斷你輸入的這個SQL語句是否滿足MySQL語法。
優化器:多種執行策略可實現目標,系統自動選擇最優進行執行。
執行器:判斷是否有許可權,將最終任務提交到存儲引擎。
存儲引擎層
負責數據的存儲和提取。其架構模式是插件式的,支持InnoDB、MyISAM、Memory等多個存儲引擎。現在最常用的存儲引擎是InnoDB,它從MySQL 5.5.5版本開始成為了默認存儲引擎(經常用的也是這個)。
SQL執行順序
2、BinLog、RedoLog、UndoLog
BinLog
BinLog是記錄所有資料庫表結構變更(例如create、alter table)以及表數據修改(insert、update、delete)的二進制日誌,主從資料庫同步用到的都是BinLog文件。BinLog日誌文件有三種模式。
STATEMENT 模式
內容:binlog 記錄可能引起數據變更的 sql 語句
優勢:該模式下,因為沒有記錄實際的數據,所以日誌量很少 IO 都消耗很低,性能是最優的
劣勢:但有些操作並不是確定的,比如 uuid() 函數會隨機產生唯一標識,當依賴 binlog 回放時,該操作生成的數據與原數據必然是不同的,此時可能造成無法預料的後果。
ROW 模式
內容:在該模式下,binlog 會記錄每次操作的源數據與修改後的目標數據,StreamSets就要求該模式。
優勢:可以絕對精準的還原,從而保證了數據的安全與可靠,並且復制和數據恢復過程可以是並發進行的
劣勢:缺點在於 binlog 體積會非常大,同時,對於修改記錄多、欄位長度大的操作來說,記錄時性能消耗會很嚴重。閱讀的時候也需要特殊指令來進行讀取數據。
MIXED 模式
內容:是對上述STATEMENT 跟 ROW 兩種模式的混合使用。
細節:對於絕大部分操作,都是使用 STATEMENT 來進行 binlog 沒有記錄,只有以下操作使用 ROW 來實現:表的存儲引擎為 NDB,使用了uuid() 等不確定函數,使用了 insert delay 語句,使用了臨時表
主從同步流程:
1、主節點必須啟用二進制日誌,記錄任何修改了資料庫數據的事件。
2、從節點開啟一個線程(I/O Thread)把自己扮演成 mysql 的客戶端,通過 mysql 協議,請求主節點的二進制日誌文件中的事件 。
3、主節點啟動一個線程(mp Thread),檢查自己二進制日誌中的事件,跟對方請求的位置對比,如果不帶請求位置參數,則主節點就會從第一個日誌文件中的第一個事件一個一個發送給從節點。
4、從節點接收到主節點發送過來的數據把它放置到中繼日誌(Relay log)文件中。並記錄該次請求到主節點的具體哪一個二進制日誌文件內部的哪一個位置(主節點中的二進制文件會有多個)。
5、從節點啟動另外一個線程(sql Thread ),把 Relay log 中的事件讀取出來,並在本地再執行一次。
mysql默認的復制方式是非同步的,並且復制的時候是有並行復制能力的。主庫把日誌發送給從庫後不管了,這樣會產生一個問題就是假設主庫掛了,從庫處理失敗了,這時候從庫升為主庫後,日誌就丟失了。由此產生兩個概念。
全同步復制
主庫寫入binlog後強制同步日誌到從庫,所有的從庫都執行完成後才返回給客戶端,但是很顯然這個方式的話性能會受到嚴重影響。
半同步復制
半同步復制的邏輯是這樣,從庫寫入日誌成功後返回ACK確認給主庫,主庫收到至少一個從庫的確認就認為寫操作完成。
還可以延伸到由於主從配置不一樣、主庫大事務、從庫壓力過大、網路震盪等造成主備延遲,如何避免這個問題?主備切換的時候用可靠性優先原則還是可用性優先原則?如何判斷主庫Crash了?互為主備的情況下如何避免主備循環復制?被刪庫跑路了如何正確恢復?( o )… 感覺越來越扯到DBA的活兒上去了。
RedoLog
可以先通過下面demo理解:
飯點記賬可以把賬單寫在賬本上也可以寫在粉板上。有人賒賬或者還賬的話,一般有兩種做法:
1、直接把賬本翻出來,把這次賒的賬加上去或者扣除掉。
2、先在粉板上記下這次的賬,等打烊以後再把賬本翻出來核算。
生意忙時選後者,因為前者太麻煩了。得在密密麻麻的記錄中找到這個人的賒賬總額信息,找到之後再拿出算盤計算,最後再將結果寫回到賬本上。
同樣在MySQL中如果每一次的更新操作都需要寫進磁碟,然後磁碟也要找到對應的那條記錄,然後再更新,整個過程IO成本、查找成本都很高。而粉板和賬本配合的整個過程就是MySQL用到的是Write-Ahead Logging 技術,它的關鍵點就是先寫日誌,再寫磁碟。此時賬本 = BinLog,粉板 = RedoLog。
1、 記錄更新時,InnoDB引擎就會先把記錄寫到RedoLog(粉板)裡面,並更新內存。同時,InnoDB引擎會在空閑時將這個操作記錄更新到磁碟裡面。
2、 如果更新太多RedoLog處理不了的時候,需先將RedoLog部分數據寫到磁碟,然後擦除RedoLog部分數據。RedoLog類似轉盤。
RedoLog有write pos 跟checkpoint
write pos :是當前記錄的位置,一邊寫一邊後移,寫到第3號文件末尾後就回到0號文件開頭。
check point:是當前要擦除的位置,也是往後推移並且循環的,擦除記錄前要把記錄更新到數據文件。
write pos和check point之間的是粉板上還空著的部分,可以用來記錄新的操作。如果write pos追上checkpoint,表示粉板滿了,這時候不能再執行新的更新,得停下來先擦掉一些記錄,把checkpoint推進一下。
有了redo log,InnoDB就可以保證即使資料庫發生異常重啟,之前提交的記錄都不會丟失,這個能力稱為crash-safe。 redolog兩階段提交:為了讓binlog跟redolog兩份日誌之間的邏輯一致。提交流程大致如下:
1 prepare階段 --> 2 寫binlog --> 3 commit
當在2之前崩潰時,重啟恢復後發現沒有commit,回滾。備份恢復:沒有binlog 。一致
當在3之前崩潰時,重啟恢復發現雖沒有commit,但滿足prepare和binlog完整,所以重啟後會自動commit。備份:有binlog. 一致
binlog跟redolog區別:
redo log是InnoDB引擎特有的;binlog是MySQL的Server層實現的,所有引擎都可以使用。
redo log是物理日誌,記錄的是在某個數據頁上做了什麼修改;binlog是邏輯日誌,記錄的是這個語句的原始邏輯,比如給ID=2這一行的c欄位加1。
redo log是循環寫的,空間固定會用完;binlog是可以追加寫入的。追加寫是指binlog文件寫到一定大小後會切換到下一個,並不會覆蓋以前的日誌。
UndoLog
UndoLog 一般是邏輯日誌,主要分為兩種:
insert undo log
代表事務在insert新記錄時產生的undo log, 只在事務回滾時需要,並且在事務提交後可以被立即丟棄
update undo log
事務在進行update或delete時產生的undo log; 不僅在事務回滾時需要,在快照讀時也需要;所以不能隨便刪除,只有在快速讀或事務回滾不涉及該日誌時,對應的日誌才會被purge線程統一清除
3、MySQL中的索引
索引的常見模型有哈希表、有序數組和搜索樹。
哈希表:一種以KV存儲數據的結構,只適合等值查詢,不適合范圍查詢。
有序數組:只適用於靜態存儲引擎,涉及到插入的時候比較麻煩。可以參考Java中的ArrayList。
搜索樹:按照數據結構中的二叉樹來存儲數據,不過此時是N叉樹(B+樹)。廣泛應用在存儲引擎層中。
B+樹比B樹優勢在於:
B+ 樹非葉子節點存儲的只是索引,可以存儲的更多。B+樹比B樹更加矮胖,IO次數更少。
B+ 樹葉子節點前後管理,更加方便范圍查詢。同時結果都在葉子節點,查詢效率穩定。
B+樹中更有利於對數據掃描,可以避免B樹的回溯掃描。
索引的優點:
1、唯一索引可以保證每一行數據的唯一性
2、提高查詢速度
3、加速表與表的連接
4、顯著的減少查詢中分組和排序的時間
5、通過使用索引,可以在查詢的過程中,使用優化隱藏器,提高系統的性能。
索引的缺點:
1、創建跟維護都需要耗時
2、創建索引時,需要對表加鎖,在鎖表的同時,可能會影響到其他的數據操作
3、 索引需要磁碟的空間進行存儲,磁碟佔用也很快。
4、當對表中的數據進行CRUD的時,也會觸發索引的維護,而維護索引需要時間,可能會降低數據操作性能
索引設計的原則不應該:
1、索引不是越多越好。索引太多,維護索引需要時間跟空間。
2、 頻繁更新的數據,不宜建索引。
3、數據量小的表沒必要建立索引。
應該:
1、重復率小的列建議生成索引。因為重復數據少,索引樹查詢更有效率,等價基數越大越好。
2、數據具有唯一性,建議生成唯一性索引。在資料庫的層面,保證數據正確性
3、頻繁group by、order by的列建議生成索引。可以大幅提高分組和排序效率
4、經常用於查詢條件的欄位建議生成索引。通過索引查詢,速度更快
索引失效的場景
1、模糊搜索:左模糊或全模糊都會導致索引失效,比如'%a'和'%a%'。但是右模糊是可以利用索引的,比如'a%' 。
2、隱式類型轉換:比如select * from t where name = xxx , name是字元串類型,但是沒有加引號,所以是由MySQL隱式轉換的,所以會讓索引失效 3、當語句中帶有or的時候:比如select * from t where name=『sw』 or age=14
4、不符合聯合索引的最左前綴匹配:(A,B,C)的聯合索引,你只where了C或B或只有B,C
關於索引的知識點:
主鍵索引:主鍵索引的葉子節點存的是整行數據信息。在InnoDB里,主鍵索引也被稱為聚簇索引(clustered index)。主鍵自增是無法保證完全自增的哦,遇到唯一鍵沖突、事務回滾等都可能導致不連續。
唯一索引:以唯一列生成的索引,該列不允許有重復值,但允許有空值(NULL)
普通索引跟唯一索引查詢性能:InnoDB的數據是按數據頁為單位來讀寫的,默認每頁16KB,因此這兩種索引查詢數據性能差別微乎其微。
change buffer:普通索引用在更新過程的加速,更新的欄位如果在緩存中,如果是普通索引則直接更新即可。如果是唯一索引需要將所有數據讀入內存來確保不違背唯一性,所以盡量用普通索引。
非主鍵索引:非主鍵索引的葉子節點內容是主鍵的值。在InnoDB里,非主鍵索引也被稱為二級索引(secondary index)
回表:先通過資料庫索引掃描出數據所在的行,再通過行主鍵id取出索引中未提供的數據,即基於非主鍵索引的查詢需要多掃描一棵索引樹。
覆蓋索引:如果一個索引包含(或者說覆蓋)所有需要查詢的欄位的值,我們就稱之為覆蓋索引。
聯合索引:相對單列索引,組合索引是用多個列組合構建的索引,一次性最多聯合16個。
最左前綴原則:對多個欄位同時建立的組合索引(有順序,ABC,ACB是完全不同的兩種聯合索引) 以聯合索引(a,b,c)為例,建立這樣的索引相當於建立了索引a、ab、abc三個索引。另外組合索引實際還是一個索引,並非真的創建了多個索引,只是產生的效果等價於產生多個索引。
索引下推:MySQL 5.6引入了索引下推優化,可以在索引遍歷過程中,對索引中包含的欄位先做判斷,過濾掉不符合條件的記錄,減少回表字數。
索引維護:B+樹為了維護索引有序性涉及到頁分裂跟頁合並。增刪數據時需考慮頁空間利用率。
自增主鍵:一般會建立與業務無關的自增主鍵,不會觸發葉子節點分裂。
延遲關聯:通過使用覆蓋索引查詢返回需要的主鍵,再根據主鍵關聯原表獲得需要的數據。
InnoDB存儲: * .frm文件是一份定義文件,也就是定義資料庫表是一張怎麼樣的表。*.ibd文件則是該表的索引,數據存儲文件,既該表的所有索引樹,所有行記錄數據都存儲在該文件中。
MyISAM存儲:* .frm文件是一份定義文件,也就是定義資料庫表是一張怎麼樣的表。* .MYD文件是MyISAM存儲引擎表的所有行數據的文件。* .MYI文件存放的是MyISAM存儲引擎表的索引相關數據的文件。MyISAM引擎下,表數據和表索引數據是分開存儲的。
MyISAM查詢:在MyISAM下,主鍵索引和輔助鍵索引都屬於非聚簇索引。查詢不管是走主鍵索引,還是非主鍵索引,在葉子結點得到的都是目的數據的地址,還需要通過該地址,才能在數據文件中找到目的數據。
PS:InnoDB支持聚簇索引,MyISAM不支持聚簇索引
4、SQL事務隔離級別
ACID的四個特性
原子性(Atomicity):把多個操作放到一個事務中,保證這些操作要麼都成功,要麼都不成功
一致性(Consistency):理解成一串對數據進行操作的程序執行下來,不會對數據產生不好的影響,比如憑空產生,或消失
隔離性(Isolation,又稱獨立性):隔離性的意思就是多個事務之間互相不幹擾,即使是並發事務的情況下,他們只是兩個並發執行沒有交集,互不影響的東西;當然實現中,也不一定需要這么完整隔離性,即不一定需要這么的互不幹擾,有時候還是允許有部分干擾的。所以MySQL可以支持4種事務隔離性
持久性(Durability):當某個操作操作完畢了,那麼結果就是這樣了,並且這個操作會持久化到日誌記錄中
PS:ACID中C與CAP定理中C的區別
ACID的C著重強調單資料庫事務操作時,要保證數據的完整和正確性,數據不會憑空消失跟增加。CAP 理論中的C指的是對一個數據多個備份的讀寫一致性
事務操作可能會出現的數據問題
1、臟讀(dirty read):B事務更改數據還未提交,A事務已經看到並且用了。B事務如果回滾,則A事務做錯了
2、 不可重復讀(non-repeatable read):不可重復讀的重點是修改: 同樣的條件, 你讀取過的數據, 再次讀取出來發現值不一樣了,只需要鎖住滿足條件的記錄
3、 幻讀(phantom read):事務A先修改了某個表的所有紀錄的狀態欄位為已處理,未提交;事務B也在此時新增了一條未處理的記錄,並提交了;事務A隨後查詢記錄,卻發現有一條記錄是未處理的造成幻讀現象,幻讀僅專指新插入的行。幻讀會造成語義上的問題跟數據一致性問題。
4、 在可重復讀RR隔離級別下,普通查詢是快照讀,是不會看到別的事務插入的數據的。因此,幻讀在當前讀下才會出現。要用間隙鎖解決此問題。
在說隔離級別之前,你首先要知道,你隔離得越嚴實,效率就會越低。因此很多時候,我們都要在二者之間尋找一個平衡點。SQL標準的事務隔離級別由低到高如下: 上圖從上到下的模式會導致系統的並行性能依次降低,安全性依次提高。
讀未提交:別人改數據的事務尚未提交,我在我的事務中也能讀到。
讀已提交(Oracle默認):別人改數據的事務已經提交,我在我的事務中才能讀到。
可重復讀(MySQL默認):別人改數據的事務已經提交,我在我的事務中也不去讀,以此保證重復讀一致性。
串列:我的事務尚未提交,別人就別想改數據。
標准跟實現:上面都是關於事務的標准,但是每一種資料庫都有不同的實現,比如MySQL InnDB 默認為RR級別,但是不會出現幻讀。因為當事務A更新了所有記錄的某個欄位,此時事務A會獲得對這個表的表鎖,因為事務A還沒有提交,所以事務A獲得的鎖沒有釋放,此時事務B在該表插入新記錄,會因為無法獲得該表的鎖,則導致插入操作被阻塞。只有事務A提交了事務後,釋放了鎖,事務B才能進行接下去的操作。所以可以說 MySQL的RR級別的隔離是已經實現解決了臟讀,不可重復讀和幻讀的。
5、MySQL中的鎖
無論是Java的並發編程還是資料庫的並發操作都會涉及到鎖,研發人員引入了悲觀鎖跟樂觀鎖這樣一種鎖的設計思想。
悲觀鎖:
優點:適合在寫多讀少的並發環境中使用,雖然無法維持非常高的性能,但是在樂觀鎖無法提更好的性能前提下,可以做到數據的安全性
缺點:加鎖會增加系統開銷,雖然能保證數據的安全,但數據處理吞吐量低,不適合在讀書寫少的場合下使用
樂觀鎖:
優點:在讀多寫少的並發場景下,可以避免資料庫加鎖的開銷,提高DAO層的響應性能,很多情況下ORM工具都有帶有樂觀鎖的實現,所以這些方法不一定需要我們人為的去實現。
缺點:在寫多讀少的並發場景下,即在寫操作競爭激烈的情況下,會導致CAS多次重試,沖突頻率過高,導致開銷比悲觀鎖更高。
實現:資料庫層面的樂觀鎖其實跟CAS思想類似, 通數據版本號或者時間戳也可以實現。
資料庫並發場景主要有三種:
讀-讀:不存在任何問題,也不需要並發控制
讀-寫:有隔離性問題,可能遇到臟讀,幻讀,不可重復讀
寫-寫:可能存更新丟失問題,比如第一類更新丟失,第二類更新丟失
兩類更新丟失問題:
第一類更新丟失:事務A的事務回滾覆蓋了事務B已提交的結果 第二類更新丟失:事務A的提交覆蓋了事務B已提交的結果
為了合理貫徹落實鎖的思想,MySQL中引入了雜七雜八的各種鎖:
鎖分類
MySQL支持三種層級的鎖定,分別為
表級鎖定
MySQL中鎖定粒度最大的一種鎖,最常使用的MYISAM與INNODB都支持表級鎖定。
頁級鎖定
是MySQL中鎖定粒度介於行級鎖和表級鎖中間的一種鎖,表級鎖速度快,但沖突多,行級沖突少,但速度慢。所以取了折衷的頁級,一次鎖定相鄰的一組記錄。
行級鎖定
Mysql中鎖定粒度最細的一種鎖,表示只針對當前操作的行進行加鎖。行級鎖能大大減少資料庫操作的沖突。其加鎖粒度最小,但加鎖的開銷也最大行級鎖不一定比表級鎖要好:鎖的粒度越細,代價越高,相比表級鎖在表的頭部直接加鎖,行級鎖還要掃描找到對應的行對其上鎖,這樣的代價其實是比較高的,所以表鎖和行鎖各有所長。
MyISAM中的鎖
雖然MySQL支持表,頁,行三級鎖定,但MyISAM存儲引擎只支持表鎖。所以MyISAM的加鎖相對比較開銷低,但數據操作的並發性能相對就不高。但如果寫操作都是尾插入,那還是可以支持一定程度的讀寫並發
從MyISAM所支持的鎖中也可以看出,MyISAM是一個支持讀讀並發,但不支持通用讀寫並發,寫寫並發的資料庫引擎,所以它更適合用於讀多寫少的應用場合,一般工程中也用的較少。
InnoDB中的鎖
該模式下支持的鎖實在是太多了,具體如下:
共享鎖和排他鎖 (Shared and Exclusive Locks)
意向鎖(Intention Locks)
記錄鎖(Record Locks)
間隙鎖(Gap Locks)
臨鍵鎖 (Next-Key Locks)
插入意向鎖(Insert Intention Locks)
主鍵自增鎖 (AUTO-INC Locks)
空間索引斷言鎖(Predicate Locks for Spatial Indexes)
舉個栗子,比如行鎖里的共享鎖跟排它鎖:lock in share modle 共享讀鎖:
為了確保自己查到的數據沒有被其他的事務正在修改,也就是說確保查到的數據是最新的數據,並且不允許其他人來修改數據。但是自己不一定能夠修改數據,因為有可能其他的事務也對這些數據使用了 in share mode 的方式上了S 鎖。如果不及時的commit 或者rollback 也可能會造成大量的事務等待。
for update排它寫鎖:
為了讓自己查到的數據確保是最新數據,並且查到後的數據只允許自己來修改的時候,需要用到for update。相當於一個 update 語句。在業務繁忙的情況下,如果事務沒有及時的commit或者rollback 可能會造成其他事務長時間的等待,從而影響資料庫的並發使用效率。
Gap Lock間隙鎖:
1、行鎖只能鎖住行,如果在記錄之間的間隙插入數據就無法解決了,因此MySQL引入了間隙鎖(Gap Lock)。間隙鎖是左右開區間。間隙鎖之間不會沖突。
2、間隙鎖和行鎖合稱NextKeyLock,每個NextKeyLock是前開後閉區間。
間隙鎖加鎖原則(學完忘那種):
1、加鎖的基本單位是 NextKeyLock,是前開後閉區間。
2、查找過程中訪問到的對象才會加鎖。
3、索引上的等值查詢,給唯一索引加鎖的時候,NextKeyLock退化為行鎖。
4、索引上的等值查詢,向右遍歷時且最後一個值不滿足等值條件的時候,NextKeyLock退化為間隙鎖。
5、唯一索引上的范圍查詢會訪問到不滿足條件的第一個值為止。