<!-- wp:heading {"level":1} -->
<h1>内容</h1>
<!-- /wp:heading -->

<!-- wp:heading {"level":5} -->
<h5>实现简单的秒杀页面(显示当前秒杀活动状态)和秒杀接口,不需要考虑下订单和退货流程。</h5>
<!-- /wp:heading -->

<!-- wp:heading -->
<h2>秒杀接口要求</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>时间到了才能开始秒杀</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>不能超买:1个用户只能秒杀1次</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>不能超卖</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>在缓存崩溃重启的情况也不能出现超买和超卖的情况</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>测试</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>功能正常</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>1个用户发起100个并发测试</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>随机用户(userId:rand(1, 1000000000)) 请求,100个并发秒杀,最先完成秒杀1000个商品的活动</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>数据表结构如下</p>
<!-- /wp:paragraph -->

<!-- wp:code -->

--

-- 用户秒杀成功记录 log

--

CREATE TABLE log (

  id int(11) NOT NULL AUTO_INCREMENT,

  eventId int(11) NOT NULL COMMENT '活动ID',

  userId int(11) NOT NULL COMMENT '用户ID',

  createTime int(11) NOT NULL COMMENT '创建时间',

  PRIMARY KEY (id),

  UNIQUE KEY eventId (eventId,userId)

) ENGINE=InnoDB AUTO_INCREMENT=4353 DEFAULT CHARSET=utf8;

<!-- /wp:code -->

<!-- wp:code -->

--

-- 秒杀活动 event

--

CREATE TABLE event (

  id int(11) NOT NULL AUTO_INCREMENT,

  cnt int(11) unsigned NOT NULL DEFAULT '0' COMMENT '总量',

  remainCnt int(11) unsigned NOT NULL DEFAULT '0' COMMENT '剩余数量',

  startTime int(11) unsigned NOT NULL DEFAULT '0' COMMENT '开始时间',

  createTime int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间',

  PRIMARY KEY (id)

) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='活动';

<!-- /wp:code -->

<!-- wp:paragraph -->
<p>代码参考(重在理解)</p>
<!-- /wp:paragraph -->

<!-- wp:code -->

<?php
namespace apphelper;

class SecKill
{
    protected $userId;//用户ID
    protected $eventId;//活动ID
    protected $eventLock;//活动锁
    protected $eventCntKey;//数量缓存键
    protected $userLogCacheKey;//用户日志缓存
    protected $userLock;//用户锁缓存键
    protected $cache = null;
    protected $db = null;
    protected $config = [];
    const EXPIRATION = 20;

    public function __construct($userId, $eventId)
    {
        $this->userId = $userId;
        $this->eventId = $eventId;
        $this->eventLock = $this->eventId . '_eventLock';
        $this->eventCntKey = $this->eventId . '_eventCnt';//缓存中数量比数据库中多1个
        $this->userLogCacheKey = $this->userId . '_log';
        $this->userLock = $this->userId . '_userLock';
        $this->config = [//可以写一个配置文件引入,在这里我就简单点写
            'cache' => [
                'host' => '127.0.0.1',
                'port' => '11211'
            ],
            'db' => [
                'host' => '127.0.0.1',
                'user' => 'root',
                'password' => 123456,
                'database' => 'miaosha',
            ]
        ];
        $this->kill();
    }

    private function kill()
    {
        try {
            $this->connectMemcached();
            //给用户加锁
            if (!$this->cache->add($this->userLock, 1, self::EXPIRATION)) {
                return 'User failed to lock';
            }
            $this->connectMysql();
            $sqlTpl = "SELECT 
                          id
                          FROM  %s 
                          WHERE userId = '%d' 
                          AND eventId = '%d'";
            $sql = sprintf($sqlTpl, self::tableName(), $this->userId, $this->eventId);
            $res = $this->db->query($sql);
            //用户是否抢到过抢到过直接退出
            if ($res->fetch_assoc() > 0) {
                $this->cache->delete($this->userLock);
                return '不要贪心呦';
            }
            //递减1
            $cnt = $this->cache->decrement($this->eventCntKey, 1);
            if ($cnt === false) {
                if (!$this->cache->add($this->eventLock, 1, self::EXPIRATION)) {
                    $this->cache->delete($this->userLock);
                    return '加锁失败';
                }
                $res = $this->db->query("SELECT * FROM event WHERE id = {$this->eventId}");
                $event = $res->fetch_assoc();
                if (!$event) {
                    $this->cache->delete($this->eventLock);
                    $this->cache->delete($this->userLock);
                    return '活动不存在';
                }
                $cnt = $event['remainCnt'];
                //添加活动数量
                if (!$this->cache->add($this->eventCntKey, $cnt)) {
                    $this->cache->delete($this->userLock);
                    $this->cache->delete($this->eventLock);
                    return "被人捷足先登";
                }
                $this->cache->delete($this->eventLock);
            }
            if ($cnt < 1) {
                $this->cache->delete($this->userLock);
                return "抢光了";
            }
            $time = time();
            $this->db->query("BEGIN");
            $this->db->query("UPDATE event SET remainCnt = remainCnt - 1 WHERE id = {$this->eventId}");
            $this->db->query("INSERT INTO log (eventId, userId, createTime) VALUES ({$this->eventId}, {$this->userId}, {$time})");
            $this->db->query("COMMIT");
            $this->cache->delete($this->userLock);
            return "Success";
        } catch (Exception $ex) {
            echo "<pre>";
            var_dump($ex);
        }
    }

    /**
     * 连接memcached
     * @return string
     */
    protected function connectMemcached()
    {
        $this->cache = new Memcache();
        if (!empty($this->config['cache'])) {
            $this->cache->addServer($this->config'cache', $this->config'cache');
        } else {
            return 'Configuration does not exist';
        }
    }

    /**
     * 连接mysql
     */
    protected function connectMysql()
    {
        $this->db = new mysqli();
        $this->db->connect($this->config'db', $this->config['db']['user'], $this->config'db', $this->config'db');
        $this->db->set_charset('utf8');
    }

    protected static function tableName()
    {
        return 'log';
    }
}

<!-- /wp:code -->

关注友儿不迷路

Last modification:July 16th, 2019 at 02:08 pm
如果觉得我的文章对你有用,请随意赞赏