秒殺問題:前端
1. 前端:redis
2. 後端:算法
商品超賣: 數據庫樂觀鎖(CAS無鎖)、 Redis分佈式鎖、MQ異步形式修改庫存(用戶須要等待)docker
單機壓力大:單獨一服務形式部署+docker。能夠實現快速擴容數據庫
用戶操做頻率塊:網關限流後端
用戶做弊:緩存
數據庫訪問壓力大: 分表分庫、使用MQ異步實現修改庫存。相似:搶票等待30s才知道搶票結果。服務器
前端優化方案: 網絡
舉個例子:若是1m帶寬等於128kb/s加載一個網頁640kb。須要 640kb/128kb=5s. 若是秒殺時候網頁加載不出來就完蛋了。併發
這個就牽涉到一個帶寬入口問題,服務器生產環境買的帶寬。
優化方案: 動靜分離 能夠經過CDN完成, 能夠參考使用七牛雲的配置使用
Nginx配置以下:
events {
#的最大鏈接數(包含全部鏈接數)1024
worker_connections 1024; ## Default: 1024
}
http{
# 代理緩存配置
proxy_cache_path "./toov5_cachedata" levels=1:2 keys_zone=meitecache:256m inactive=1d max_size=1000g;
server {
listen 80;
location /{
#使用緩存名稱
proxy_cache toov5cache;
#對如下狀態碼實現緩存
proxy_cache_valid 200 206 304 301 302 1d;
#緩存的key
proxy_cache_key $request_uri;
add_header X-Cache-Status $upstream_cache_status;
#反向代理地址
proxy_pass http://127.0.0.1:9500;
}
}
}
同時在Nginx目錄下建立:
toov5_cachedata
可是這樣,若是商品詳情頁修改了,對於緩存怎麼辦? 能夠經過Nginx+Lua+OpenResty實現優化。或者把url後面拼接時間戳。
後端:
數據庫設計:
秒殺商品表:verson:樂觀鎖。 seckill_id 秒殺商品ID
秒殺訂單記錄表:
超賣問題:
超賣方案一:
修改庫存時候: update toov5_seckill set inventory = inventory -1 where seckill_id = ? and inventory >0 ; 前提是庫存必須大於0! 數據庫自帶行鎖!
超賣方案二:
先查詢版本號: select version from toov5_seckill where seckill_id = '10023';
修改數據時候:update toov5_seckill set inventory = inventory -1, version = version +1 where seckill_id = ? and version = ' ' and inventory > 0
至關於某個線程獲取到鎖,去執行。
分析:
方案一: 使用數據庫自帶的行鎖機制,100個商品,若是200個請求,100個能夠成功。
方案二: 樂觀鎖控制, 100個商品,若是200請求,只有一部分能夠搶購成功。能防止一會兒都搶購完。
用戶訪問頻率問題:
用戶頻率的限制: 不能讓一個用戶把庫存全部的都搶購了,或者用戶持續請求訪問。
方案: 讓用戶請求間隔10s
具體: Redis,單線程,setNx 成功失敗返回值。 key: phone value: seckillId expireTime: 10s
數據庫高併發IO操做問題:
問題: 若是秒殺的請求過多,對數據庫頻繁的IO操做,可能會數據庫崩潰。 即使是分庫分表,讀寫分離也是無濟於事的。
方案: MQ+令牌桶
具體: 提早生成好對應令牌,放在令牌桶。經過MQ異步發送,實現修改庫存。(拿到令牌的才能夠去修改庫存)。
好比:100個庫存,10w個請求
提早生成100個token,誰能獲取到令牌,獲取到令牌後,MQ異步實現庫存修改。MQ也能夠進行流量削峯。
只有一部分能執行上面的 update語句。
注意: 採用MQ實現秒殺接口,用戶不能一次拿到秒殺結果。
具體: 1.前端調用秒殺接口,若是秒殺成功的話,返回正在排隊中...
2. 前端經過定時器,使用token查詢是否秒殺成功。(MQ消費速度很快的狀況下)
注意生成token的Redis採用的數據類型: key:庫存Id value: list. 每次從redis獲取token後刪除redis的token。直到沒有token爲止,此時表明商品搶購完了。
注意:生產者和消費者不能在同一個服務,消費者單獨起一個服務。要不一個失敗了都失敗了。生產者掛了,不影響消費者。
限流方案:
網關層面: 經過令牌桶算法限流,每秒往令牌桶放入令牌。
限流算法:
關於令牌桶算法是市面上比較常見的方案,原理圖:
拿不到令牌的場景就是,高併發請求量比較大,請求過多時候。一秒200個令牌,則一秒200個請求。
原理:
以規定的速率往令牌桶中存入Token,用戶請求必須獲取到令牌中的Token才能夠處理 請求,若是沒有從令牌桶中獲取到令牌則丟失該請求。
例如:令牌桶中最多隻能存放20個Token,以規定速率存入Token實如今高併發狀況下 限流
優點:控制請求的速率勻速相等的 1s/10r
關於漏桶實現原理圖:
漏桶算法原理:
水(請求)先進入到漏桶裏,漏桶以必定的速度出水,當水流入速度過大會直接溢出,能夠看出漏桶算法能強行限制數據的傳輸速率。
區別:
漏桶算法與令牌桶算法在表面看起來相似,很容易將二者混淆。但事實上,這二者具備大相徑庭的特性,且爲不一樣的目的而使用。 漏桶算法與令牌桶算法的區別在於,漏桶算法可以強行限制數據的傳輸速率,令牌桶算法可以在限制數據的平均傳輸速率的同時還容許某種程度的突發傳輸。 須要注意的是,在某些狀況下,漏桶算法不可以有效地使用網絡資源,由於漏桶的漏出速率是固定的,因此即便網絡中沒有發生擁塞,漏桶算法也不能使某一個單獨的數據流達到端口速率。所以,漏桶算法對於存在突發特性的流量來講缺少效率。而令牌桶算法則可以知足這些具備突發特性
RateLIimiter源碼中,有個線程異步往令牌桶放入token. 能夠進行配置。
問題: 若是Tomcat50個線程已經處理請求訪問了,同時咱們本身的查詢用戶秒殺結果的接口請求。這樣的話是不行的,可能致使服務雪崩效應。須要進行線程池隔離!經過Hystrix就能夠。