傳統商城的秒殺設計簡單實現

*秒殺的實現
一.秒殺的特點
1.秒殺的特點是在規定時間內,用戶在同一時間進行搶購,網站流量激增。
2.秒殺的請求量遠遠大於庫存量。
3.搶購支付成功從數據庫減少庫存。

二.秒殺的設計
限流:由於有很多個線程搶奪資源,只有少數線程搶成功,所以要對大部分線程進行分流處理,只允許少數的線程進入秒殺的服務器端。
削峯:隊友秒殺系統會有大量的線程湧入,所以在搶購一開始的時候會有很高的峯值,可能會對系統產生巨大的衝擊力,把高流浪峯值變成平穩的流量也是秒殺系統很重要的思路,實現削峯的常用方法利用緩存技術和消息中間件(mq,kafaka)等技術,將線程全放在一個消息隊列中,按照進棧出棧進行流量的削峯處理。
異步處理:就是流量削峯處理的一種處理方式。
內存緩存:秒殺系統最大的瓶頸就是對數據庫的讀寫,由於數據庫讀寫屬於磁盤IO,性能很低,所以我們只對系統的業務邏輯和系統數據轉移到緩存裏,避免對數據庫的直接操作,會提高系統的性能。
拓展:想支持更多用戶,更大的併發,將系統設計成彈性的,或進行集羣,可提高系統的性能。

三.設計思路
將請求攔截在系統的上游,降低下游的壓力減少數據庫的壓力。如果不在前段攔截可能造成數據庫讀寫鎖衝突,甚至死鎖,最終請求超時
衝鋒利用緩存:避免對數據庫直接進行讀寫操作,提高系統的讀寫速度。
消息隊列:消息隊列可以削峯,將攔截大量的請求,這也是異步處理,後臺從消息隊列主動拉取消息進行業務處理。

四.秒殺的架構
秒殺界面–>服務器網關(限流,削峯)–>秒殺服務器層–>操作數據庫

五.前段處理
1.頁面靜態化:將活動頁面上的所有可以靜態的元素全部靜態化,儘量減少動態元素,通過CDN來抗峯。
2.進制重複提交:用戶提高完後將按鈕設爲灰色,進制重複提交。
3.用戶限流:在某一時間裏只允許用戶提交一次,可以採用ip限流。

六.後端方案
例如當秒殺量很大時,即使用戶只有一個請求,到服務的請求數量還是很大,比如10w用戶同時搶1000個手機,服務器的請求壓力至少爲10w。
1.採用消息隊列緩存技術:可以先把這些請求全部都寫到消息隊列中緩存一下,數據庫訂閱消息減庫存,減庫存成功的請求返回秒殺成功,是白的返回秒殺結束。
2.採用緩存應對讀請求:對於讀多寫少的業務,大部分是查詢的請求,可以利用緩存分擔數據庫的壓力。
3.利用緩存續寫請求:緩存也是可以應對讀寫請求的,我們可以把數據庫中的庫存數轉移到Redis緩存中,所有減少庫存的操作都在Redis中,然後通過後天進程中的用戶秒殺請求同步到數據庫中。

數據庫層
一般設計在上游就進行攔截過濾,數據庫值承擔“能力範圍”內的請求,所有通過在服務器層引入隊列和緩存,讓最底層的數據庫沒有壓力。

利用消息中間件和緩存實現簡單的秒殺系統
Redis是一個分佈式緩存系統,支持多種數據結構。我們採用Key-value的數據結構,用一個原子類型的的變量值作爲key,把用戶id作爲value,我們使用RPUSH key-value插入秒殺請求,當插入的秒殺請求達到上限請求時停止所有的後續插入,然後用LPOP key讀取秒殺成功的用戶的id,然後在數據庫操作最後的訂單庫存操作。

二redis的事務實現秒殺
redis的事務通常使用watch,mutil,exec,discard來完成,redis的事務不支持回滾,這樣會阻塞其他客戶端的請求
watch:監視一個事務(key);
mutil:開啓一個事務;
exec:執行事務的操作;
discard:結束一個事務;

@Service
public class MiaoShaServiceImpl implements MiaoShaService{

	@Autowired
	private JedisPool jedisPool;

	@Override
	public void miaosha() {
		Jedis jedis = jedisPool.getResource();		
		jedis.watch("productum");
		//得到當前的數量
		String string = jedis.get("productnum");
		//轉化成iteger
		Integer currentNum = Integer.parseInt(string);
		
	}
	
	/*@Override
	public void miaosha() {
		Jedis jedis = jedisPool.getResource();
		Boolean exists = jedis.exists("productnum");
		if(exists) {
			Long decr = jedis.decr("productnum");
			if(decr>0) {
				System.out.println(Thread.currentThread().getName()+"搶到商品"+decr+"搶購人數爲:"+(100-decr+1));
				//想mq發送信息取更新mysql數據庫
				System.out.println("想mq發送信息更新mysql數據庫");
			}else {
				System.out.println(Thread.currentThread().getName()+"庫存不足");
			}
		}else {
			System.out.println(Thread.currentThread().getName()+"redis數據出現異常");
		}*/
	
	
	}
	
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章