1. 程式人生 > >搶紅包案例分析以及程式碼實現

搶紅包案例分析以及程式碼實現

概述

電商的秒殺、搶購,春運搶票,微信QQ搶紅包,從技術的角度來說,這對於Web 系統是一個很大的考驗. 高併發場景下,系統的優化和穩定是至關重要的.

網際網路的開發包括 Java 後臺、 NoSQL、資料庫、限流、CDN、負載均衡等內容, 目前並沒有權威性的技術和設計,有的只是長期經驗的總結,但是使用這些經驗可以有效優化系統,提高系統的併發能力.

我們接下來的幾篇博文主要討論 Java 後臺、 NoSQL ( Redis )和資料庫部分技術.

搶紅包案例

主要分以下幾大部分:

  1. 環境搭建

  2. 模擬超量傳送的場景-DataBase(MySql5.7)

  3. 悲觀鎖的實現版本-DataBase(MySql5.7)

  4. 樂觀鎖的實現版本-DataBase(MySql5.7)

  5. Redis實現搶紅包

案例關注點

模擬 20 萬元的紅包,共分為 2 萬個可搶的小紅包,有 3 萬人同時搶奪的場景 ,模擬出現超發和如何保證資料一致性的問題。

在高併發的場景下,除了資料的一致性外,還要關注效能的問題 , 因為一般而言 , 時間太長使用者體驗就會很差,所以要測試資料一致性和系統的效能

工程結構

640

庫表設計

MySql5.7

/*==============================================================*/
/* Table: 紅包表                                        */
/*==============================================================*/

create table T_RED_PACKET
(
  id                   int(12)                        not null auto_increment COMMENT '紅包編號',
  user_id              int(12)                        not null COMMENT '發紅包的使用者id',
  amount               decimal(16,2)                  not null COMMENT '紅包金額',
  send_date            timestamp
                     not null DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '發紅包日期',
  total                int(12)                        not null COMMENT '紅包總數',
  unit_amount          decimal(12)                    not null COMMENT '單個紅包的金額',
  stock                int(12)                        not null COMMENT '紅包剩餘個數',
  version              int(12) default 0              not null COMMENT '版本(為後續擴充套件用)',
  note                 varchar(256)                    null COMMENT '備註',,
  primary key clustered (id)
);

紅包表表示存放紅包的是一個大紅包的資訊,它會分為若干個小紅包,為了業務簡單,假設每一個紅包是等額的。而對於搶紅包而言,就是從大紅包中搶奪那些剩餘的小紅包,剩餘紅包數會被記錄在紅包表中。 兩個表有外來鍵關聯 T_RED_PACKET.id = T_USER_RED_PACKET.red_packet_id

/*==============================================================*/
/* Table: 使用者搶紅包表                                                */
/*==============================================================*/
create table T_USER_RED_PACKET
(
  id                   int(12)                        not null auto_increment COMMENT '使用者搶到的紅包id',
  red_packet_id        int(12)                        not null COMMENT '紅包id',
  user_id              int(12)                        not null COMMENT '搶紅包使用者的id',
  amount               decimal(16,2)                  not null  COMMENT '搶到的紅包金額',
  grab_time            timestamp                      not null DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '搶紅包時間',
  note                 varchar(256)                   null COMMENT '備註',
   primary key clustered (id)
);
/**
* 插入一個20萬元金額,2萬個小紅包,每個10元的紅包資料
*/

insert into T_RED_PACKET(user_id, amount, send_date, total, unit_amount, stock, note)
values(1, 200000.00, now(), 20000, 10.00, 20000,'20萬元金額,2萬個小紅包,每個10元');
commit;

這樣就建好了兩個表,並且將一個 20 萬元金額,2 萬個小紅包,每個 10 元的紅包資訊插入到了紅包表中,用作模擬資料。

Domain

有了這兩個表,我們就可以為這兩個表建兩個 POJO 了,讓這兩個表和 POJO 對應起來,這兩個 POJO 為 RedPacket 和 UserRedPacket,實現類序列化介面。

