一次并发处理过程, 基于 Redis

场景复现

  1. APP: 牌桌游戏APP
  2. 业务流程:
    • 每一局牌桌有四个人, 每一局牌桌结束后, 多个APP客户端同时上传游戏中的用户id信息;
    • 后端给每个用户分配称号, 有多种称号, 称号等级越高, 获得的概率越低.
    • 称号会存到数据库, 一定时间段会重置.
    • 后端分配称号后存储用户称号, 存储牌局结果, 响应结果给客户端
  3. 问题:
    • 牌局结束后, 多个客户端会同时上传相同的牌局结果, 因为不是完全从数据库获取数据(有些用户是新用户, 会重新随机出来并写入用户表中, 有些用户称号被定时随机了)
    • 并发状态下会造成多客户端获取的结果不一致, 同时也会写入多条牌局结果到数据库中(正确情况下应该是只有一条), 重复写入多条用户数据到用户表中.

处理方案

方案1: 由于是多客户端同时请求, 客户端要求的响应时间不能太长, 数据库加锁的方式太慢, 不采用
方案2: 使用 redis setnx, 保证同一个用户的称号在不同的客户端请求中, 结果一致.

处理过程

遍历用户数据时, 对每一个用户, 首先生成用户称号, 并使用redis setnx设置key, value 为用户称号, 保证多客户端的用户称号是相同的.
因为多客户端上传数据相同, 还要保证写数据库时数据是唯一的(每个用户只有一条用户数据, 每一局比赛只有一条数据)
代码:

<?php
use Illuminate\Support\Facades\Redis;
class UserTitle 
{
    public function generateTitle($users, $tableId)
    {
        foreach($users as $k=>$v)
        {
            $title = 0; //称号id
            $user = DB::table('users')->where('user_id', $v)->first();
            if(empty($user))
            {
                $title = $this->getRand(); // 用户不存在, 随机获取一个称号
            } else {
                $title = $user->title;
            }

            // 使用redis setnx, 给当前用户id加锁, 其他客户端的该用户可以获得数据
            $userKey = 'user_title:'.$v; //使用用户id作为key
            Redis::setNX($userKey, $newTitleId); //
            Redis::expire($userKey, 2); //没有使用事务, 会导致多个客户端重复延长该值
            $newTitleId = Redis::get($userKey); //重新从redis获取key, 防止多个客户端值不一致的问题
            // 数据库添加/更新用户title等操作, 一个用户id只能有一条用户数据
        }

        // 整个比赛结果落库, 只能有一条比赛数据
        $matchKey = 'user_title:'.$tableId; //多个客户端的table_id是相同的
        $matchLock = Redis::get($dbKey); //获取值
        if (empty($matchLock))
        {
            Redis::setNX($matchKey, 'lock_match');
            Redis::expire($matchKey, 20);
            // 数据库操作, 执行 updateOrInsert 方法

        }
    }
}

总结

redis 可以做的东西很多, 设计中肯定还有不完善的地方, 如果您有更好的方案, 请不吝赐教, 谢谢!

本作品采用《CC 协议》,转载必须注明作者和本文链接
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

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