The two most important days in your life are the day you were born and the day you find out why. -Mark Twain

#72 一秒38萬個交易的故事 - 資料庫引擎篇

與資料庫有關的文章到目前為止已張貼了二十二篇文章,這些文章所談論的內容大部份都是描述資料庫引擎裡 Storage 和 Transaction 的內容.資料庫引擎裡的內容當然不只這些,還包括 SQL query parser, query optimizer, logging manager, 以及一些基本的功能如 security control 等等.光是有關 storage manager 的故事就能寫一本書了,除非你想做資料庫引擎的專家才需要知道所有的細節,不然的話,只要了解一些基本的設計便能幫助你在工作上做到更好的境界.在這些二十二篇與資料庫引擎有關的文章,我寫的那些內容其實簡化或跳過不少的細節,但那些細節並不會影響你對資料庫引擎設計的了解,只讓你可以用比較高層次的角度來檢視資料庫引擎會為你做的事情.畢竟這些內容主要的對象是給非電腦科系畢業的資訊人所參考用,因此忽略了許多數學上的推導與比較.若你是電腦科系的學生正在學習資料庫理論,請以課本內容為主,因為課本內容裡面會用一些數學來明確表達運作成本等事情.為什麼要說這些呢 ? 這當然是和本篇文章的主題有關,一個一秒38萬個交易的故事.

這句話是去年阿里巴巴在雙十一的購物節活動後,阿里巴巴的大老闆馬雲所講的.他用總交易數量除以總交易時間,得到一個值就是阿里巴巴的系統在購物節活動時能處理一秒三十八萬筆交易.這是一個相當驚人的數字,也證明了阿里巴巴的工程團隊能得到這樣的水準.其實這樣的水準,既便是連 Amazon 也不見得能有這機會達成,畢竟以美國的人口數和使用量,要一秒鐘達成三十八萬交易數量,似乎蠻難的.中國有更龐大的人口再加上雙十一購物節的熱潮,自然而然能在短時間內形成非常高的使用量.反觀台灣呢 ? 若我記憶正確的話,高鐵訂購系統曾因大家搶票而掛了,江蕙退隱歌壇演唱會的訂票系統也曾發生這樣的事.想想台灣人口才多少人,瞬間能產生的使用量一定遠遠不如阿里巴巴遇到的情況,但很可惜系統掛掉的事情仍是發生了.可能的原因大概有兩種,一種是工程人員無法勝任工作,另一種原因是電腦資源不足.不論是那一種原因所產生的結果都是會令人產生負面的印象.

一個能處理一秒三十八萬筆交易的系統是一個龐大的系統,每個部份都有許多故事可以講,接下來的內容只討論一些與資料庫引擎有關的內容.在網路上我曾看過一篇文章談到阿里巴巴的資料庫交易系統裡為效能改進所做的一些事情,其中最讓我印象深刻的事情是預先建立訂單資料.以資料庫引擎的角度來看,預先建立訂單資料等於把所需要的 page 先建立完成,因此在系統運行的過程中,不會有 page split 的事情產生.請參考之前的文章
以上這幾篇文章與 page 有關,看完後,你便能了解為何 page split 是件大事,並且也能了解預先建立訂單資料就相當預先把這些 page 都建立完成.等系統運行時,資料庫引擎便不需要在當下建立這些 page,而是直接使用這些 page.簡單的說就是不用做 insert table, 只要 update table 即可.

除此之外,把 foreign key 拿掉也是件對效能有幫助的事情.因為把 foreign key 拿掉就等於讓資料庫引擎不用去檢查 foreign key 對應 primary key 的正確性,也就是讓資料庫引擎少做事情.但這樣做有某種程度的風險,請記得要搭配一些必要的配套措施.這方面的內容請參考之前的文章 http://www.woolycsnote.tw/2015/04/7-foreign-key.html