紅包資訊

package com.artisan.redpacket.pojo;

import java.io.Serializable;
import java.sql.Timestamp;

/**
*
*
* @ClassName: RedPacket
*
* @Description: 紅包表對應的實體類,可序列化
*
* @author: Mr.Yang
*
* @date: 2018年10月8日 下午3:42:58
*/

public class RedPacket implements Serializable {

 private static final long serialVersionUID = 9036484563091364939L;
 // 紅包編號
 private Long id;
 // 發紅包的使用者id
 private Long userId;
 // 紅包金額
 private Double amount;
 // 發紅包日期
 private Timestamp sendDate;
 // 紅包總數
 private Integer total;
 // 單個紅包的金額
 private Double unitAmount;
 // 紅包剩餘個數
 private Integer stock;
 // 版本(為後續擴充套件用)
 private Integer version;
 // 備註
 private String note;
 // 省略set/get
}

搶紅包資訊

package com.artisan.redpacket.pojo;

import java.io.Serializable;
import java.sql.Timestamp;

/**
*
*
* @ClassName: UserRedPacket
*
* @Description: 使用者搶紅包表
*
* @author: Mr.Yang
*
* @date: 2018年10月8日 下午3:47:40
*/

public class UserRedPacket implements Serializable {

 private static final long serialVersionUID = 7049215937937620886L;

 // 使用者紅包id
 private Long id;
 // 紅包id
 private Long redPacketId;
 // 搶紅包的使用者的id
 private Long userId;
 // 搶紅包金額
 private Double amount;
 // 搶紅包時間
 private Timestamp grabTime;
 // 備註
 private String note;
 // 省略set/get
}

Dao層實現

MyBatis Dao介面類及對應的Mapper檔案

使用 MyBatis 開發,先來完成大紅包資訊的查詢先來定義一個 DAO 物件

package com.artisan.redpacket.dao;

import org.springframework.stereotype.Repository;

import com.artisan.redpacket.pojo.RedPacket;


@Repository
public interface RedPacketDao {
 
 /**
  * 獲取紅包資訊.
  * @param id --紅包id
  * @return 紅包具體資訊
  */

 public RedPacket getRedPacket(Long id);
 
 /**
  * 扣減搶紅包數.
  * @param id -- 紅包id
  * @return 更新記錄條數
  */

 public int decreaseRedPacket(Long id);
 
 
}

其中的兩個方法 , 一個是查詢紅包,另一個是扣減紅包庫存。

搶紅包的邏輯是,先查詢紅包的資訊,看其是否擁有存量可以扣減。如果有存量,那麼可以扣減它,否則就不扣減。

接著將對應的Mapper對映檔案編寫一下

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.artisan.redpacket.dao.RedPacketDao">

 <!-- 查詢紅包具體資訊 -->
 <select id="getRedPacket" parameterType="long"
   resultType="com.artisan.redpacket.pojo.RedPacket">

   select id, user_id as userId, amount, send_date as
   sendDate, total,
   unit_amount as unitAmount, stock, version, note from
   T_RED_PACKET
   where id = #{id}
 </select>

 <!-- 扣減搶紅包庫存 -->
 <update id="decreaseRedPacket">
   update T_RED_PACKET set stock = stock - 1 where id =
   #{id}
 </update>


</mapper>

這裡getRedPacket並沒有加鎖這類動作,目的是為了演示超發紅包的情況。

然後是搶紅包的設計了 ,先來定義插入搶紅包的 DAO ,緊接著是Mapper對映檔案

package com.artisan.redpacket.dao;

import org.springframework.stereotype.Repository;

import com.artisan.redpacket.pojo.UserRedPacket;

@Repository
public interface UserRedPacketDao {

 /**
  * 插入搶紅包資訊.
  * @param userRedPacket ——搶紅包資訊
  * @return 影響記錄數.
  */

