往往大數據量,高並發時, 瓶頸都在數據庫上, 好多人都說用數據庫的復制,發布, 讀寫分離等技術, 但主從數據庫之間同步時間有延遲.代碼的作用在於保證在上端緩存服務失效(一般來說概率比較低)時,形成倒瓶頸,從而能夠保護數據庫,數據庫宕了,才是大問題(比如影響其他應用)。
假設(非完全正確數據,僅做示例):
每秒支持10,000,000次查詢(千萬);
一次讀庫需要耗時:1ms;
修改內存變量需要耗時:0.001ms;
那么:
每秒最終訪問的數據庫的請求數量 < 1000
其他的9,900,000個請求會返回到其他頁面。這就是為啥很多搶單網站有人可以訪問,而有人得到繁忙中頁面的原因。
微觀到1ms來看,在currentValidSessionID == -1的時間是 1ms,從而平均會有10000條記錄涌入。
currentValidSessionID從-1變為其他值的時間為0.001ms,這個時間內,
lock (databaseDoor) { // now there is only one request can reach below codes. if (currentValidSessionID == -1) { currentValidSessionID = currentRequest.SessionID; } }
平均會有 10000×0.001=10條記錄會執行到上述這段代碼,操作系統會為鎖形成等待序列。
那么我們的目標是,每毫秒只允許一次讀庫(因為其他應用也會使用),所以我們只希望這進入的10條,最終只有一條能夠繼續前進。
那么這就是
if (currentValidSessionID == -1) { }
的作用了。再次進行一次判斷,進入原子保護隊列的請求,也只有一個能夠繼續。
一點思考:
其實對於一個主頻能上N GHz的服務器來說,一個內存數賦值給另一個內存數據就是1~4條指令(平均2條,兩次MOV操作),也就是2/N ns時間,而不是我們上述假設的 1000ns(0.001ms)。其實不用原子,我們已經可以把千億級請求的訪問數控制在個位數。
不過一個架構師,如果可以用一個99.99%安全的方案,就絕對不用99.9%。 SO。
public static long currentValidSessionID = -1; public static object databaseDoor = new object(); void readDatabase(Request currentRequest) { // use currentValidSessionID to filter out other requests came in during the execute time gap if (currentValidSessionID == -1) { // use object-lock to filter out other requests came in during the variable change time gap. lock (databaseDoor) { // now there is only very little number of requests can reach below codes. if (currentValidSessionID == -1) { // now there will be only one request can access the database currentValidSessionID = currentRequest.SessionID; } } } if (currentValidSessionID == currentRequest.SessionID) { // here is the one ! try { // use transaction to guarantee the execute time to void block // access database codes go here } catch() { // exception codes go here } finally { currentValidSessionID = -1; // recover to original state } } }