用户提交价格重复问题

我当前遇到一个这样的问题,一个招标报价系统,用户提交价格的时候,人数较多的时候,出现高并发,会出现多人提交相同的价格问题,现在需要实现每个用户提交的价格必须唯一,不能多个用户提交相同的价格,请问哪位大神帮忙想个办法,这个问题类似于电商解决秒杀超卖问题.

讨论数量: 5

你这问题太简单了,根本用不到数据库级别的锁,也不需要将所有的用户报价入库,这样都是在浪费IO,并且并发时用户提交失败的可能性非常大,压力全都给到了数据库。很明显不是最优解决方案。

以下是我的解决方案,你可以参考:

  1. 用户提交报价后,将其保存在 Redis 的 zset 合集中
  2. 拍卖结束后,按照 zset 的最高值决定拍卖总价
  3. 取出 zset 所有数据入库
// 模拟并发情况
$userId = rand(1, 1000);
$price = rand(1, 20);

// 使用SETNX指令来判断价格是否存在
if (Redis::setnx('user_quote_price_1_' . $price, "$userId:$price")) {
    // 价格不存在,将用户的报价添加到Redis有序集合中
    Redis::zadd('price_1', $price, "$userId:$price");
} else {
    // 价格已经存在,处理冲突情况
    return '价格已经存在';
}

落库

// 获取所有的报价,并按照价格进行倒序排序
$allPrice = Redis:: zrevrange('price_1', 0, -1, 'WITHSCORES');

// 处理排序后的报价,比如入库等操作
foreach ($allPrice as $key => $price) {
    // 处理报价
    // ...
}

// 清空Redis中的有序集合
Redis::del('price_1');

// 删除 SetNX 创建的 Key
$prefix = config('database.redis.options.prefix'); // 获取Redis前缀配置
$keys = Redis::keys('user_quote_price_*'); // 模糊查询刚刚创建的所有 SetNX 锁
if (!empty($keys)) {
    $cleanedKeys = array_map(function ($key) use ($prefix) {
        return str_replace($prefix, '', $key); // 去除前缀
  }, $keys);

  Redis::del($cleanedKeys); 
}

测试脚本

ab -n 100 -c 100 https://(你的报价路由)

100次并发请求,不会有任何重复数据,简单吧?

6个月前 评论
CodingHePing 6个月前

有用户请求的时候锁住

6个月前 评论
小民爱Laravel

锁机制,队列应该都能解决这个问题

6个月前 评论

队列加锁还有mysql 行级锁

6个月前 评论

你这问题太简单了,根本用不到数据库级别的锁,也不需要将所有的用户报价入库,这样都是在浪费IO,并且并发时用户提交失败的可能性非常大,压力全都给到了数据库。很明显不是最优解决方案。

以下是我的解决方案,你可以参考:

  1. 用户提交报价后,将其保存在 Redis 的 zset 合集中
  2. 拍卖结束后,按照 zset 的最高值决定拍卖总价
  3. 取出 zset 所有数据入库
// 模拟并发情况
$userId = rand(1, 1000);
$price = rand(1, 20);

// 使用SETNX指令来判断价格是否存在
if (Redis::setnx('user_quote_price_1_' . $price, "$userId:$price")) {
    // 价格不存在,将用户的报价添加到Redis有序集合中
    Redis::zadd('price_1', $price, "$userId:$price");
} else {
    // 价格已经存在,处理冲突情况
    return '价格已经存在';
}

落库

// 获取所有的报价,并按照价格进行倒序排序
$allPrice = Redis:: zrevrange('price_1', 0, -1, 'WITHSCORES');

// 处理排序后的报价,比如入库等操作
foreach ($allPrice as $key => $price) {
    // 处理报价
    // ...
}

// 清空Redis中的有序集合
Redis::del('price_1');

// 删除 SetNX 创建的 Key
$prefix = config('database.redis.options.prefix'); // 获取Redis前缀配置
$keys = Redis::keys('user_quote_price_*'); // 模糊查询刚刚创建的所有 SetNX 锁
if (!empty($keys)) {
    $cleanedKeys = array_map(function ($key) use ($prefix) {
        return str_replace($prefix, '', $key); // 去除前缀
  }, $keys);

  Redis::del($cleanedKeys); 
}

测试脚本

ab -n 100 -c 100 https://(你的报价路由)

100次并发请求,不会有任何重复数据,简单吧?

6个月前 评论
CodingHePing 6个月前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!