 public int grapRedPacket(UserRedPacket  userRedPacket);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
 PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.artisan.redpacket.dao.UserRedPacketDao">
   <!-- 插入搶紅包資訊 -->
   <insert id="grapRedPacket" useGeneratedKeys="true"
   keyProperty="id" parameterType="com.artisan.redpacket.pojo.UserRedPacket">

     insert into T_USER_RED_PACKET( red_packet_id, user_id, amount, grab_time, note)
     values (#{redPacketId}, #{userId}, #{amount}, now(), #{note})
   </insert>
</mapper>

這裡使用了 useGeneratedKeys 和 keyPrope町,這就意味著會返回資料庫生成的主鍵資訊,這樣就可以拿到插入記錄的主鍵了 , 關於 DAO 層就基本完成了。別忘了單元測試!!!

Service層實現

接下來定義兩個 Service 層介面,分別是 UserRedPacketService和RedPacketService

package com.artisan.redpacket.service;

import com.artisan.redpacket.pojo.RedPacket;


public interface RedPacketService {
 
 /**
  * 獲取紅包
  * @param id ——編號
  * @return 紅包資訊
  */

 

相關推薦

紅包案例分析以及程式碼實現

概述電商的秒殺、搶購,春運搶票,微信QQ搶紅包,從技術的角度來說,這對於Web 系統是一個很大的

紅包案例分析以及程式碼實現(三)

前文回顧接下來我們使用樂觀鎖的方式來修復紅包超發的bug樂觀鎖樂觀鎖是一種不會阻塞其他執行緒併發

紅包案例分析以及程式碼實現(三) 侵立刪

轉自:https://mp.weixin.qq.com/s/Pp-nCYrzXXXfLcFFS_ttWg   前文回顧 搶紅包案例分析以及程式碼實現(一) 搶紅包案例分析以及程式碼實現(二) 接下來我們使用樂觀鎖的方式來修復紅包超發的bug

紅包案例分析以及程式碼實現(二) 侵立刪

轉自:https://mp.weixin.qq.com/s/F1U1nUK2KF5R0nxT8lmfBg   概述 上一篇文章中使用ssm+mysql實現,存在併發超發問題,這裡我們使用悲觀鎖的方式來解決這個邏輯錯誤,並驗證資料一致性和效能狀況。 超發問題分析 針對

紅包案例分析以及程式碼實現(一) 侵立刪

轉自:https://mp.weixin.qq.com/s/d3HyAtWua38TSpelF-v6nQ   概述 電商的秒殺、搶購,春運搶票,微信QQ搶紅包,從技術的角度來說,這對於Web 系統是一個很大的考驗. 高併發場景下,系統的優化和穩定是至關重要的. 網際網路的開

紅包案例分析

概述 電商的秒殺、搶購,春運搶票,微信QQ搶紅包,從技術的角度來說,這對於Web 系統是一個很大的考驗. 高併發場景下,系統的優化和穩定是至關重要的. 網際網路的開發包括 Java 後臺、 NoSQL、資料庫、限流、CDN、負載均衡等內容, 目前並沒有權威性的技術和設計,

使用者註冊登入案例分析以及簡單實現

使用者註冊登入案例 功能分析 本案例主要是使用者註冊和登入功能。根據使用者輸入的使用者名稱和密碼判斷使用者能否登入進去。使用者的資訊儲存在userInformation.txt檔案中。 分析順序如下: 1. 分包和建類 2. 實現功能 分

高併發-【紅包案例】之四:使用Redis+Lua指令碼實現紅包並非同步持久化到資料庫

文章目錄導讀概述 導讀 概述 上面三篇博文是使用的MySql資料庫來作為資料的載體資料最終會將資料儲存到磁碟中,而Redis使用的是記憶體,記憶體的速度比磁碟速度肯定要快很多. 對於使用 Redis實現搶紅包,首先需要知道的是Redis的功能不如資料庫

PHP 控制反轉與依賴注入詳細分析程式碼實現

PHP有很多的設計模式,比如單例模式,訂閱模式,策略模式,工廠模式,觀察者模式,這些設計模式其實無非都是為了讓程式簡化,容易維護,模組間解耦。現在我們來講講PHP的另外一種設計模式,控制反轉/依賴注入,這兩者其實是同一個概念,只是凶不同的角度去解釋的而已。 依賴注入:是從需要實現的業務邏輯上面去

Hash表分析以及Java實現

   這篇部落格主要探討Hash表中的一些原理/概念,及根據這些原理/概念,自己設計一個用來存放/查詢資料的Hash表,並且與JDK中的HashMap類進行比較。 我們分一下七個步驟來進行。  一。    Hash表概念 二 . &n

氣泡排序,選擇排序、二分法查詢圖示以及程式碼實現

氣泡排序 請看下面的這個栗子: 需要排序的陣列arr = {99,88,77,55,66,44}; 具體排序細節各位客官請看圖: 程式碼實現: public class BubbleSort { public static void main(String[] ar

高併發紅包案列以及使用鎖,版本號,redis快取解決,專案可執行,詳細註釋(三)

1redis搶紅包實現 在redis中首先設定紅包的數量和金額,使用者搶到紅包之後,在redis中計算紅包數量-1,儲存使用者的資訊,直到紅包被搶完。再將使用者資訊批量儲存到資料庫中。由於redis的計算是原子性的,所以不會出現資料錯誤,可以理解成atomic系列 具體的環境搭建請檢視

高併發紅包案列以及使用鎖,版本號,redis快取解決,專案可執行,詳細註釋(二)

1 悲觀鎖 <!-- 查詢紅包具體資訊 --> <select id="getRedPacketForUpdate" parameterType="int"     resultType="test814RedPacket.pojo.Red

高併發紅包案列以及使用鎖,版本號,redis快取解決,專案可執行,詳細註釋(一)

 1.問題描述 簡單來說就是當大量資料來訪問資料庫的時候,可能導致資料不一致。如下: 發一個2000元的大紅包,總共2000個小紅包,每個一元,但是有30000個人去搶,紅包少一個就減一,插入搶紅包使用者資訊,結果看圖:  stock表示餘留的紅包數,結果是負一

高併發-「紅包案例」之一:SSM環境搭建及復現紅包超發問題

文章目錄 概述 搶紅包案例 案例關注點 工程結構 庫表設計 Domain Dao層實現 Service層實現 使用全註解搭建SSM 開發環境 Controller層 View層 執行測試 超量傳送的BUG驗證 超發問題解決思路 概述 電商的秒殺、搶購,春運搶票,微信QQ搶

大資料MR模型以及程式碼實現

資料:     [customers.txt] 1,tom,12     2,tom,13     3,tom,14     4,tom,15   &

機器學習系列文章:Apriori關聯規則分析演算法原理分析程式碼實現

1.關聯規則淺談     關聯規則(Association Rules)是反映一個事物與其他事物之間的相互依存性和關聯性,如果兩個或多個事物之間存在一定的關聯關係,那麼,其中一個事物就能通過其他事物預測到。關聯規則是資料探勘的一個重要技術,用於從大量資料中挖掘出有價值的資料

電商產品評論的資料情感分析python程式碼實現

步驟1:從爬取的資料中提取對應的評論資訊 #-*- coding: utf-8 -*- import pandas as pd inputfile = '.../huizong.csv' #評論彙總檔案 outputfile = '.../meidi_jd.t

高併發-【紅包案例】之一:SSM環境搭建及復現紅包超發問題

概述 電商的秒殺、搶購,春運搶票,微信QQ搶紅包,從技術的角度來說,這對於Web 系統是一個很大的考驗. 高併發場景下,系統的優化和穩定是至關重要的. 網際網路的開發包括 Java 後臺、 NoSQL、資料庫、限流、CDN、負載均衡等內容, 目前並沒有權威性

ML-01-線性迴歸以及程式碼實現

一、表示方式以及數學符號解釋 準備工作: 訓練資料集 attr1 attr2 value 1.3 1.9 3.2 1.1 1.5 2.5 1.5 2.3 3.9 1.7 2.7 4.6