抽獎系統的設計

抽獎模塊設計

思路

管理員在後臺設置獎品的中獎概率,未中獎概率 = 1-中獎概率之和

僞隨機數生成函數用於生成0-1之間的隨機數,參考了官方手冊中mt_getrandmax示例。
http://php.net/manual/zh/function.mt-getrandmax.php

  1. 處理獎項數組,增加未中獎選項的概率
  2. 獲得僞隨機數
  3. 遍歷獎項數據
  4. 通過獎項的獲獎概率,設置座標的左右區間
  5. 比較隨機數是否落在區間

代碼

<?php
function randomFloat($min = 0, $max = 1) {
    return $min + mt_rand() / mt_getrandmax() * ($max - $min);
}
$lottery_arr = [
    ['name'=>'非洲5日遊','chance'=>0.1],
    ['name'=>'朝鮮3日遊','chance'=>0.2],
    ['name'=>'雲南3日遊','chance'=>0.3],
    ['name'=>'京東購物卡','chance'=>0.25]
];

$lottery_arr[] = ['name'=>'謝謝參與','chance'=>(1-array_sum($lottery_arr))];

$res_arr = [
    '非洲5日遊'=>0,
    '朝鮮3日遊'=>0,
    '雲南3日遊'=>0,
    '京東購物卡'=>0,
    '謝謝參與'=>0
];

for($i=0;$i<10000;$i++){
    $randomFloat = randomFloat();
    $baseFloat = 0;
    foreach($lottery_arr as $item){
        if( $randomFloat > $baseFloat && $randomFloat <= ($baseFloat+$item['chance']) ){
            $res_arr[$item['name']]++;
        }
        $baseFloat += $item['chance'];
    }
}

var_dump($res_arr);

數據庫模塊設計

限制請求次數

在執行抽獎流程之前,應先判斷用戶上次抽獎的時間以及總共的抽獎次數,對於不符合要求的無需進入抽獎流程。

避免超發問題

用戶抽中獎之後,一般會檢查獎品剩餘量,然後再 修改獎品的數量。

SELECT used_num,total_num from awards WHERE id = 1;

如果 total_num> used_num,則

UPDATE awards SET used_num = used_num+1 WHERE id = 1;

假設此時total_num = 100,used_num = 99。

由於讀操作沒有加鎖,進程1讀取used_num和total_num是符合要求的。

但是在更新used_num之前時,進程2也讀取了used_num和total_num的值發現自己也是符合要求的。

由於進程1在更新used_num字段,鎖了該行,進程2則在進程1更新之後,再次更新了used_num字段,此時used_num=101,超發了一個。

解決該問題有兩種方案,分別是悲觀鎖和樂觀鎖,其中樂觀鎖的併發性能夠更好一些。

悲觀鎖

修改SELECT語句(悲觀鎖在事務中生效,事務commit或rollback之後,釋放鎖)

SELECT used_num,total_num from awards WHERE id = 1 FOR UPDATE

樂觀鎖

修改UPDATE語句

UPDATE awards SET used_num = used_num+1 WHERE id = 1 AND used_num<total_num;

由於在SELECT語句之後,還需要判斷 used_num < total_num 是否成立,然後才判斷是否需要更新,因此樂觀鎖的方案更好一些。

保證事務的原子性

以下操作應該作爲一個事務執行

  1. 用戶已抽獎次數的增加(或剩餘抽獎次數的減少)
  2. 獎品數據的減少(如果抽中獎品)
  3. 抽獎結果記錄的插入
發佈了189 篇原創文章 · 獲贊 64 · 訪問量 47萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章