博客來售票網的程式馬們.. (1)

唔.. 看到 2007 金馬影展官方部落格
我實在是不忍再苛責博客來售票網。

包括錯誤的確認信件,到目前為止收到了這些信:

  1. 2007/11/05 17:58 【金馬-套票註冊成功信】(一份四百餘人的清單)
  2. 2007/11/05 22:00 【博客來售票網致歉信函】(誤寄信件道歉 + coupon 們)
  3. 2007/11/06 22:23 【博客來致歉函】(誤寄信件正式道歉)
  4. 2007/11/08 20:05 【博客來致歉函】(全額補償方案)
  5. 2007/11/09 18:04 【售票網通知:「金馬影展」劃位小祕訣】
  6. 2007/11/10 12:19 – 13:13 十二封【售票訂購單】*
  7. 2007/11/10 20:18 【博客來售票網致歉函】(劃位系統爆炸道歉)

這樣總共是.. 十八銅人封信 (默)

中間不包含博客來網路「書店」寄送的信,
因為架構上明顯的不同,我將它們視為兩個不同的系統。

* 為什麼會有十二封,我之後會解釋

我必須承認,打從我看到售票網是以 ASP 寫成時,
就覺得事情不大對勁了,一是博客來書店是以 PHP 架構的,
另外,就是跑 ASP 的伺服器如未經調教調校,
而若剛好又養了那種崇尚自由、我行我素的程式馬的話,
根本就無法應付大量的連線,或是惡意的攻擊。

但正因為是博客來,我並沒有多想。

在收到那張落落長的名單時,
除了覺得當天收到的 coupon 有種息事寧人的味道,
也還是沒有多想,總覺得那群師程工應該會把程式馬管好;
沒多久收到了全額賠償的道歉信,反而有受寵若驚之感,
總覺得博客來確實正視了這件事情,並且設法解決。

這樣近似錯覺的想法,完全是建立在對博客來的信任。

在發生了這麼多事情後,理性且邏輯一點地來說,
「博客來售票網」已經不值得再被信任了,
而這樣的不信任,會間接地影響到對網路書局的感受。

我還是不忍苛責博客來。

畢竟,要讓 PHP-based 系統跟 ASP-based 系統一起運作,
本來就是件很傷腦筋的事情,而未經慎思的系統架構,
導致不斷地重蹈覆轍,也是預料中事。

但這不全然只能怪工程師。

為什麼呢?

第一,我不知道這項產品的開發時間,無從推論是誰對誰錯;
第二,ASP 本身是不難寫,但要寫得好可能會比 PHP 還困難;
第三,劃票定位流程本身就有瑕疵,這不見得是工程師想出來的

雖說如此,這也不表示工程師就能完全脫罪,
一個程式設計者,照著上面給下來的指示寫程式固然沒錯,
但應當發現流程中可能出現錯誤,或是效能不彰的可能條件;
對於那些準備披上戰袍、上戰場廝殺的程式馬們更是重要。


我不知道系統運作的實際狀況如何,
下面是我對這個系統運作效能低落的一些猜測。

那麼,就從登入畫面開始說吧。

4000 人同時登入系統,理論上應該不會造成太大的壓力,
真正的壓力是在後端資料庫處理與程式運算的部份。

十點鐘之後,系統突然間就連不上線了,
等十一點多總算連上時,發現是一個 PHP 頁面。

20071110_GoldenHorseBrr01

這個頁面沒有什麼太神奇的地方,
就是在倒數完成前,按鈕是 disabled 的,
而完成倒數後,會由 JS 負責向發號碼牌的 server 要號碼..

我想,應該是用類似 Sesseion ID 的方式達成,
不過這個系統應該是用 username 去決定誰能登入,
感覺上,像是直接用 stack-liked 的方式去堆,
然後,採 FIFO 方式排隊。

為什麼會這樣猜呢?

因為我本來用 Opera 排隊,但時間到了之後無法登入;
不知道是 browser 的問題還是 server 的問題,
最後想想,就換回遲緩的 IE 繼續劃位。

原本以為是用 Cookie 或是 SESSID 去決定的,
但換瀏覽器之後,不用重新排隊便能登入系統,
想想,嗯,應該是直接紀錄誰抽過號碼牌了。

一個朋友在 12:10 前登入系統,
他說,七分鐘就把套票全部劃完了,
而我抽到的號碼牌是在 12:10 後登入,
從開始到劃完座位,中間被系統踢出去兩次,
還有一次購物車被清空,更多的是像下面這樣的畫面:

20071110_GoldenHorseBrr04

ERROR 500,通常不是 script 寫錯,
就是 script 執行逾時,導致系統判斷程式執行錯誤;
假設該網頁 scrip 本身沒大問題,那就是 DB 被打掛了.. ||

我很假設性地猜,號碼牌機制祗決定你什麼時候可以登入
而完全不顧登入之後呆了多久,或是執行了什麼;
不知道為什麼,這樣的登入方式會讓我想到百貨週年慶
只差沒有看到一堆人在排隊入場 @@

這樣週年慶式的登入方式也沒什麼不好,
但很明顯地,比較早進入系統的人勢必會享有較多資源,
而之後不斷地放人,卻沒有控制系統總人數,
若沒有經過悉心調校,系統將會十分吃力。

唔,從網頁上使用者的留言來看,
潛在設計不良的選位程式,查詢節目與購物車的機制,
似乎也間接造成了系統資源被快速消耗。

先從自動選位程式來講吧,
最容易寫出來的程式形狀大概是這樣:

foreach(seat[best ... last] as seekSeat)
{
isTaken = queryDB(seekSeat);
if(isTaken == false)
{
queryDB(seat[seekSeat] = taken);
return seekSeat;
}
else seekSeat = next seat;
}
return noSeat;

(注意,我只是假設程式是這樣寫,不代表實際狀況一定相同)

大意就是說,我讓電腦從一張清單裡面挑,
從第一個座位一路看下去,找到空位就傳回來。

這樣的程式看起來很人畜無害,
但這次四個場地中,日新威秀與 in89 都是大廳,
可選擇座位較多,迴圈有可能會跑上幾百次,
在前幾個人選擇的時候當然沒什麼問題,
但在人越來越多時,程式就會送出大量的 query,
只為了得知「座位是不是被選走了」這件事情,
最後變成資料庫無法負擔如此大量的連線而被打掛。

如果是我的話,會把可用坐位記成幾段純文字,
以簡單的 0 / 1 來表示是否被選。

lockDB(seatsData);
seatsString = queryDB(seatsData);
availableSeatNo = strpos(seatsString, "0");
if(availableSeatNo == false) return noSeat;
else
{
queryDB(seatsString[availableSeatNo] = '1');
return availableSeatNo;
}
releaseDB(seatsData);

這麼一來,DBMS 就顯得輕鬆許多了,
即便是上千個人使用,也不會一直問系統位子到底被選了沒,
一次 query 便傳回所有座位的狀況,
而 lock 的時間應該也不會太過誇張。

..當然,實際狀況可能還要注意更多東西就是了。

而相同的狀況似乎也發生在查詢節目單時。

一般來說,多人使用的系統應該會有 cache 的機制,
但從反應時間來看,我合理的懷疑系統沒有 cache,
在幾千個人同時上線的時候,資料庫系統就會很可憐 (默)

這讓我想到一句忠告:「DBMS 不是唯一解,也不見得是最佳解。」


因為我必須追趕我的日記與準備期中考,
其他可能有的問題,我之後會再抽空補完的 T__T