另外還有一個比較常用的技巧是將 database schema 做 de-normalization 的處理.如果你的程式需要做很多的 table join 時,你得好好考慮 de-normalization 這件事.平常使用量不高時,table join 也許不會造成你太大的影響,一旦使用量一高時,table join 將是個高成本的動作.所以在特別的情況下,將資料做 de-normalization 減少 table join 這能讓資料庫引擎少做許多事情.這方面的內容請參考之前的文章
當整個系統過了尖峰時刻後,再將結果的資料回復到 normalization 的情況下即可.

如果為了特別的目的而需要讓資料庫引擎少做一些事情以讓它達到最大效能的運行情況,都需要進行一些特別處理,上面所講的都是不錯的特別處理,可以有效讓資料庫引擎少做一些事讓它達到盡可能好的運行效能.當然可行的方法不只是這些,還有許多其他從資料角度或硬體角度來思考的方法.這篇文章的主要目的就是幫助大家明白在做這些特別處理的事情時背後所用的原理 (原因) 是什麼,讓你有足夠強大的理由相信這樣做的確是有幫助的,這也是這個部落格的目的.

Hope it helps,


Share:

#71 資料庫引擎 - Deadlock 偵測 (Wait-For graph)

前兩篇文章談完了 table join,接著這篇文章把主題拉回到資料庫的 Transaction.

之前文章曾提過 deadlock 的發生,這在關聯式資料庫裡算是件平常可見的事情.之前曾提過當 deadlock 發生時,資料庫引擎可利用 wait-for graph 來偵測 deadlock 的發生.這篇文章將說明什麼是 wait-for graph.

Wait-for graph 是一個簡單的圖形,是一個有方向的圖形 (directed graph),也就是它圖形上的邊具有方向性,這裡的方向性用來代表是那一個交易等待那一個交易.如下圖:



上圖的 S(A) 代表某個交易要對 A 物件取得 shared lock,R(A) 代表某交易要對 A 物件進行 read 動作,X(B) 代表某交易要對 B 物件取得 exclusive lock,W(B) 代表某交易要對 B 物件進行 write 動作.在進行 read 動作之前必須取得 shared lock,在進行 write 動作之前必須取得 exclusive lock.

從上圖可得知,T1 等待 T2,因為 T2 正在寫入 B 物件.所以在 wait-for graph 上就會有一個邊從 T1 指向 T2,用來代表 T1 等待 T2.再來, T2 等待 T3 因為 T2 想要寫入 C 物件,但 T3 先對 C 物件進行 read 動作.T3 等待 T1,因為 T3 想對 A 物件進行寫入動作,但在這之前 T1 對 A 物件進行讀取動作.因此就變成了 T1 等待 T2, T2 等待 T3, T3 等待 T1 的情況.這個情況將使得 wait-for graph 會形成一個 cyclic graph ,意思就是圖形裡點的某個點走到其他點上後有可能再走回到原來出發的點,這便形成了一個 cycle.



資料庫引擎在這個 wait-for graph 上來辦別是否有 cycle.如果有,這表示 deadlock 發生了.此時資料庫引擎便得採用設計好的規則來決定該如何解決 deadlock.解決的方法就是讓 wait-for graph 不會有 cycle 的現象.理論上,只要在 cycle 的路徑上把某一個 transaction 刪除即可.通常來說,資料庫引擎會刪除 "年輕" 的 transaction,"年輕" 是用 transaction 啟動時間來決定,因為基本上來說年輕的 transaction 已做過的動作可能比較少,所以刪除他們再重新啟動後比較省點讀取和寫入資料成本.

Wait-for graph 的應用其實蠻廣泛,在資料庫系統或一般的作業系統都會用到它來描述物件之間的關係.甚至在程式開發工具裡,元件之間參考的關係也可以用 wait-for graph 來呈現.當 A 元件參考 B 元件,B 元件參考 C 元件,C 元件再參考 A 元件,這情況將不允許發生,因為在 wait-for graph 裡將會有一個 cycle.

Hope it helps,


Share: