业务:
抽奖活动 限制每天抽奖次数 每个用户每天只能抽奖3次
同时若有中奖的话更新奖品中奖数和奖品库存数
方案一:
使用mysql(ssd硬盘) 如下所示
#更新用户抽奖次数
select * from draw_count;
+----+---------+------------+-------+
| id | user_id | draw_date | count |
+----+---------+------------+-------+
| 1 | 1 | 2016-03-12 | 3 |
+----+---------+------------+-------+
#显式指定count<3 如果更新count失败 表示已抽完3次 使用这种方式也可以应对同一用户并发超抽的情况。
update draw_count set count=count+1 where user_id = 1 and draw_date=current_date and count<3;
Query OK, 0 rows affected (0.00 sec)
Rows matched: 0 Changed: 0 Warnings: 0
#中奖后更新奖品中奖数和库存
select * from award_count;
+----+----------+-----------+-------+
| id | award_id | win_count | stock |
+----+----------+-----------+-------+
| 1 | 1 | 0 | 100 |
+----+----------+-----------+-------+
#显式指定stock>0 避免并发超抽的情况 只有更新成功了 才表示成功中奖
update award_count set win_count = win_count+1 , stock = stock-1 where award_id = 1 and stock>0;
Query OK, 1 row affected (0.06 sec)
Rows matched: 1 Changed: 1 Warnings: 0
方案二:
使用redis 如下所示
#更新用户抽奖次数
127.0.0.1:6379> incr 1_20160312
(integer) 1
127.0.0.1:6379> incr 1_20160312
(integer) 2
127.0.0.1:6379> incr 1_20160312
(integer) 3
127.0.0.1:6379> get 1_20160312
"3"
#更新奖品中奖数和库存
127.0.0.1:6379> incr 1_win_count
(integer) 1
127.0.0.1:6379> decr 1_stock
(integer) 99
使用redis来限制并发超抽的情况
好像面对上述场景大家想到的都是使用redis, 但使用mysql的劣势在哪呢?尤其当使用的是ssd硬盘的mysql
迷茫2017-04-17 13:57:21
看具體情況,對並發比較大的情況下redis會比較有優勢,而且incr系列api又是保證原子性操作。 mysql速度會稍微差些,ssd也不會有記憶體快。對於這種有key/value操作顯然nosql產品更有優勢。
伊谢尔伦2017-04-17 13:57:21
使用redis會有一個問題,那就是到最後你的商品數量剩餘量可能是小於0的,即因為redis只能保證每個操作的原子性,如果你需要判斷商品數量是否大於0,然後再決定是否減少餘量,這樣redis是沒法保證原子性的,並發就會導致餘量小於0,但是並不影響業務的正確性,因為你可以透過decr的回傳值是否>=0來判斷發獎成功與否。
MySQL畢竟是檔案存儲,肯定沒辦法跟內存比,這種情況下一般方案是redis+mysql,這種資料不落DB誰放心呢。