document/公司/工作/友达/抽奖算法优化说明.md
2025-05-18 15:14:53 +08:00

4.7 KiB
Raw Blame History

抽奖算法优化说明

优化背景

原有抽奖系统在处理大量库存和高并发抽奖场景时存在性能瓶颈和内存占用过高的问题。同时,在宝箱类型奖品和特殊奖品处理上存在一些不一致性。为了提升系统性能、优化用户体验并确保数据一致性,我们对抽奖核心算法进行了全面优化。

主要改进

1. 随机抽取算法优化

旧算法

$ordinary_prize_all = [];
foreach ($ordinary_prize as $k => $v) {
    $surplus_prize = $v['surplus_stock'];
    if ($surplus_prize > 0) {
        for ($i = 1; $i <= $surplus_prize; $i++) {
            $ordinary_prize_all[] = $v;
        }
    }
}

shuffle($ordinary_prize_all);
shuffle($ordinary_prize_all);

for ($i = 0; $i < $prize_num; $i++) {
    $ordinary_prize_info = $ordinary_prize_all[$i];
    // 处理中奖逻辑...
}

新算法

// 过滤掉库存为0的奖品
$valid_prizes = array_filter($ordinary_prize, function($item) {
    return $item['surplus_stock'] > 0;
});

// 创建权重数组用于加权随机
$weights = [];
foreach ($valid_prizes as $index => $prize) {
    $weights[$index] = $prize['surplus_stock'];
}

// 开普通奖品
for ($i = 0; $i < $prize_num; $i++) {
    // 检查是否还有可抽奖品
    if (empty($weights) || array_sum($weights) <= 0) {
        break;
    }
    
    // 使用加权随机算法选择奖品
    $selected_index = $this->weightedRandom($weights);
    $ordinary_prize_info = $valid_prizes[$selected_index];
    
    // 减少权重以反映库存变化
    $weights[$selected_index]--;
    
    // 如果权重为0则从数组中移除
    if ($weights[$selected_index] <= 0) {
        unset($weights[$selected_index]);
        unset($valid_prizes[$selected_index]);
    }
    
    // 处理中奖逻辑...
}

2. 加权随机算法实现

/**
 * 加权随机算法
 * @param array $weights 权重数组
 * @return int 选中的索引
 */
private function weightedRandom(array $weights)
{
    $sum = array_sum($weights);
    $rand = mt_rand(1, $sum);
    
    foreach ($weights as $index => $weight) {
        $rand -= $weight;
        if ($rand <= 0) {
            return $index;
        }
    }
    
    return array_key_first($weights); // 防止浮点数精度问题导致无法选中
}

3. 特殊奖品宝箱处理统一

为特殊奖品如全局赏、LAST赏、最终赏添加了对宝箱类奖品的支持使其与普通奖品的宝箱处理逻辑保持一致

// 处理宝箱
if ($ordinary_prize_info['goods_type'] == 4) {
    // 查找宝箱奖品
    $goodslist_1 = GoodsList::where(['goods_id' => $goods_id])
        ->where('goods_list_id', '=', $ordinary_prize_info['id'])
        ->select()->toArray();
        
    if (!empty($goodslist_1)) {
        $box_res = $this->ordinary_prize_notice_box($goodslist_1, 1, $order_id, $user_id, $goods_id, $order_type, $num);
        $res = array_merge($res, $box_res);
    }
}

4. 事务处理优化

优化了事务处理逻辑,避免嵌套事务导致的问题,提高数据一致性:

try {
    // 核心抽奖逻辑...
    return $res;
} catch (\Exception $e) {
    // 记录错误日志
    trace('抽奖异常: ' . $e->getMessage(), 'error');
    // 抛出异常,让外层事务处理回滚
    throw $e;
}

优化效果对比

优化方向 旧算法 新算法 改进幅度
内存占用 随库存线性增长 与奖品种类成正比 大幅减少(>90%*
计算效率 O(n*m) O(n*k) 显著提升**
随机分布 基于数组洗牌 精确权重控制 更精确
数据一致性 可能不一致 事务保证 显著增强
错误处理 简单处理 完整异常机制 更健壮

*对于库存量大的商品如1000+),内存节省更为显著
**n为奖品种类数m为库存总量k为抽奖次数

关键优势

  1. 性能显著提升

    • 内存占用减少90%以上(对大库存商品)
    • 处理速度提升,尤其是在大量抽奖场景
  2. 随机公平性增强

    • 每次抽奖都是独立的随机事件
    • 动态调整概率分布,确保与当前库存比例一致
  3. 代码可维护性提高

    • 结构更清晰,责任划分更明确
    • 完整的错误处理机制
  4. 数据一致性保障

    • 完善的事务处理
    • 更健壮的异常处理

后续优化方向

  1. 缓存机制:考虑对奖品信息和库存数据进行缓存,减少数据库查询

  2. 分布式锁:在高并发场景下,可以考虑引入分布式锁确保数据一致性

  3. 抽奖日志:增强抽奖日志记录,便于后续数据分析和问题排查

  4. 性能监控:添加性能监控点,实时掌握抽奖系统的运行状态