问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

服务器高并发的时候mysql扣款失败

发布网友 发布时间:2022-05-01 11:19

我来回答

2个回答

懂视网 时间:2022-05-01 15:41

TABLE `yxt_test_concurrence` ( `id` int(11) NOT NULL AUTO_INCREMENT, `value` int(11) NOT NULL COMMENT ‘库存‘, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT=‘库存表‘;
CREATE TABLE `yxt_test_pv` (
 `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
 `val` int(255) DEFAULT NULL COMMENT ‘该线程读取到的库存数量‘,
 PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=351 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT=‘访问记录表,每次访问都增加一条记录,并记录此次访问时的库存数‘;

在库存表中存入模拟库存500个.

在此,为方便,php采用TP框架:

public function tc(){
 $this->tc = M("test_concurrence");//模拟商品的剩余数量
 $this->pv = M("test_pv");//模拟访问次数
 $res=$this->tc->field(‘value‘)->find(1);//查到的剩余数量
 $value=$res[‘value‘];
 if($value>0){//如果大于0,则进行下面的逻辑
 $this->pv->data(array(‘val‘=>$value))->add();//这个是用来记录访问的次数,并记录此次访问时的库存数
 M()->execute("UPDATE `yxt_test_concurrence` SET `value`=`value` - 1 WHERE `id` = 1"); //商品数量减1
}
}

使用ab工具模拟并发访问:

C:Userschenhui>ab -c 50 -n 500 http://study.com/course/Course/tc/
This is ApacheBench, Version 2.3 <$Revision: 1554214 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking studyyxtcmf.com (be patient)
Completed 100 requests
Completed 200 requests
Completed 300 requests
Completed 400 requests
Completed 500 requests
Finished 500 requests


Server Software: Apache/2.4.9
Server Hostname: studyyxtcmf.com
Server Port:  80

Document Path:  /course/Course/tc/
Document Length: 25786 bytes

Concurrency Level: 50
Time taken for tests: 60.035 seconds
Complete requests: 500
Failed requests: 450
 (Connect: 0, Receive: 0, Length: 450, Exceptions: 0)
Total transferred: 12973630 bytes
HTML transferred: 12785130 bytes
Requests per second: 8.33 [#/sec] (mean)
Time per request: 6003.543 [ms] (mean)
Time per request: 120.071 [ms] (mean, across all concurrent requests)
Transfer rate:  211.03 [Kbytes/sec] received

Connection Times (ms)
  min mean[+/-sd] median max
Connect: 0 1 2.1 1 34
Processing: 781 5915 1578.6 5996 12272
Waiting: 765 5901 1581.8 5983 12261
Total: 783 5916 1578.4 5997 12272

Percentage of the requests served within a certain time (ms)
 50% 5997
 66% 6385
 75% 6707
 80% 6850
 90% 7387
 95% 8402
 98% 9734
 99% 10300
 100% 12272 (longest request)

查看数据库记录:

SELECT * from yxt_test_pv;
--截取一段记录(左边是第几次访问,右侧是当次访问看到的库存)
| 338 | 164 |
| 339 | 164 |
| 340 | 163 |
| 341 | 162 |
| 342 | 162 |
| 343 | 162 |
| 344 | 162 |
| 345 | 157 |
| 346 | 156 |
| 347 | 156 |
| 348 | 153 |
| 349 | 155 |
| 350 | 151 |

可以发现在341-343次读取的库存数量是一样的,在库存还很多的情况时,并不会出现问题:因为程序中减库存的逻辑,是当前库存量减去1.但是库存不多的时候,就很可能出现问题,比如库存只有一个了,而此时有多个线程查询到此时还有一个库存,因为1>0满足条件,所以库存减1,多个线程都对当前库存减1,最后就多减了库存,出现负数,这是不允许的.

所以一定要采取措施.

我认为,总的原则是:对于某一个时刻的库存,只允许一个会话去修改.要满足此条件.有两种选择:

1.对于某一个时刻的库存,只允许一个会话去读取(锁机制).待锁被释放后,其他会话才可以读取库存.

2.对于某一个时刻的库存,设定版本(即增加一个版本字段,用于比较.我对版本的理解是刻个记号),更新库存时要判断版本是否发生变化,若没发生变化,则更新库存的同时,更新版本号.若更新库存时发现版本发生变化了,那一定是有别的线程早已对库存修改,此情况下就放弃修改.

选择1.使用mysql的锁机制.(悲观锁)

 

public function tc(){
 $this->tc = M("test_concurrence");//模拟商品的剩余数量
 $this->pv = M("test_pv");//模拟访问次数
 //对表加锁,注意,如果加锁过程中要操作多个表,要对这几个表都加锁,否则会报错
    //mysql> lock table yxt_test_concurrence read;--只锁了一张表

         //Query OK, 0 rows affected (0.00 sec)

         //mysql> SELECT * from yxt_test_pv;--读取没有被锁的表

        //ERROR 1100 (HY000): Table ‘yxt_test_pv‘ was not locked with LOCK TABLES--报错,提示查询的表没有被锁住

 M()->execute("lock tables yxt_test_concurrence write,yxt_test_pv write;");
 $res=$this->tc->field(‘value‘)->find(1);//查到的剩余数量
 $value=$res[‘value‘];
 if($value>0){//如果大于0,则进行下面的逻辑
  $this->pv->data(array(‘val‘=>$value))->add();//这个是用来记录访问的次数
  M()->execute(
  "UPDATE `yxt_test_concurrence` SET `value`=`value` - 1 WHERE `id` = 1");
  //商品数量减1
 }
 //解锁
 M()->execute("unlock tables");
 }

采用锁机制,可以严格控制库存数量的变化,但是采用锁会增加数据库的开销. 

选择2.版本控制(乐观锁)

乐观锁,是假定事务之间是互不干扰的,事务在访问数据的时候,并不会获取锁,但是,在提交前,每个事务都要确保其他事务并没有修改他读取到的数据.如果在更新数据时发现其他事务已经修改了数据,则回滚提交.乐观锁经常用于"低争用数据结构"的场景中.当冲突特别少的时候,事务可以在完成时,不需要管理锁的开销及等待其他事务释放锁,这可以带来更高的吞吐率.但是,如果对于数据的争用特别频繁,重新开启一个新事务的开销会明显影响性能.

通常认为,其他并发控制方法,在此情况下会有更好的表现,然而,基于悲观锁的方法,会导致较差的性能.因为即使死锁可以避免,"锁"仍会极大的影响并发性能.(我想应该是因为会话被阻塞,从而导致只能串行访问数据库)

以上定义摘自wiki:https://en.wikipedia.org/wiki/Optimistic_concurrency_control

这种情况下,如果并发访问,则修改失败的几率会较高,

举例:在热销产品场景下则容易出现购买失败的情况.这对用户的体验是不好的.因为这意味着又要重新尝试一次.

 

小结:应该采取哪一种锁,应根据实际场景来权衡利弊,如果更新的很频繁,那应该使用悲观锁.此刻需要考虑的问题是:如何解决并发问题.如果很少更新,则使用乐观锁更为方便省事.

高并发访问mysql时的问题(一):库存超减

标签:

热心网友 时间:2022-05-01 12:49

这类问题源自对某些高频账号,大量的并发访问而产生。
这会导致瓶颈首先出现在某些数据库记录上,大量操作由于无法竞争到数据库的行锁而导致等待,这些等待中的操作又会占用其他资源,最终导致系统不可用。
介绍一些常用的处理办法:
1、不设置余额字段。由于对于一个稳定的计费来说,一定是会记录计费流水明细的,所以完全可以不设置余额字段,而采用根据流水明细计算的方式来获取余额。
2、合并与拆分。合并,就是对单个账号的数次请求作合并处理,再往数据库写,这样就等于降低了数倍的压力。拆分,则是把一个主账号拆分成数个子账号,然后把请求分配到各个子账号上,这样单个账号的压力就小了。然后再用其他手段把子账号的数据合并成主账号数据,返回给用户,减少行锁占用时间。
声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
日本最厉害的神叫什么?形象又是什么样的? 我是1995年的,我是几岁上的小学啊? 95年大学毕业于哪一年几月份 1995年生的孩子几岁上小学,中学,高中 叶罗丽时辰砂兑换码大全 叶罗丽精灵梦礼包哪里领 礼包领取兑换地址大全 叶罗丽彩妆公主怎么玩_叶罗丽彩妆公主玩法大全 叶罗丽小游戏大全(叶罗丽小游戏大全换装游戏) 港式碗仔翅的烹饪秘诀有哪些呢? ...三个月的机械键盘有一些键失灵了 用回以前的键盘普通键盘就可以用... 我该如何反击。我发了一句年年岁岁花相似,岁岁年年人不同。有人回复 朝朝暮暮桃花洞,洞里洞外仙欲死。 风风雨雨,朝朝暮暮;花开花谢,潮起潮落.这句话起到什么作用 年年岁岁花相似,暮暮朝朝叶无双出自哪首诗?全诗是什么。 感雪 裂雪行拂何所恨, 逍遥云海卧松梅。 白头到老终相许, 花朝暮暮有春归。 评论这首诗 风风雨雨暖暖寒寒处处寻寻觅觅枝枝叶叶花花草草卿卿暮暮朝朝 意思详解!! 朝朝暮暮云雨定何如花日穿窗梅小小雪风洒雨柳疏疏人唱晚初晴是什么意思_百度问一问 花什么暮什么四字词语 ipad录制视频加密后画面变成竖屏,怎么变成横屏? 女主叫沈清彦男主叫百里南赫的小说名字 《隐秘而伟大》:夏继承为何不接受沈清禾?还是前任令他心有余悸? 她的沈清有动画版吗? 求她的沈清资源!!! 如何评价韩国漫画《她的沈清》? 控制小杂鱼喂翘嘴还是红尾好? 一个可以同时用两部手机登陆吗? 能不能同时登陆两个手机 一个可以同时登两个手机吗? 一个能同时两部手机登陆吗? 一个可以同时登两个手机号吗 同一个可以同时在两个手机上登录吗 古风剧情歌歌词有桃花夭,暮暮又朝朝这些字眼。剧情是女子等男子没等到后来男子回来的时候桃林已经被烧了 风风雨雨,朝朝暮暮,花开花落,潮起潮落这样的开头有什么作用 风有约,花不误什么意思? 朝朝暮暮常思念花开花落是什么歌? 年年岁岁春风过,花开花落任匆匆 求上联 电脑屏幕上有小黑点怎么回事?特别小 华为手机mate305g经常40w快充八九个小时有没有问题? 电脑屏幕突然出现很多小黑点? 组球队换组教新官火老一套数字是什么 讲课还是老一套 —— 屡教不改。猜数字 千篇一律无新意猜一个数字 组球队换组教新官火老一套 歇后语字谜,注明:谐音,意思,猜三个数字(数字0-9) “按老方子吃药” 凉气流 依旧误事(打三个数字谜) 一套多招打三个数字? 前方封闭猜三个数字 衣冠简朴 古风存猜三个数字? 打三个数字是哪三个数字? 手机怎么设置来电秀 一起打三个数字怎么表示?