项目中遇到的超卖问题及解决办法(使用go做测试工具)

Posted alin-qu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了项目中遇到的超卖问题及解决办法(使用go做测试工具)相关的知识,希望对你有一定的参考价值。

  超卖问题:在一个很短的时间内,mysql的数据状态在 取出,比较,提交,或修改中,另外一个进程访问数据导致的超卖问题。

  案例:

    1.前端没有做限制,如果用户连续点击签到,那么会有多条数据发送到后端,如果数据状态没有来得及完全修改过来,导致用户的签到数据被多次添加。

    2.每天签到用户的前3名用户可以获得一张价值100元的优惠券,如果有多名用户在很短的时间内同时签到,那么就会有多发的问题。

  

       解决案例1:使用数据库的行锁和表锁

DROP TABLE
IF EXISTS `crm_concurrency`;

CREATE TABLE `crm_concurrency` (
    `id` INT (10) UNSIGNED NOT NULL AUTO_INCREMENT,
    `member_id` INT (10) UNSIGNED DEFAULT NULL,  -- 会员ID
    `sign_date` date DEFAULT NULL,          -- 签到日期
    `create_at` datetime DEFAULT NULL,
    PRIMARY KEY (`id`)
) ENGINE = INNODB DEFAULT CHARSET = utf8;

 

    public function addSignValue(){
        //会员ID
        $member_id = I(‘get.member_id‘);

        if(!$member_id){
            return false;
        }
        //签到日期
        $sign_date = date(‘Y-m-d‘);


        $where_condition = array(
            ‘member_id‘    =>$member_id,
            ‘sign_date‘    =>date(‘Y-m-d‘)
        );

        //锁表
        $sign_value = M(‘concurrency‘,‘crm_‘)
                ->where($where_condition)->find();
        if(!$sign_value){
            $add_value = array(
                ‘member_id‘    =>$member_id,
                ‘sign_date‘    =>$sign_date
            );
            //添加一条签到记录
            M(‘concurrency‘,‘crm_‘)->add($add_value);            
        }
    }

 

正常情况下,数据会正确的添加到数据库,且不会被多次添加,但是使用Go做一下压力测试

 

func fGet(url string) {
	res, err := http.Get(url)
	defer res.Body.Close()

	if err != nil {
		log.Fatal(err)
	}

	re, err := ioutil.ReadAll(res.Body)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(string(re))
	ch <- struct{}{}
}

var ch = make(chan struct{})

func main() {

	url := "测试URL?member_id=";

	for index := 1; index <= 10; index++ {
		tmp := url + strconv.Itoa(1) ;
		go fGet(tmp)
	}

	for index := 1; index <= 10; index++ {
		<-ch
	}
}

  运行Go测试代码之后,数据被重复插入到数据库

    public function addSignValue(){
        //会员ID
        $member_id = I(‘get.member_id‘);

        if(!$member_id){
            return false;
        }
        //签到日期
        $sign_date = date(‘Y-m-d‘);


        $where_condition = array(
            ‘member_id‘    =>$member_id,
            ‘sign_date‘    =>date(‘Y-m-d‘)
        );

        //锁表
        $sign_value = M(‘concurrency‘,‘crm_‘)
                ->where($where_condition)
                ->lock(true)
                ->find();
        if(!$sign_value){
            $add_value = array(
                ‘member_id‘    =>$member_id,
                ‘sign_date‘    =>$sign_date
            );
            //添加一条签到记录
            M(‘concurrency‘,‘crm_‘)->add($add_value);            
        }
    }

 加锁之后,就不会出现问题了。加上lock(true)的实际就是在查询语句最后加上 for update

以上是关于项目中遇到的超卖问题及解决办法(使用go做测试工具)的主要内容,如果未能解决你的问题,请参考以下文章

Redis-使用Lua脚本解决多线程下的超卖问题以及为什么?

如何解决高并发秒杀的超卖问题

微信小程序开发中遇到的坑及解决办法

Java前后端分离项目生成二维码链接带中文参数遇到的问题及解决办法

Java前后端分离项目生成二维码链接带中文参数遇到的问题及解决办法

Java前后端分离项目生成二维码链接带中文参数遇到的问题及解决办法