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

  案例:

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

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

  

一 .使用表锁,解决案例1中的问题

1.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;

1.2 添加签到记录逻辑

    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);
}
}

1.3 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
}
}

1.4 加上lock(true)可以解决问题。实际就是在查询语句最后加上 for update,这里用到了表锁。

    public function addSignValue(){//锁表
    M('')->startTrans();
    $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); }
     
     M('')->commit();
} //SELECT * FROM `crm_concurrency` WHERE `member_id` = 1 AND `sign_date`
//= '2017-12-25' LIMIT 1 FOR UPDATE

二 .使用行锁,解决案例2中的问题

  1.新建一张签到发券配置表和发券表,这里只建了一个配置表做演示

DROP TABLE IF EXISTS `crm_coupons_config`;
CREATE TABLE `crm_coupons_config` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`config_count` int(10) unsigned DEFAULT NULL COMMENT '要发放券的数量',
`has_given` int(255) unsigned DEFAULT NULL COMMENT '已发放的券的数量',
`create_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

  2.下面是第一种思路的伪代码,也使用了表锁

    public function sendCoupons(){
//1.从crm_coupons_config表中取出配置 //2.锁住发券表,并计算已经发的券的数量 //3.如果已发的券的数量大于等于配置表的数量,则停止发送券
}

  3.下面是另外一一种思路,使用到了行锁。不需要添加额外锁表代码,因为mysql在查询,更新 的时候,是默认锁表的

    public function sendCoupons(){
$req = M('coupons_config','crm_')
->where('config_count>has_given')
->setInc('has_given');
if($req){
//申请到了优惠券
}
} //UPDATE `crm_coupons_config` SET `has_given`=has_given+1 WHERE ( config_count>has_given )

三 总结

  主要使用到了数据库中的表锁和行锁

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

  1. ASP.NET CORE MVC 2.0 项目中引用第三方DLL报错的解决办法 - InvalidOperationException: Cannot find compilation library location for package

    目前在学习ASP.NET CORE MVC中,今天看到微软在ASP.NET CORE MVC 2.0中又恢复了允许开发人员引用第三方DLL程序集的功能,感到甚是高兴!于是我急忙写了个Demo想试试,我 ...

  2. 关于IDEA中web项目中web.xml配置文件标红的解决办法

    原文链接 https://blog.csdn.net/qq_33451695/article/details/86684127 解决方法前提:web.xml没有实际错误,但依然被web.xml标红 出 ...

  3. 关于CUDA C 项目中“ error C2059: 语法错误:“<” ”问题的解决方法

    该问题的关键在于理解CUDA项目中C\C++文件需要由c++编译器进行编译,而CUDA C的源文件需要由CUDA的编译器nvcc.exe进行编译. 发生该语法错误的原因是cu文件被C++编译器所编译, ...

  4. eclipse中的js文件报错的解决办法

    在使用别人的项目的时候,导入到eclipse中发现js文件报错,解决办法是关闭eclipse的js校验功能. 三个步骤: 1. 右键点击项目->properties->Validation ...

  5. Spring MVC普通类或工具类中调用service报空空指针的解决办法(调用service报java.lang.NullPointerException)

    当我们在非Controller类中应用service的方法是会报空指针,如图: 这是因为Spring MVC普通类或工具类中调用service报空null的解决办法(调用service报java.la ...

  6. [转]iOS Safari 中click点击事件失效的解决办法

    iOS Safari 中click点击事件失效的解决办法 问题起因: 在微信公众号开发(微站)过程中用jquery的live方法绑定的click事件点击无效(不能执行) 问题描述 当使用委托给一个元素 ...

  7. python matplotlib plot 数据中的中文无法正常显示的解决办法

    转发自:http://blog.csdn.net/laoyaotask/article/details/22117745?utm_source=tuicool python matplotlib pl ...

  8. 配置IIS提示打开目录浏览时的问题:未能从程序集“System.ServiceModel, Version=3.0.0.0”中加载类型“System.ServiceModel.Activation.HttpModule” 的解决办法

    错误消息: 未能从程序集“System.ServiceModel, Version=3.0.0.0”中加载类型“System.ServiceModel.Activation.HttpModule” 的 ...

  9. Android 在 Fragment 中使用 getActivity() NullPointException 的思考和解决办法

    问题: 使用 AS 在 Fragment 中调用 getActivity() 方法的时候会出现可能为空指针的提醒 使用 monkey 多次十万次测试,会出现 getActivity() NullPoi ...

随机推荐

  1. Bits Equalizer UVA - 12545

    点击打开链接 #include<cstdio> #include<cstring> /* 别看错了:0能变1,1不能变0 能完成的条件是,s与t长度相等且s中0数量和?数量之和 ...

  2. PHP常见的输出语句 echo()、print()、print_r()、var_dump()、pinrtf()之间的区别

    echo().print().print_r().var_dump().pinrtf()之间的区别 1.print() :简单输出语句,可以输出字符串,变量.表达式等单项的值!(不能输出数组.对象等复 ...

  3. Appium + Python自动化 - 元素定位uiautomatorviewer

    元素定位主要介绍如何使用uiautiomatorviewer,通过定位到页面上的元素,然后进行相应的点击等操作.uiautiomatorviewer是android-sdk自带的一个元素定位工具,非常 ...

  4. Codeforces Round #243 (Div. 1)

    ---恢复内容开始--- A 枚举l,r #include <iostream> #include<cstdio> #include<cstring> #inclu ...

  5. dangerouslySetHTML 和 style 属性

    这一节我们来补充两个之前没有提到的属性,但是在 React.js 组件开发中也非常常用,但是它们也很简单. dangerouslySetHTML 出于安全考虑的原因(XSS 攻击),在 React.j ...

  6. 定时器、线程queue、进程池和线程池

    1.定时器 指定n秒后,执行任务 from threading import Timer,current_thread import os def hello(): print("%s he ...

  7. linux的top下buffer与cache的区别

    buffer:    缓冲区,一个用于存储速度不同步的设备或优先级不同的设备之间传输数据 的区域.通过缓冲区,可以使进程之间的相互等待变少,从而使从速度慢的设备读入数据 时,速度快的设备的操作进程不发 ...

  8. css3 blur模糊解决ie6-ie9兼容

    css3 blur模糊是css3的新特性,但是不兼容ie6-ie9,以下代码可以解决此问题: filter: progid:DXImageTransform.Microsoft.Blur(Pixel ...

  9. postgresql 9.4.17 64位安装说明

    本文在win 7 64位下安装postgresql 9.4.17 64位版,安装过程有录入项的界面都有截图 运行安装程序开始安装 开始时可能自动安装c++运行环境 然后正式开始安装 安装目录位置,不要 ...

  10. 动态栅格(DEM)图层实现服务端渲染

    PS:此处动态图层指,图层文件都放在经过注册的文件目录里,可以通过文件名动态加载图层 动态加载的矢量图层,可以实现客户端和服务端的定制渲染,但栅格一般是不能再渲染的,以下介绍可行的方法 建立一个很简单 ...