? 并發(fā)隊(duì)列的選擇
? Java的并發(fā)包提供了三種常見的并發(fā)隊(duì)列實(shí)現(xiàn):arrayblockingqueue、concurrentlinkedqueue和linkedblockingqueue。
? ArrayBlockingQueue是一個(gè)具有固定初始容量的阻塞隊(duì)列,它可以作為數(shù)據(jù)庫模塊(如10項(xiàng))的成功投標(biāo)隊(duì)列,因此我們建立了一個(gè)10大小的數(shù)組隊(duì)列。
? Concurrentlinkedqueue是一個(gè)由CAS原語實(shí)現(xiàn)的無鎖異步隊(duì)列。進(jìn)入隊(duì)列的速度很快,鎖定隊(duì)列后性能稍慢。
? LinkedBlockingQueue也是一個(gè)阻塞隊(duì)列,無論是進(jìn)出隊(duì)列都被鎖定,當(dāng)團(tuán)隊(duì)為空時(shí),線程將暫時(shí)阻塞。
? 在請求預(yù)處理階段,由于系統(tǒng)對進(jìn)入隊(duì)列的需求遠(yuǎn)遠(yuǎn)大于離開隊(duì)列的需求,因此不會(huì)出現(xiàn)空隊(duì)列,因此可以選擇并發(fā)鏈接隊(duì)列作為請求隊(duì)列的實(shí)現(xiàn)
? 1.請求接口的合理設(shè)計(jì)
? seckill或snap-up頁面通常分為兩部分,一部分是靜態(tài)HTML和其他內(nèi)容,另一部分是參與seckill的web后臺(tái)請求接口。
一般情況下,靜態(tài)HTML等內(nèi)容都是通過CDN部署的,一般壓力并不大,核心瓶頸實(shí)際上是在后臺(tái)請求接口上。這個(gè)后端接口必須能夠支持高并發(fā)請求。同時(shí),盡快返回用戶的請求結(jié)果非常重要。為了盡可能快地實(shí)現(xiàn),接口的后端存儲(chǔ)將與存儲(chǔ)器級操作更好。不適合直接面對MySQL等存儲(chǔ)。如果有如此復(fù)雜的業(yè)務(wù)需求,建議使用異步編寫。

? 當(dāng)然,也有一些秒殺和閃購使用了“滯后反饋”,即秒殺目前還不知道結(jié)果,從頁面上可以看出用戶一段時(shí)間后秒殺是否成功。但這種行為屬于“偷懶”行為,用戶體驗(yàn)不好,很容易被認(rèn)為是“暗箱操作”。
? 高并發(fā)下的數(shù)據(jù)安全
? 我們知道,當(dāng)多線程寫入同一文件時(shí),有一個(gè)"螺紋安全"問題(多個(gè)線程同時(shí)運(yùn)行相同的代碼,如果每個(gè)運(yùn)行的結(jié)果與單個(gè)線程的結(jié)果相同),結(jié)果是線程安全(預(yù)期)。如果是MySQL數(shù)據(jù)庫,可以使用自己的鎖定機(jī)制來解決這個(gè)問題。但是,在大規(guī)模并發(fā)場景中,不建議使用MySQL。在殺人搶奪的場面中,還有另一個(gè)問題,那就是“超調(diào)”,如果在這方面不小心,就會(huì)造成太多的送人。我們也聽說一些電子商務(wù)公司從事閃購活動(dòng)。買家成功拍照后,商家不承認(rèn)訂單有效,拒絕送貨。這里的問題可能不一定是企業(yè)是奸詐的,但在系統(tǒng)的技術(shù)水平上存在超限風(fēng)險(xiǎn)。
? 1. 超發(fā)的原因
?? 假設(shè)在搶購的情況下,我們總共只有100種產(chǎn)品。在最后一刻,我們已經(jīng)消費(fèi)了99種產(chǎn)品,只剩下最后一種。此時(shí),系統(tǒng)發(fā)送多個(gè)并發(fā)請求,這些請求讀取的貨物的剩余量為99,然后全部通過該余量判斷,最終導(dǎo)致溢出。(與文章前面提到的場景相同)

? 在上面的圖中,并發(fā)用戶B也被“搶占”,讓另一個(gè)人訪問該產(chǎn)品。在高并發(fā)的情況下,這種場景很容易出現(xiàn)。
? 2. 悲觀鎖思路
? 解決線程安全問題的思路很多,可以從悲觀鎖的角度進(jìn)行討論。
? 悲觀鎖,即在修改數(shù)據(jù)時(shí),使用鎖狀態(tài)排除外部請求的修改。如果您遇到鎖定狀態(tài),您必須等待。

? 盡管上述解決方案確實(shí)解決了線程安全問題,但不要忘記我們的場景是“高并發(fā)性”。換句話說,會(huì)有很多這樣的修改請求,每個(gè)請求都必須等待一個(gè)"鎖",一些線程可能永遠(yuǎn)不會(huì)有機(jī)會(huì)抓取"鎖",這樣的請求就會(huì)在那里死去。同時(shí),會(huì)有很多這樣的請求,這會(huì)在瞬間增加系統(tǒng)的平均響應(yīng)時(shí)間。因此,可用連接數(shù)將耗盡,系統(tǒng)將陷入異常。
? 3. FIFO隊(duì)列思路
? 好吧,讓我們修改一下上面的方案,我們直接在隊(duì)列中使用FIFO(先進(jìn)先出),這樣我們就不會(huì)引起一些請求永遠(yuǎn)得不到鎖??催@里,把多線程變成一個(gè)線程不是有點(diǎn)強(qiáng)迫嗎。

? 然后,我們現(xiàn)在解決了鎖定問題,并且在"先進(jìn)先出"隊(duì)列中處理了所有請求。所以新問題來了。在高并發(fā)場景中,由于請求較多,隊(duì)列內(nèi)存很可能在一瞬間突發(fā),然后系統(tǒng)進(jìn)入異常狀態(tài)。或者大內(nèi)存隊(duì)列的設(shè)計(jì)也是一種解決方案,但是系統(tǒng)請求在一個(gè)隊(duì)列內(nèi)的速度不能與瘋狂涌流隊(duì)列的數(shù)量相比較。也就是說,隊(duì)列中的請求將越來越多地累積起來。最后,web系統(tǒng)的平均響應(yīng)時(shí)間會(huì)急劇下降,系統(tǒng)仍然會(huì)陷入異常狀態(tài)。
? 4. 樂觀鎖思路
? 在這一點(diǎn)上,我們可以討論樂觀鎖的想法。樂觀鎖是一種比悲觀鎖更為寬松的鎖機(jī)制,大多數(shù)悲觀鎖都是用版本更新的。該實(shí)現(xiàn)是所有對該數(shù)據(jù)的請求都有資格被修改,但是只有在版本號匹配時(shí)才會(huì)獲得數(shù)據(jù)的版本號,而其他的返回將失敗。這樣就不需要考慮隊(duì)列問題,但會(huì)增加CPU的計(jì)算成本。然而,從全面的角度來看,這是一個(gè)很好的解決辦法。

? 有許多軟件和服務(wù)支持樂觀鎖功能。例如,redis的手表就是其中之一。通過此次實(shí)施,我們保證了數(shù)據(jù)的安全