我們希望有人購買時檢查商品數(shù)量是否足夠,如果庫存有剩余那么就讓用戶購買成功,之后變更庫存,假如用戶排隊挨個購買這樣當(dāng)然沒有問題。
可是實(shí)際情況下,可能是用戶多個用戶同時來購買,同時檢查庫存,這是可能庫存僅夠其中一人購買,但是由于庫存還沒減掉,就會出現(xiàn)幾個人都購買成功,然后庫存減為負(fù)數(shù)出現(xiàn)超賣的情況。這在大量用戶在同一時間點(diǎn)同時購買時極可能出現(xiàn)。
于是我們調(diào)整一下順序,有用戶購買時我們先減掉庫存,那你肯定要問,怎么減?庫存不夠一個人的時候也減?
我們假設(shè)每份商品有一個唯一的購買碼(開始搶購前預(yù)先生成),用戶搶到購買碼的數(shù)量即他買到的份數(shù),那么有用戶購買時我們第一步就是給幸運(yùn)碼的狀態(tài)由有效更改為無效,并為其標(biāo)記上其購買者ID
這樣其實(shí)mysql會給我們一個返回結(jié)果,叫做影響行數(shù),就是說這條語句更新影響了多少行的數(shù)據(jù),這個影響行數(shù)就是他實(shí)際購買到的商品份數(shù),如果影響行數(shù)為0,就說明一份也沒購買成功,也就意味著商品已經(jīng)搶購?fù)瓿闪恕?/p>
java實(shí)現(xiàn):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
|
/** * 生成商品的購買碼<大量數(shù)據(jù)插入> * * @param goodsIssue * @author Nifury */ public void insertLotteryNumbers(GoodsIssue goodsIssue) { String prefix = "INSERT INTO `lottery_number` (`goods_id`, `periods`,`luck_number`, `create_time`, `status`, `issue_id` ) VALUES \n" ; Timestamp now = new Timestamp(System.currentTimeMillis()); Connection con = null ; try { con = jdbcTemplate.getDataSource().getConnection(); con.setAutoCommit( false ); PreparedStatement pst = con.prepareStatement( "" ); Long total = goodsIssue.getTotalShare(); // 總?cè)舜?/code> for ( int i = 0 ; i < total; i += 10000 ) { // 1萬條提交一次 StringBuffer suffix = new StringBuffer(); List<Integer> numbers = new ArrayList<Integer>(); for ( int j = 0 ; j < 10000 && i+j < total; j++) { numbers.add( 10000001 + i + j); } Collections.shuffle(numbers); //打亂幸運(yùn)碼 for ( int n = 0 ,length = numbers.size(); n < length; n++) { suffix.append( "(" + goodsIssue.getGoodsId() + "," + goodsIssue.getPeriods() + "," + numbers.get(n) + ",'" + now.toString() + "'," + 1 + "," + goodsIssue.getIssueId() + ")\n," ); } // 構(gòu)建完整sql String sql = prefix + suffix.substring( 0 , suffix.length() - 2 ); pst.addBatch(sql); pst.executeBatch(); con.commit(); } con.setAutoCommit( true ); // 還原 pst.close(); con.close(); } catch (Exception e) { e.printStackTrace(); try { // 事務(wù)回滾 con.rollback(); con.setAutoCommit( true ); con.close(); } catch (SQLException e1) { e1.printStackTrace(); } // 還原 } } |
分配購買碼(我們的業(yè)務(wù)需要給購買用戶展示購買碼,所以有返回)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
/** * 通過商品issue_id(每期每個商品有唯一issue_id)來隨機(jī)獲取購買碼(使用的購買碼會設(shè)為失效狀態(tài)) * @param issueId * @param amount 需要獲取的購買碼的數(shù)量 * @param userId * @return LotteryNumber對象列表 * @author Nifury 2016-7-22 */ public List<LotteryNumber> queryByNewIssueId2(Long issueId, Long amount,Long userId) { List<LotteryNumber> numberList = new ArrayList<LotteryNumber>(); try { long currentTime=System.currentTimeMillis(); String updateUserId = "UPDATE `lottery_number` SET `status` = 0,`user_id` = ?,`current_time`= ? WHERE `issue_id` = ? AND `status`=1 LIMIT ? " ; int rownum=jdbcTemplate.update(updateUserId, userId, currentTime, issueId, amount ); if (rownum> 0 ){ //還有剩余有效購買碼 Object[] buyargs={issueId, userId ,currentTime}; numberList = jdbcTemplate.query(QUERY + " WHERE `issue_id` = ? AND `status` = 0 AND `user_id` = ? AND `current_time`= ?" , buyargs, LotteryNumberMapper); } } catch (DeadlockLoserDataAccessException e) { System.out.println( "----分配購買碼出現(xiàn)死鎖,用戶分得0個購買碼-----" ); } return numberList; } |
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持服務(wù)器之家。
原文鏈接:http://blog.csdn.net/Nifury/article/details/52335084