我一直覺得選課系統應該很好寫啊, 是我忽略掉了什麼重要的因素嗎?
比如所有必要數據緩存在內存里 (應該完全可以吧), 伺服器端每隔一段較短的時間寫入一次, 客戶端在這段時間後詢問伺服器端上一次寫入是否成功, 如果成功則通知用戶選課完成. 感覺能應對相當大的並發量呀. 是我忽略掉了什麼不該忽略的么?
// 囧主要關注的是高並發的問題, 業務邏輯和用戶體驗假設都能做好.
選課系統一年就兩波流量,每次就一兩個星期,如果伺服器配高內存高性能,領導又不高興太花錢。
新系統領導用不來,或者不兼容領導的ie6——(我是黑)
新系統上線的試用期很難定,要是出了問題要下線調試怎麼辦?選課就是這個禮拜的事情啊,說不定等bug好了就要下學期要運行新系統了。
高並發?10000學生同時選課?ok解決了後,搶課插件出現了。。。妥妥每分鐘流量上升100倍。怎麼辦?(我們學校就是教務處約談插件作者,要求改成每3秒請求一次。以及在登陸後後用白底黑字紅色突出「使用選課插件可能造成誤選誤退課,教務處概不負責」。。。插件作者被塑造成惡意退別人的課方便自己這樣一個形象。。。)首先的問題肯定是整個系統的效率了……
浙江(好像還見過福建)不少高校在用正方那一套系統,http://ASP.NET的程序,被反編譯後據說是真的無法直視的代碼質量……還有就是伺服器配置了。
這個配置,除了硬體層面的,還有參數上的,和日常運營管理上的。一個參數卡死你的事情絕對不少。硬碟出壞道了沒人處理繼續帶病運行不卡就怪了。你和你的代碼、資料庫效率、伺服器配置戰鬥完畢了,萬事大吉?
你忘了,除了自己可控的代碼質量,還有一個不可控的事情是,你發布後,你的整套系統部署到的環境質量。這個環境質量除了系統在運行的伺服器的配置外,還有更重要的,網路質量。學校沒那麼有錢搞高大上的,網關的承受能力很可能就平時1K/s的訪問量,搶課的時候每秒3K~5K的突發訪問,直接擠爆基本網路設備了,軟體質量再好又如何?PS.扯淡:我個人認為,一般一個事業單位對一套在用的系統是不會輕易去考慮更換的。首先,只要他們領導認為平時還可以啊,那就不願意折騰了,畢竟只是短痛,痛過就過了。
其次,你去換一套系統,你覺得你能平穩的換過去還不產生不好用、不習慣、不科學的地方?換的時候搞出來了,鍋就是你的了還有,各家數據表設計實在是過於千差萬別了,也沒見過有寫標準化數據導出的,遷移數據,部分是代碼活,還有很多很多人力活……其實這個問題和之前大家吐槽12360一樣,因為距離太遠所以感覺上很簡單,其實遠沒那麼簡單。
選課不是一個僅僅在「已選人數」上+1或-1的問題。
學生在登錄選課系統後看到的數據首先是經過系統篩選的,教務處在課程上一般都會設置這個課程僅供哪些學生可以選,也就是選課條件,篩選維度可以有性別、年級、專業、院系、班級,而這種條件有可以設置多個組,條件組又有嵌套。。。光要解決「拉出課程清單」這個事情就夠你喝一壺了。
你以為只要你能看到這個課,並且人數沒滿就能選了?讓我來告訴你幾個業界都會有的選課限制規則吧:- 每學期門數限制
- 可選課程必須在培養計劃範圍內
- 可選課程必須在當學期培養計劃範圍內
- 大學4年總共可選的學分不得超過培養計划上的要求
- 本學期可選的學分不得超過培養計划上本學期的要求
- 英語等級B的不能選要求英語等級A的課程之類
- 本科不能選專科的課,專科不能選本科的課
- 某些專業是不能選某些課程的(比如計算機的公共課不允許計算機專業學生選)
- 某些類別的課大學4年不能選超過N門
- 某些類別的課本學期不能選超過N門
- 某些類別的課大學4年不能選超過N學分
- 某些類別的課本學期不能選超過N學分
- 一定要修某些課程才能選某些課程,比如必須學高數(上)才能選高數(下)
- 不能選時間上衝突的課程
- 允許or不允許重修
- 允許or不允許及格重修(也就是防刷分)
- 不允許退被制定的課程(比如有些必修課一開始就在學生課表上了,不能退)
- 必須要先評教才能選課
- 只能退當前輪次選的課(比如某些學生會把第一輪選的課退掉,但是第二輪這個課並沒有開放出來)
- 退課釋放出的名額不能馬上給學生看見,要在某個時間點才能讓學生看見(復旦這麼乾的)
- 選課退課都要記錄日誌,哈,又是一張表
當然實際業務中包括並不限於這些選課規則。
光看上面的你就知道選課這麼一個簡單的操作背後是一個多麼龐大的事務,關聯查詢的表之多超乎你的想像。
除了以上說的事務龐大的問題,還有以下這些問題:- 如何做緩存,要知道教務處管理員會隨時對選課涉及到的業務數據做更改,數據兩頭改,緩存設多長失效時間合適,如果太短還不如不設,太長又達不到實時性要求。
- 如何保證事務原子性,如果把事務拆分成幾個小事務,那麼如何保證數據一致性?
- 如何避免死鎖,多線程高並發情況下,寫的又不知一張表,如何保證不會產生資料庫死鎖?
- 如何提高性能,前面有人講高性能不是最難的我只能說too young,too simple
- 瀏覽器兼容性,你怎麼知道學生都用chrome?IE8以上,firefox,還有360(領導要用360的!)統統都要支持!
- 網站性能優化,壓縮圖片啦,css,js minify啦,用http緩存啦
- 負載均衡
- session共享
- 等等等等
你忽略掉的因素:
1.這項任務要做到極致,需要一群頂尖的工程師。國內只有BAT才有這種實力,可惜BAT不做這類業務。
2.國內目前做這種系統的公司,基本上都是一些渣爛公司。無論架構師、技術、實現等員工的水平都相當差。
3.你們不能只考慮業務的性能。這玩意至少需要一個web界面來給用戶操作吧。也許性能的瓶頸並不是在業務上,而是在web上。
最後補充一點,選課系統要做到極致的話,會產生很多技術難題。不過,一些頂尖的本科程度的畢業生就能解決所有的技術難題。但是,這類工程的工作量太大,幾個精英再牛,其開發速度也是相當有限,根本無法在客戶規定時間內完成這類工程。
- 並發量,全校一起上;
- 選課規則,比如互斥的課程、總學分計算等;
- 用戶友好,及時提示課程人數滿等。
沒有二、三點,也就和靜態表格沒區別了……
沒有第三點,只能靠用戶頻繁刷新,那也就是上個世紀的業務系統的水平了……選課系統和搶火車票是類似的,首先都有一個資源池,裡面又分很多槽(課程/車次),每個槽有容量限制。都是一個大並發的程序(全校學生在閘口開放一瞬間一涌而上),卻要保證100%的正確性(課程/車票資源是有限的,一個都不能多分配出去,不然讓人坐那兒?)。
所以難點在於為了保證正確性就必須對資源加鎖,為了應對大並發必須把鎖加在儘可能小的粒度,在選課這個場景最小似乎就只能把鎖加在課程的餘量上面,這其實還是太粗粒度了。要麼就用無鎖的方式,比如使用通訊取代share與競爭,這是另一種思路下面會說。
這樣首先是可以根據不同課程進行一次query hash,保證相同課程落到相同機器上面,但問題是如果某個課程極度熱門,那麼理論上最壞情況是所有用戶都在搶它,效果可想而知。
為了增強彈性這時候只能再擴大規模,那就只能犧牲實時性了,把選課請求全打到消息隊列裡面,讓後端來挨個處理(這裡依然可以使用query hash)。這樣正確性肯定有保證了,大並發的難度也被消息隊列化解的七七八八。剩下的就是用戶抱著買彩票一般的心情在點擊選課後幾秒鐘甚至更長的時間裡那期待而又絕望的瘋狂F5所帶來的壓力了,不過只讀的場景就好抗多了……nodejs
可能是以前沒有雲計算的商業模式吧。沒法短期內購買大量資源,之後用完再丟掉。都是直接自己買伺服器,平常閑置率太高,用的時候又不夠。
PS:至於寫到內存裡面去,然後經常回硬碟存一下這種技術設計有了好久了吧-.- 不需要自己寫的,直接拉個現成的資料庫就好了。
PPS:還需要考慮數據結構的問題。如果數據結構設計得的好,讀取寫入少精準的話,用戶實際體驗輕鬆翻一翻^_^確實不難,特別是在虛擬機環境下,選課那幾天多給點資源唄。另外,搞開發不要一開始就想得那麼複雜,先寫個最簡單的實現,再慢慢優化。插件的問題好辦,輸入驗證碼即可。另外,別把資料庫想得太弱,你想到的,資料庫都想到了,而且實現得比你好。問題是大多數人不懂得如何優化資料庫。
如果已經有人架構好了整個系統(選課,課程,教師,教室,院系,課程體系)等等,現在只需要根據業務邏輯寫一個選課系統的話,這是一件非常簡單的事情,簡單到只需要掌握下面的知識即可:
- 根據已有的資料庫表(比如課程,學生信息)等設計所需要的資料庫結構
- 使用相關編程語言實現網站的前端及後端,涉及的知識有HTML、JavaScript、CSS、Java或C#、SQL等
- 處理高並發,個人認為並這不是最難的部分,由於題主特意強調了這一部分,這裡重點介紹。解決方法很簡單,將熱點數據緩存在內存中(比如某個課程現在有哪些用戶選擇,這個數據需要經常更新並驗證人數是否已滿),這部分推薦用nodejs寫一個中間組件來讀寫資料庫並記錄至內存(比如redis),這種方案最為關鍵的部分是如何選擇熱點數據(據說新浪微博和twitter都使用的這種策略),由於內存是非常高昂的開銷,所以選擇存儲哪些數據要非常的慎重,一位朋友的網站因為緩存的數據過多而經常使網站癱瘓(內存不足)。
- 體驗優化,可以使用web socket等技術進行主動推送以避免用戶頻繁刷新。
如果需要從頭開始設計這個系統,那麼最為困難的部分就是需要滿足所有用戶需求的系統,就是需求分析與設計。這裡的用戶可能有管理員、教務處職工、教職工、學生等等,內容包括課程信息、教室信息、教職工信息、課程體系信息等等,將這些業務分拆並設計出滿足所有用戶需求的系統是件非常耗時並困難的事。
推薦閱讀:
※ASP.NET ADO.NET 和C#編程語言他們之間的關係是怎樣的?
※VS 為什麼不支持 LaTeX?
※如何擺脫寫代碼總有一種照搬別人代碼只是在此基礎上修修補補的感覺?
※如何利用已有的流行即時通訊軟體實現自定義加密通信?
※漢諾塔問題的遞歸解法是怎麼想出來的?