ci mysql高并发_高并发访问mysql时的问题(一):库存超减
標簽:
如果在對某行記錄的更新時不采取任何防范措施,在多線程訪問時,就容易出現庫存為負數的錯誤.
以下用php、mysql,apache ab工具舉例說明:
mysql表結構
CREATE TABLE`yxt_test_concurrence` (
`id`int(11) NOT NULLAUTO_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 NULLAUTO_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 functiontc(){$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:\Users\chenhui>ab -c 50 -n 500 http://study.com/course/Course/tc/
This is ApacheBench, Version 2.3Copyright 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 * fromyxt_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 functiontc(){$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
這種情況下,如果并發訪問,則修改失敗的幾率會較高,
舉例:在熱銷產品場景下則容易出現購買失敗的情況.這對用戶的體驗是不好的.因為這意味著又要重新嘗試一次.
小結:應該采取哪一種鎖,應根據實際場景來權衡利弊,如果更新的很頻繁,那應該使用悲觀鎖.此刻需要考慮的問題是:如何解決并發問題.如果很少更新,則使用樂觀鎖更為方便省事.
標簽:
總結
以上是生活随笔為你收集整理的ci mysql高并发_高并发访问mysql时的问题(一):库存超减的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 英国女孩刷个牙的工夫就失忆一次 记忆每3
- 下一篇: 电商领域迎来头号玩家!抖音盒子App上线