前言:最近作關於優惠券的開發,可是發現優惠券量大了以後,性能徹底跟不上,庫中存200萬條優惠券,發一張券居然須要5分鐘之久,而後我就着手優化,最終到發一張券只須要15毫秒左右,如今把整個思路以及代碼貼出來,供你們一塊兒討論和學習。前端
主要實現優惠券促銷活動,首先建立活動,而後建立券組,採用預處理的方式提早進行制券,在初版本主要實現,功能的基本業務。而後在分支實現,大數量和高併發問題。mysql
1:解決優惠券編碼重複問題,原先採用的是獲取數據庫全部的券,而後去比對是否重複,若是庫數據量達百萬的時候就會出現很是緩慢,並且會 出現常常制券失敗等,因此此版本捨棄原先採用隨機數的模式,經過推特的雪花算法來避免惟一,可是依然保留優惠券前綴和後綴。
2:由原來的異步採用線程修改成線程池,覺得高併發時候存在大量的線程佔內存空間。
3:由原來制券採用for循環模式修改成批量制券,並且採用分配插入優惠券,一批次目前定爲5000.
4:加入消息隊列(採用rabbitMQ)對於某一批次添加失敗,把失敗的放入對列中,經過隊列進行補救,已到達高可用。避免大批量優惠券來回從新導入 消息隊列對於異常信息拒絕解決並重返消息隊列中,配置2個消費者以免其中一個服務異常,消息處理出現死循環git
1:加入操做日誌
目的:跟蹤熱點數據,查詢日誌快速跟蹤應用程序中的慢查詢或慢操做,爲後面的優化奠基基礎
2:加入異常日誌
目的:快速的獲取線程的異常問題,經過日誌中的數據能快速修改
3:採用技術 經過aop和rabbitmq中間件來作,這樣減小因爲日誌問題給程序帶來的效率問題github
採用數據庫mysql
數據:添加25個有效活動,每一個活動下分別有2個券組,每一個券組下制券是5萬張。優惠券表中250萬條記錄
業務:一個會員消費同時知足這25個活動要送50張優惠券。
統計:整個發券過程通過10次統計得出大約消耗是306s,其中每次獲取優惠券耗時6s。若是屢次循環必然帶來性能的瓶頸
更新優惠券狀態大約耗時是0.5s,從上咱們能夠看出咱們的性能問題主要出在獲取優惠券上。因此才1.3版本主要經過程序來解決這個問題redis
目的:經過程序代碼和優化數據庫來提升性能
具體方案:
1:之前獲取券組下全部的優惠券如今修改成每次只獲取100條(經測試統計得出發送50張券消耗時間是106s,每次獲取優惠券大約耗時是2s多,總體性能提高近3倍)
2:優化sql,加入組合索引(統計得出發送50張優惠券消耗總時間是2.5s,每次獲取優惠券大約耗時是0.015s,總體的性能提高了近42倍)
3:加入本地緩存(若是一次性獲取的優惠券先放入map中,那麼下次若是還有就不須要從庫中獲取優惠券。統計發現:10件商品,每件商品發50張優惠券
不加本地緩存效率耗時是7.5s,加入本地緩存後耗時約5.5s,總體性能提高了2s)
效果分析:
4:對於發券採用批量更新來替代for循環(由上面的約5.5s性能提高爲大約4.8s)算法
目的:經過異步和消息隊列來進行發券
具體方案:
1:經過異步進行發券,這樣能夠提升cpu的利用率,同時經過消息隊列來保證穩定性,避免出現異常致使返回前端發券成功,可是異步制券時候出現異常在發500張優惠券的時候效率大約提高了0.5s
2:對代碼進行一次重構
原則:把大方法修改小方法,每一個小方法處理一個業務,好比獲取活動,那麼這個方法的職責就是獲取活動,同時每一個小方法儘可能有返回值,這樣能夠增長代碼的可讀性sql
1:採用redis作緩存,取當天有效的活動,活動下券組,券組下500張券存入緩存中。
2:加入定時任務,在天天12點時候更新緩存(這個時間能夠經過熱點數據來監控)
3:統計結果發現:
加入緩存後發送500張優惠券耗時只有2.7s,比以前的4.8s快了2.1s,大大的提高了性能數據庫
總結:代碼我就不貼,你們能夠本身去看。感興趣的朋友能夠在這個基礎繼續研發學習。在版本1.6可能加入分庫分表,目前想採用的是噹噹的sharding-jdbc緩存
源碼地址併發