您好,登錄后才能下訂單哦!
這篇文章將為大家詳細講解有關數據庫知識都有哪些,文章內容質量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關知識有一定的了解。
大多數計算機系統都是有狀態的,并且可能會依賴存儲系統。隨著時間的推移,我對數據庫的了解程度不斷加深,這是以我們的設計錯誤導致數據丟失和中斷為代價。在數據量很大的系統中,數據庫是系統設計目標的核心。盡管開發人員不可能對數據庫一無所知,但他們所預見和所經歷的問題往往只是冰山一角。我將分享一些見解,這些見解對于不擅長數據庫領域的開發人員來說非常有用。
現如今,一方面人們認為網絡很可靠,一方面由于網絡中斷而導致系統宕機的情況卻又很普遍。這方面的研究工作并不多,而且通常由大公司主導,而這些公司使用了配備定制硬件的專用網絡和專門的工作人員。
谷歌服務的可用性為 99.999%,他們聲稱只有 7.6% 的 Spanner(谷歌的分布式數據庫) 問題是因為網絡導致的,盡管他們一直認為專用網絡是其可用性背后的核心支撐。2014 年,Bailis 和 Kingsbury 的一份調查報告對 Peter Deutsch 在 1994 年提出的分布式計算謬論之一提出了挑戰——網絡真的可靠嗎?
我們無法進行全面的調查,供應商們也不會提供足夠的數據來說明有多少客戶的問題是因為網絡導致的。我們經常會遭遇大型云供應商網絡發生宕機,導致部分網絡癱瘓數小時,這些事件有大量可見的受影響客戶,還有很多是我們看不到的。網絡中斷可能會影響到更多方面,盡管并非所有事件都產生了很大的影響。云計算客戶也不一定能看到這些問題所在。當問題出現時,他們不太可能認為與供應商的網絡錯誤有關。對他們來說,第三方服務就是黑盒。如果你不是供應商,要估計出真實的影響程度是不太可能的。
與供應商的報告相比,如果你的系統只有一小部分宕機與網絡問題有關,那你是幸運的。網絡仍然受傳統問題的影響,比如硬件故障、拓撲變更、管理配置變更和電源故障。但最近我才知道,一些新發現的問題(比如鯊魚咬斷海底光纜)也成了主要影響因素。
ACID 代表原子性、一致性、隔離性和持久性。即使在發生崩潰、錯誤、硬件故障等類似事件時,數據庫也需要保證這些屬性是有效的。大多數關系型事務數據庫都盡量提供 ACID 保證,但很多 NoSQL 數據庫是沒有 ACID 事務保證的,因為實現成本很高。
在我剛進入這個行業時,我們的技術主管懷疑 ACID 是不是一個過時的概念。可以說,ACID 被認為是一個種泛泛而談的概念,而不是一個嚴格的執行標準。現在,我發現它非常有用,因為它提供了一類問題和一類潛在的解決方案。
并不是每個數據庫都兼容 ACID,而且在兼容 ACID 的數據庫當中,對 ACID 的解釋也可能存在差異。之所以存在差異,其中一個原因是在實現 ACID 時涉及的權衡程度的不同。數據庫可能宣稱自己兼容 ACID,但對于一些邊緣情況,或者在面對“不太可能”出現的問題時,處理方式有所不同。
MongoDB 的 ACID 表現一直飽受爭議,即使是在發布了 v4 版本之后。MongoDB 在很長一段時間內都不支持日志記錄。對于下面這種情況,應用程序進行了兩次寫操作 (w1 和 w2),MongoDB 能夠持久化 w1,但因為發生硬件故障,導致無法持久化 w2。
MongoDB 在將數據寫入物理磁盤之前發生崩潰,造成數據丟失
將數據提交到磁盤是一個開銷很大的過程,它們聲稱寫入性能良好,卻是以避免頻繁提交數據為代價,從而犧牲了持久性。現在,MongoDB 有了日志記錄,但臟寫仍然會影響數據的持久性,因為默認情況下每 100 毫秒才提交一次日志。即使風險大大降低,日記記錄的持久性和變更仍然有可能出現同樣的問題。
在 ACID 這幾個屬性中,一致性和隔離級別的實現方式是最多的,因為權衡范圍最大。為了保持數據一致性,數據庫需要進行協調,爭用資源的情況會增加。當需要在多個數據中心之間進行水平伸縮時 (特別是在不同的地理區域之間),就變得非常困難。隨著可用性的降低和網絡分區的頻繁出現,提供高水平的一致性是非常困難的。關于這一問題的深入解釋,請參見 CAP 定理。不過需要注意的是,應用程序可以在數據一致性方面做一些處理,或者程序員可能對這個問題有足夠的了解,可以在應用程序中添加額外的邏輯來處理,而不是嚴重依賴數據庫。
數據庫通常會提供各種隔離級別,應用程序開發人員可以根據權衡選擇最經濟有效的隔離級別。較弱的隔離級別可能速度更快,但可能會引入數據競態問題。更強的隔離級別消除了一些潛在的數據競態問題,但速度較慢,并且可能會引入資源爭用,使數據庫慢到宕機。
現有并發模型及其之間關系的概覽
SQL 標準只定義了 4 個隔離級別,盡管在理論方面和實際當中都還有更多可用的級別。如果你想進一步了解,jepson.io 提供了更多對現有并發模型的介紹。谷歌的 Spanner 保證了時鐘同步的外部串行性,即使這是一個更嚴格的隔離級別,但它在標準隔離級別中并沒有定義。
SQL 標準中提到的隔離級別是:
串行化 (最嚴格、成本最高):串行化執行的效果與事務的串行執行是一樣的。串行執行是指每個事務在下一個事務開始之前執行完成。需要注意的是,由于在解釋上的差異,串行化通常被實現成“快照隔離”(例如 Oracle),但在 SQL 標準中并沒有“快照隔離”。
可重復讀:當前事務中未提交的讀取對當前事務可見,但其他事務所做的更改 (如新插入的行) 不可見。
讀已提交:未提交的讀取對事務不可見。只有提交的寫是可見的,但可能會發生幻讀取。如果另一個事務插入和提交新行,當前事務在查詢時可以看到它們。
讀未提交 (最不嚴格、成本最低):允許臟讀,事務可以看到其他事務未提交的更改。實際上,這個級別對于返回近似聚合很有用,比如 COUNT(*) 查詢。
串行化級別將發生數據競爭的機會降到最低,盡管它的開銷最大,并給系統帶來了最多的爭用。其他隔離級別開銷較小,但增加了數據競爭的可能性。有些數據庫允許設置隔離級別,有些數據庫不一定支持所有的隔離級別。
各種數據庫對隔離級別的支持情況
使用數據庫鎖的成本是非常高的,它們不僅引入了更多的爭用,而且要求應用程序服務器和數據庫之間保持穩定的連接。排它鎖受網絡分區的影響更大,并會導致難以識別和解決的死鎖。在這種情況下,可以考慮使用樂觀鎖。
樂觀鎖是指在讀取一行數據時,記下它的版本號、最近修改的時間戳或校驗和。然后,你可以在修改記錄之前檢查版本有沒有發生變化。
UPDATE products SET name = 'Telegraph receiver', version = 2 WHERE id = 1 AND version = 1
如果之前有一個更新操作修改了 products 表,那么當前的更新操作將不修改任何數據。如果之前沒有被修改,當前的更新操作將修改一行數據。
在討論數據一致性時,我們主要關注可能會導致臟讀和數據丟失的競態條件。但除了這些,我們還要注意異常數據。
這類異常的一個例子是寫傾斜(write skew)。寫傾斜并不是在進行寫操作時發生臟讀或數據丟失時出現的,而是在數據的邏輯約束被破壞時出現的。
例如,假設有一個監控應用程序,要求至少有一個運維人員可以隨叫隨到。
對于上述情況,如果兩個事務成功提交,就會出現寫傾斜。即使沒有發生臟讀或數據丟失,數據的完整性也會丟失,因為有兩個人被指派隨叫隨到。
串行化化隔離級別、模式設計或數據庫約束可能有助于消除寫傾斜。開發人員需要在開發期間識別出這些異常,避免在生產環境中出現這個問題。話雖如此,直接從代碼中識別出寫傾斜是非常困難的。特別是在大型的系統中,如果不同的團隊使用相同的表,但沒有相互溝通,也沒有檢查如何訪問數據,就更難發現問題了。
數據庫提供的核心功能之一是順序保證,但這也是讓應用程序開發人員感到驚訝的一個地方。數據庫按照接收事務的順序來安排順序,而不是按照代碼中所寫的事務順序來安排順序。事務執行的順序很難預測,特別是在大規模并發系統中。
在開發過程中,特別是在使用非阻塞開發庫時,糟糕的可讀性可能會導致出現這樣的問題:用戶認為事務是按順序執行的,但事務可能以任意順序到達數據庫。下面的代碼看起來像是要順序地調用 T1 和 T2,但如果這些函數是非阻塞的,并且會立即返回 promise,那么實際的調用順序將由它們到達數據庫的時間決定。
result1 = T1() // 返回的是promise result2 = T2()
如果原子性是必需的 (完全提交或中止所有操作),而且順序很重要,那么 T1 和 T2 應該包含在單個數據庫事務中。
分片是對數據庫進行水平分區的一種方法。盡管有些數據庫可以自動對數據進行水平分區,但有些數據庫不會這么做,或者可能不擅長這么做。當數據架構師或開發人員能夠預測數據的訪問模式時,他們可能會在用戶端進行水平分區,而不是在數據庫端,這叫作應用程序級別的分片。
“應用程序級別的分片”通常給人一種錯誤的印象,即認為分片應該存在于應用程序中。實際上,分片功能可以作為數據庫前面的一個層。隨著數據增長和模式的迭代,分片需求可能會變得越來越復雜。
一個應用服務器與分片服務分離的示例架構
將分片作為單獨的服務,可以在不重新部署應用程序的前提下提升分片策略的迭代能力。Vitess 是這方面的一個很好的例子。Vitess 為 MySQL 提供了水平分片能力,客戶端可以通過 MySQL 協議連接到 Vitess,Vitess 會在各個 MySQL 節點上對數據進行分片。
https://youtu.be/OCS45iy5v1M?t=204
自動遞增是生成主鍵的常用方法。使用數據庫作為 ID 生成器,并在數據庫中創建帶有 ID 生成的表,這種情況并不少見。但是,通過自動遞增生成主鍵可能不是理想的方法,原因如下:
在分布式數據庫系統中,自動遞增是一個難題。你需要一個全局鎖來生成 ID,但如果可以生成 UUID,就不需要協調數據庫節點。使用帶鎖的自動遞增可能會引入爭用,并且可能會顯著降低分布式寫入性能。像 MySQL 這樣的數據庫可能需要特定的配置,并且要保證主主復制的正確性。但是,配置很容易出錯,并可能導致寫入中斷。
一些數據庫有基于主鍵的分區算法。順序 ID 可能會導致不可預測的熱點,導致某些分區數據量過大,而其他分區處于空閑狀態。
訪問數據庫最快方法是使用主鍵。如果你使用了其他列來標識記錄,那么順序 ID 可能會變得毫無意義。所以,請盡可能選擇一個全局唯一的自然主鍵 (例如用戶名)。
在決定哪種方法更適合自己之前,請考慮一下自動遞增 ID 與 UUID 對索引、分區和分片的影響。
多版本并發控制 (MVCC) 可以支持上述的很多一致性方面的能力。一些數據庫 (如 Postgres、Spanner) 借助 MVCC 讓每個事務可以查看快照,即數據庫的舊版本。這些事務可以串行化,以此來保持一致性。從舊快照讀取數據時,讀取的是陳舊的數據。
讀取稍微陳舊一點的數據也是很有用的,例如,基于數據生成分析報告或計算近似聚合值。
讀取陳舊數據的第一個好處是延時 (特別是當數據庫分布在不同的地理區域時)。MVCC 數據庫的第二個優點是它允許只讀事務是無鎖的。如果讀取陳舊數據是可接受的,那么對于偏重讀取很大的應用程序來說,這就是一個主要的優點。
應用服務器從本地副本讀取 5 秒前的陳舊數據,即使在太平洋的另一端有可用的最新版本
數據庫會自動清除舊版本,在某些情況下,它們允許按需進行清理。例如,Postgres 允許用戶按需清理,或者每隔一段時間自動清理一次,而 Spanner 則使用垃圾回收器來清除超過一小時的陳舊數據。
計算系統最隱秘的秘密是所有的時間 API 都會“撒謊”。計算機無法準確地知道當前時間,它們都有一個石英晶體,會產生計時信號,但石英晶體無法準確地計時,不是比實際時鐘快就是比實際時鐘慢。每天出現的時間漂移最多可長達 20 秒。為了準確起見,計算機上的時間需要時不時地與實際時間同步。
NTP 服務器用于同步時間,但同步本身可能會因為網絡而出現延遲。在同一個數據中心中進行 NTP 服務器同步需要花費一點時間,而與公共 NTP 服務器同步有可能出現更大的傾斜。
原子時鐘和 GPS 時鐘是用來確定當前時間更好的一種來源,但它們昂貴,而且需要復雜的設置,無法在每臺機器上安裝。考慮到這些限制,數據中心使用了多層方法。雖然原子時鐘和 GPS 時鐘提供了準確的時間,但它們的時間是通過輔助服務器廣播到其他的機器上的。這意味著每臺機器都會與實際的時間發生某種量級的傾斜。
應用程序和數據庫通常位于不同的機器上,不僅分布在多臺機器上的數據庫節點無法就時間達成一致,應用服務器時鐘和數據庫節點時鐘也無法達成一致。
谷歌的 TrueTime 采用了不同的方法。大多數人認為谷歌在時鐘方面的進步要歸功于他們使用了原子時鐘和 GPS 時鐘,但這只是其中的部分原因。TrueTime 實際上做了這些事情:
TrueTime 使用兩種不同的來源:GPS 和原子時鐘。這些時鐘有不同的故障模式,因此同時使用它們提高了可靠性。
TrueTime 有一個非常規的 API,它以間隔的形式返回時間,時間可以是下限和上限之間的任意點。谷歌的分布式數據庫 Spanner 可以等待,直到確定當前時間超過了特定時間。
Spanner 組件使用了 TrueTime,TT.now() 返回一個時間間隔,Spanner 可以進行 sleep,以確保當前時間已經通過了一個特定的時間戳。
如果你在一個房間里問 10 個人“延遲”是什么意思,他們可能會有不同的答案。在數據庫中,延遲通常是指“數據庫延遲”,而不是客戶端所感知到的延遲。客戶端可以看到數據庫的延遲和網絡延遲。在調試問題時,能夠識別客戶端延遲和數據庫延遲是非常重要的。在收集和顯示指標時,始終都要考慮到兩者。
有時候,數據庫會說明它們在讀寫吞吐量和延遲方面的性能特征和限制。但在評估數據庫性能時,更全面的做法是對每一個關鍵操作 (查詢或事務) 進行評估。例如:
往一張表 X(已經有 5 千萬行記錄)插入新行,并更新相關表,此時的寫入吞吐量和延遲是怎么樣的?
當平均朋友數量為 500 人時,查詢某個用戶的朋友的朋友,此時的延遲是怎樣的?
當用戶訂閱了 500 個帳號 (每小時有 X 項更新) 時,查詢用戶時間軸的前 100 條記錄,此時的延遲是怎樣的?
性能評估可能包含了這些情況,直到你確信數據庫能夠滿足你的性能需求為止。
在收集指標時,要小心高基數。如果你需要高基數調試數據,請使用日志,甚至是分布式跟蹤信息。
并不是每一種數據庫都支持嵌套事務。嵌套事務可能會導致意外的編程錯誤,這些錯誤不容易識別,直到拋出異常。
嵌套事務可以在客戶端檢測和避免。如果無法避免,就要注意避免出現意外情況,即已提交的事務由于子事務而意外中止。
在不同的層封裝事務可能會出現意外的嵌套事務,而從可讀性角度來看,可能很難理解其意圖。看看下面這個例子:
with newTransaction(): Accounts.create("609-543-222") with newTransaction(): Accounts.create("775-988-322") throw Rollback();
這段代碼的結果是什么?它是回滾兩個事務還是只回滾內部事務?如果我們使用多層庫來封裝事務,會發生什么呢?我們是否能夠識別并改善這種情況?
假設一個數據層已經在一個事務中實現了多個操作 (例如 newAccount),在業務邏輯的事務中運行它們時會發生什么?此時具有怎樣的隔離性和一致性特征?
function newAccount(id string) { with newTransaction(): Accounts.create(id) }
與其要處理這種問題,不如避免使用嵌套事務。數據層仍然可以實現自己的操作,但無需創建事務。然后,業務邏輯可以啟動、執行、提交或中止事務。
function newAccount(id string) { Accounts.create(id) } // 在主程序中: with newTransaction(): // 從數據庫讀取一些配置數據 // 調用ID服務生成ID Accounts.create(id) Uploads.create(id) // 創建用戶上傳隊列
應用程序開發人員可能會在事務中使用應用程序狀態來更新某些值或設置查詢參數,這個時候要注意作用域。當發生網絡問題時,客戶端經常會重試事務。如果事務依賴的狀態在其他地方被修改,就使用了錯誤的值。
var seq int64 with newTransaction(): newSeq := atomic.Increment(&seq) Entries.query(newSeq) // 其他操作
無論最終結果如何,上面的事務每次運行時都會增加序列號。如果由于網絡原因提交失敗,在第二次重試時,它將使用不同的序列號進行查詢。
查詢計劃決定了數據庫將會如何執行查詢。它們還會在執行查詢之前對其進行分析和優化。查詢計劃只能根據某些信號提供一些可能的估計。例如下面這個查詢:
SELECT * FROM articles where author = "rakyll" order by title;
獲取結果有兩種方法:
全表掃描:我們可以遍歷表中的每條記錄,并返回與作者姓名匹配的文章,然后根據標題排序。
索引掃描:我們可以使用一個索引來查找匹配的 ID,獲取這些行,然后排序。
查詢計劃的作用是確定最佳執行策略。但可用于預測的信號是有限的,因此可能會導致做出錯誤的決策。DBA 或開發人員可以用它們來診斷和調優性能較差的查詢。慢查詢日志、延遲問題或執行時間統計信息可用于識別需要優化的查詢。
查詢計劃提供的一些度量可能不會很準確,特別是在估計延遲或 CPU 時間方面。作為查詢計劃的補充,跟蹤和執行路徑工具在診斷這些問題方面更有用,但并不是每種數據庫都會提供這些工具。
在線或實時遷移就是在不停機、不影響數據正確性的情況下從一個數據庫遷移到另一個數據庫。如果要遷移到同一個數據庫或引擎,實時遷移會容易一些,但要遷移到具有不同性能特征和模式需求的新數據庫,就要復雜得多。
在線遷移有一些可遵循的模式:
在兩個數據庫上執行雙重寫操作。在這個階段,新的數據庫不包含所有數據,但會包含新數據。在這一步穩妥之后,就可以進入第二步。啟用針對兩個數據庫的查詢路徑。
讓新數據庫承擔主要的讀寫任務。
停止對舊數據庫的寫入,但可以繼續從舊數據庫讀取數據。此時,新數據庫仍然不包含所有數據,要讀取舊數據,仍然需要從舊數據庫獲得。
此時,舊數據庫是只讀的。用舊數據庫中的數據填充新數據庫缺失的數據。遷移完成后,所有讀寫路徑都可以使用新數據庫,舊數據庫可以從系統中移除。
數據庫的增長會帶來不可預測的伸縮性問題。
隨著數據庫的增長,之前對數據大小和網絡容量的假設或預期可能會過時,比如大型 scheme 重構、大規模的運維改進、容量問題、部署計劃改變或遷移到其他數據庫以避免宕機。
不要以為了解數據庫的內部結構就足夠了,因為伸縮性會帶來新的未知問題。不可預測的數據熱點、不均勻的數據分布、意外的容量和硬件問題、不斷增長的流量和新的網絡分區,這些都會迫使你重新考慮數據庫、數據模型、部署模型和部署規模。
關于數據庫知識都有哪些就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。